this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Bring back mldr and start work on darlingserver

The goal of this new branch is to implement Darling entirely in userspace, no LKM necessary.

This first commit re-introduces mldr (a userspace Mach-O loader last used about 4 years ago in 89d6e6ddb126a0d4fdc1624b1d06e2c74d7a173f) together with some updates from the Mach-O loading code in the LKM.

This commit also sets up the basis for darlingserver: RPC and process/thread tracking.

+4891 -1110
+1 -1
Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/elfcalls/elfcalls.h
··· 1 - ../../../../../../../../../src/libelfloader/native/elfcalls.h 1 + ../../../../../../../../../src/startup/mldr/elfcalls/elfcalls.h
+1 -1
Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/elfcalls/threads.h
··· 1 - ../../../../../../../../../src/libelfloader/native/threads.h 1 + ../../../../../../../../../src/startup/mldr/elfcalls/threads.h
+1 -1
cmake/setcap.cmake
··· 4 4 install(CODE "execute_process( 5 5 COMMAND 6 6 ${SETCAP_EXECUTABLE} 7 - cap_sys_rawio+ep 7 + ${caps} 8 8 ${CMAKE_INSTALL_PREFIX}/${file} 9 9 RESULT_VARIABLE 10 10 _SETCAP_RESULT
+2 -2
cmake/wrap_elf.cmake
··· 21 21 endif (ARGC LESS 3) 22 22 23 23 set(DYLIB_INSTALL_NAME "${destination}/lib${name}.dylib") 24 - include_directories(${CMAKE_SOURCE_DIR}/src/libelfloader/native) 24 + include_directories(${CMAKE_SOURCE_DIR}/src/startup/mldr/elfcalls) 25 25 add_darling_library(${name} SHARED ${CMAKE_CURRENT_BINARY_DIR}/${name}.c) 26 - target_link_libraries(${name} PRIVATE system elfloader) 26 + target_link_libraries(${name} PRIVATE system) 27 27 make_fat(${name}) 28 28 install(TARGETS ${name} DESTINATION libexec/darling/${destination}) 29 29 endfunction(wrap_elf)
+4 -5
src/CMakeLists.txt
··· 10 10 include(pyc) 11 11 12 12 #add_subdirectory(external/xcbuild) 13 - add_subdirectory(libelfloader/native) 14 13 add_subdirectory(bsdln) 15 14 16 15 add_definitions( ··· 44 43 find_package(BISON) 45 44 find_package(FLEX) 46 45 46 + set(DARLING_SDK_RELATIVE_PATH "Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk") 47 + set(DARLING_ROOT_RELATIVE_TO_SDK "../../../../../..") 48 + 47 49 add_subdirectory(external/cctools-port/cctools/ld64/src) 48 50 add_subdirectory(external/cctools-port/cctools/ar) 49 51 #add_subdirectory(external/cctools-port/cctools/as) ··· 55 57 add_subdirectory(buildtools) 56 58 add_subdirectory(libelfloader/wrapgen) 57 59 add_subdirectory(startup) 60 + add_subdirectory(darlingserver) 58 61 59 62 include_directories(${CMAKE_SOURCE_DIR}/basic-headers) 60 63 ··· 78 81 include(darling_lib) 79 82 include(darling_static_lib) 80 83 81 - set(DARLING_SDK_RELATIVE_PATH "Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk") 82 - set(DARLING_ROOT_RELATIVE_TO_SDK "../../../../../..") 83 - 84 84 file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/${DARLING_SDK_RELATIVE_PATH}/usr/include) 85 85 86 86 include_directories( ··· 140 140 add_subdirectory(libcache) 141 141 add_subdirectory(ncurses) 142 142 add_subdirectory(libiconv) 143 - add_subdirectory(libelfloader) 144 143 add_subdirectory(duct/src) 145 144 add_subdirectory(libresolv) 146 145 add_subdirectory(external/corecrypto)
+60
src/darlingserver/CMakeLists.txt
··· 1 + project(darlingserver) 2 + 3 + cmake_minimum_required(VERSION 3.10) 4 + 5 + include_directories( 6 + include 7 + internal-include 8 + ${CMAKE_CURRENT_BINARY_DIR}/include 9 + ${CMAKE_CURRENT_BINARY_DIR}/internal-include 10 + ) 11 + 12 + add_custom_command( 13 + OUTPUT 14 + ${CMAKE_CURRENT_BINARY_DIR}/include/darlingserver/rpc.h 15 + ${CMAKE_CURRENT_BINARY_DIR}/internal-include/darlingserver/rpc.internal.h 16 + ${CMAKE_CURRENT_BINARY_DIR}/src/rpc.c 17 + COMMAND 18 + ${CMAKE_CURRENT_SOURCE_DIR}/scripts/generate-rpc-wrappers.py 19 + ${CMAKE_CURRENT_BINARY_DIR}/include/darlingserver/rpc.h 20 + ${CMAKE_CURRENT_BINARY_DIR}/internal-include/darlingserver/rpc.internal.h 21 + ${CMAKE_CURRENT_BINARY_DIR}/src/rpc.c 22 + \"../include/darlingserver/rpc.h\" 23 + VERBATIM 24 + MAIN_DEPENDENCY 25 + ${CMAKE_CURRENT_SOURCE_DIR}/scripts/generate-rpc-wrappers.py 26 + ) 27 + 28 + add_custom_target(generate_dserver_rpc_wrappers 29 + ALL 30 + DEPENDS 31 + ${CMAKE_CURRENT_BINARY_DIR}/include/darlingserver/rpc.h 32 + ${CMAKE_CURRENT_BINARY_DIR}/internal-include/darlingserver/rpc.internal.h 33 + ${CMAKE_CURRENT_BINARY_DIR}/src/rpc.c 34 + ) 35 + 36 + add_executable(darlingserver 37 + src/darlingserver.cpp 38 + src/server.cpp 39 + src/message.cpp 40 + src/call.cpp 41 + src/registry.cpp 42 + src/logging.cpp 43 + src/process.cpp 44 + src/thread.cpp 45 + ) 46 + 47 + add_dependencies(darlingserver 48 + generate_dserver_rpc_wrappers 49 + ) 50 + 51 + target_compile_options(darlingserver PRIVATE -pthread -std=c++17) 52 + target_link_options(darlingserver PRIVATE -pthread) 53 + 54 + install(TARGETS darlingserver DESTINATION bin) 55 + 56 + #file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/${DARLING_SDK_RELATIVE_PATH}/usr/include/darlingserver") 57 + #create_symlink( 58 + # "${DARLING_ROOT_RELATIVE_TO_SDK}/../../../src/darlingserver/include/darlingserver/rpc.h" 59 + # "${CMAKE_BINARY_DIR}/${DARLING_SDK_RELATIVE_PATH}/usr/include/darlingserver/rpc.h" 60 + #)
+65
src/darlingserver/internal-include/darlingserver/call.hpp
··· 1 + /** 2 + * This file is part of Darling. 3 + * 4 + * Copyright (C) 2021 Darling developers 5 + * 6 + * Darling is free software: you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation, either version 3 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * Darling is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + 20 + #ifndef _DARLINGSERVER_CALL_HPP_ 21 + #define _DARLINGSERVER_CALL_HPP_ 22 + 23 + #include <darlingserver/rpc.h> 24 + #include <darlingserver/rpc.internal.h> 25 + 26 + #include <darlingserver/message.hpp> 27 + #include <darlingserver/registry.hpp> 28 + 29 + #include <memory> 30 + 31 + namespace DarlingServer { 32 + class CallWithReply; 33 + 34 + class Call { 35 + public: 36 + enum class Number { 37 + Invalid = dserver_callnum_invalid, 38 + DSERVER_ENUM_VALUES 39 + }; 40 + 41 + protected: 42 + std::weak_ptr<Thread> _thread; 43 + MessageQueue& _replyQueue; 44 + Address _replyAddress; 45 + 46 + void _stopPending(); 47 + 48 + public: 49 + Call(MessageQueue& replyQueue, std::shared_ptr<Thread> thread, Address replyAddress); 50 + virtual ~Call(); 51 + 52 + static std::shared_ptr<Call> callFromMessage(Message&& requestMessage, MessageQueue& replyQueue); 53 + 54 + virtual Number number() const = 0; 55 + std::shared_ptr<Thread> thread() const; 56 + 57 + virtual void processCall() = 0; 58 + 59 + DSERVER_CLASS_DECLS; 60 + }; 61 + 62 + DSERVER_CLASS_DEFS; 63 + }; 64 + 65 + #endif // _DARLINGSERVER_CALL_HPP_
+88
src/darlingserver/internal-include/darlingserver/logging.hpp
··· 1 + /** 2 + * This file is part of Darling. 3 + * 4 + * Copyright (C) 2021 Darling developers 5 + * 6 + * Darling is free software: you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation, either version 3 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * Darling is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + 20 + #ifndef _DARLINGSERVER_LOGGING_HPP_ 21 + #define _DARLINGSERVER_LOGGING_HPP_ 22 + 23 + #include <fstream> 24 + #include <string> 25 + #include <sstream> 26 + 27 + namespace DarlingServer { 28 + static struct EndLog {} endLog; 29 + 30 + class Log { 31 + private: 32 + std::string _category; 33 + 34 + enum class Type { 35 + Debug, 36 + Info, 37 + Warning, 38 + Error, 39 + }; 40 + 41 + void _log(Type type, std::string message); 42 + static std::string _typeToString(Type type); 43 + 44 + public: 45 + Log(std::string category); 46 + 47 + Log(const Log&) = delete; 48 + Log& operator=(const Log&) = delete; 49 + Log(Log&&) = delete; 50 + Log& operator=(Log&&) = delete; 51 + 52 + class Stream; 53 + friend class Stream; 54 + 55 + Stream debug(); 56 + Stream info(); 57 + Stream warning(); 58 + Stream error(); 59 + }; 60 + 61 + class Log::Stream { 62 + friend class Log; 63 + 64 + private: 65 + Type _type; 66 + Log& _log; 67 + std::ostringstream _buffer; 68 + 69 + Stream(Type type, Log& log); 70 + 71 + public: 72 + Stream(const Stream&) = delete; 73 + Stream& operator=(const Stream&) = delete; 74 + Stream(Stream&&) = delete; 75 + Stream& operator=(Stream&&) = delete; 76 + 77 + template<class T> 78 + Stream& operator<<(T value) { 79 + _buffer << value; 80 + return *this; 81 + }; 82 + 83 + template<> 84 + Stream& operator<<<EndLog>(EndLog value); 85 + }; 86 + }; 87 + 88 + #endif // _DARLINGSERVER_LOGGING_HPP_
+170
src/darlingserver/internal-include/darlingserver/message.hpp
··· 1 + /** 2 + * This file is part of Darling. 3 + * 4 + * Copyright (C) 2021 Darling developers 5 + * 6 + * Darling is free software: you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation, either version 3 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * Darling is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + 20 + #ifndef _DARLINGSERVER_MESSAGE_HPP_ 21 + #define _DARLINGSERVER_MESSAGE_HPP_ 22 + 23 + #include <sys/socket.h> 24 + #include <sys/un.h> 25 + #include <vector> 26 + #include <cstdint> 27 + #include <deque> 28 + #include <mutex> 29 + #include <optional> 30 + 31 + namespace DarlingServer { 32 + class Address { 33 + private: 34 + struct sockaddr_un _address; 35 + size_t _size; 36 + 37 + public: 38 + Address(); 39 + Address(const struct sockaddr_un& rawAddress, size_t addressSize = sizeof(struct sockaddr_un)); 40 + 41 + struct sockaddr_un& raw(); 42 + const struct sockaddr_un& raw() const; 43 + 44 + size_t rawSize() const; 45 + void setRawSize(size_t newRawSize); 46 + }; 47 + 48 + /** 49 + * A C++ wrapper for local (Unix) SOCK_DGRAM/SOCK_SEQPACKET socket messages, with support for file descriptors (SCM_RIGHTS) and credentials (SCM_CREDENTIALS). 50 + */ 51 + class Message { 52 + private: 53 + struct msghdr _header; 54 + struct iovec _dataDescriptor; 55 + std::vector<uint8_t> _buffer; 56 + struct cmsghdr* _controlHeader = nullptr; 57 + Address _socketAddress; 58 + 59 + void _initWithOther(Message&&); 60 + void _cleanupSelf(); 61 + 62 + struct cmsghdr* _credentialsHeader(); 63 + const struct cmsghdr* _credentialsHeader() const; 64 + struct cmsghdr* _descriptorHeader(); 65 + const struct cmsghdr* _descriptorHeader() const; 66 + size_t _descriptorSpace() const; 67 + 68 + void _ensureCredentialsHeader(); 69 + void _ensureDescriptorHeader(size_t descriptorSpace); 70 + 71 + public: 72 + /** 73 + * Default-initializes a Message. 74 + * 75 + * By default, the data buffer holds 256 bytes and the control data buffer has space for 4 descriptors. 76 + * 77 + * If this Message is being used to receive a message from a socket, you must preallocate 78 + * the buffers for the recvmsg/recvmmsg call. The buffers are already preallocated to the 79 + * sizes specified by in this constructor, however, if you wish to grow the data buffer, 80 + * you can call pushData() with a null buffer pointer and a non-zero size. If you wish to 81 + * grow the control data buffer, you can call pushDescriptor() with a descriptor value of `-1`. 82 + */ 83 + Message(size_t bufferSpace = 256, size_t descriptorSpace = 4); 84 + ~Message(); 85 + 86 + Message(Message&&); 87 + Message& operator=(Message&&); 88 + 89 + Message(const Message&) = delete; 90 + Message& operator=(const Message&) = delete; 91 + 92 + struct msghdr& rawHeader(); 93 + const struct msghdr& rawHeader() const; 94 + 95 + std::vector<uint8_t>& data(); 96 + const std::vector<uint8_t>& data() const; 97 + 98 + Address address() const; 99 + void setAddress(Address address); 100 + 101 + bool copyCredentialsOut(struct ucred& outputCredentials) const; 102 + void copyCredentialsIn(const struct ucred& inputCredentials); 103 + 104 + pid_t pid() const; 105 + void setPID(pid_t pid); 106 + 107 + uid_t uid() const; 108 + void setUID(uid_t uid); 109 + 110 + gid_t gid() const; 111 + void setGID(gid_t gid); 112 + 113 + /** 114 + * Returns an array of all the descritors currently owned by this Message. 115 + * 116 + * Note that the descriptors are still owned by this Message during and after this call. 117 + * Callers should NOT close the descriptors they receive in the returned vector. 118 + * 119 + * See extractDescriptor() for a method that will transfer ownership of a descriptor to the caller. 120 + */ 121 + std::vector<int> descriptors() const; 122 + 123 + /** 124 + * Acquires ownership of the given descriptor. 125 + * 126 + * Note that callers should NOT close the descriptor after a call to this method. 127 + * This Message becomes the owner of the descriptor and will take care of closing it. 128 + */ 129 + void pushDescriptor(int descriptor); 130 + 131 + /** 132 + * Extracts a single descriptor from this Message, transferring ownership of it to the caller. 133 + * 134 + * @returns The descriptor that was extracted, or `-1` if it was not found. 135 + */ 136 + int extractDescriptor(int descriptor); 137 + 138 + /** 139 + * Like extractDescriptor(), but extracts it based on its index in the vector returned by descriptors(). 140 + */ 141 + int extractDescriptorAtIndex(size_t index); 142 + 143 + /** 144 + * Gives up ownership of the descriptors previously held by this Message and acquires ownership 145 + * of the descriptors passed in the given vector. 146 + * 147 + * Note that this method does not return the old descriptors in any way, 148 + * so if the caller wishes to know what they were, they must call descriptors() before calling this method. 149 + */ 150 + void replaceDescriptors(const std::vector<int>& newDescriptors); 151 + }; 152 + 153 + /** 154 + * A thread-safe Message queue with methods for sending and receiving messages. 155 + */ 156 + class MessageQueue { 157 + private: 158 + std::deque<Message> _messages; 159 + std::mutex _lock; 160 + 161 + public: 162 + void push(Message&& message); 163 + std::optional<Message> pop(); 164 + 165 + void sendMany(int socket); 166 + void receiveMany(int socket); 167 + }; 168 + }; 169 + 170 + #endif // _DARLINGSERVER_MESSAGE_HPP_
+76
src/darlingserver/internal-include/darlingserver/process.hpp
··· 1 + /** 2 + * This file is part of Darling. 3 + * 4 + * Copyright (C) 2021 Darling developers 5 + * 6 + * Darling is free software: you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation, either version 3 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * Darling is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + 20 + #ifndef _DARLINGSERVER_PROCESS_HPP_ 21 + #define _DARLINGSERVER_PROCESS_HPP_ 22 + 23 + #include <sys/types.h> 24 + #include <memory> 25 + #include <vector> 26 + #include <mutex> 27 + #include <shared_mutex> 28 + 29 + namespace DarlingServer { 30 + class Thread; 31 + class Server; 32 + 33 + class Process { 34 + friend class Thread; 35 + friend class Server; 36 + 37 + private: 38 + pid_t _pid; 39 + pid_t _nspid; 40 + int _pidfd; 41 + mutable std::shared_mutex _rwlock; 42 + std::vector<std::weak_ptr<Thread>> _threads; 43 + std::string _vchrootPath; 44 + 45 + void _unregisterThreads(); 46 + 47 + public: 48 + using ID = pid_t; 49 + using NSID = ID; 50 + 51 + Process(ID id, NSID nsid); 52 + ~Process(); 53 + 54 + Process(const Process&) = delete; 55 + Process& operator=(const Process&) = delete; 56 + Process(Process&&) = delete; 57 + Process& operator=(Process&&) = delete; 58 + 59 + /** 60 + * The PID of this Process as seen from darlingserver's namespace. 61 + */ 62 + ID id() const; 63 + 64 + /** 65 + * The PID of this Process as seen from within the container (i.e. launchd's namespace). 66 + */ 67 + NSID nsid() const; 68 + 69 + std::vector<std::shared_ptr<Thread>> threads() const; 70 + 71 + std::string vchrootPath() const; 72 + void setVchrootPath(std::string path); 73 + }; 74 + }; 75 + 76 + #endif // _DARLINGSERVER_PROCESS_HPP_
+171
src/darlingserver/internal-include/darlingserver/registry.hpp
··· 1 + /** 2 + * This file is part of Darling. 3 + * 4 + * Copyright (C) 2021 Darling developers 5 + * 6 + * Darling is free software: you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation, either version 3 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * Darling is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + 20 + #ifndef _DARLINGSERVER_REGISTRY_HPP_ 21 + #define _DARLINGSERVER_REGISTRY_HPP_ 22 + 23 + #include <vector> 24 + #include <memory> 25 + #include <mutex> 26 + #include <unordered_map> 27 + #include <shared_mutex> 28 + #include <optional> 29 + #include <functional> 30 + 31 + #include <sys/types.h> 32 + 33 + #include <darlingserver/message.hpp> 34 + #include <darlingserver/process.hpp> 35 + #include <darlingserver/thread.hpp> 36 + 37 + namespace DarlingServer { 38 + template<class Entry> 39 + class Registry { 40 + private: 41 + std::unordered_map<typename Entry::ID, std::shared_ptr<Entry>> _map; 42 + std::unordered_map<typename Entry::NSID, std::shared_ptr<Entry>> _nsmap; 43 + std::shared_mutex _rwlock; 44 + 45 + public: 46 + using ID = typename Entry::ID; 47 + using NSID = typename Entry::NSID; 48 + 49 + std::shared_ptr<Entry> registerIfAbsent(NSID nsid, std::function<std::shared_ptr<Entry>()> entryFactory) { 50 + std::unique_lock lock(_rwlock); 51 + 52 + auto it2 = _nsmap.find(nsid); 53 + if (it2 != _nsmap.end()) { 54 + return (*it2).second; 55 + } 56 + 57 + auto entry = entryFactory(); 58 + _map[entry->id()] = entry; 59 + _nsmap[entry->nsid()] = entry; 60 + return entry; 61 + }; 62 + 63 + bool registerEntry(std::shared_ptr<Entry> entry, bool replace = false) { 64 + std::unique_lock lock(_rwlock); 65 + 66 + if (!replace && (_map.find(entry->id()) != _map.end() || _nsmap.find(entry->nsid()) != _nsmap.end())) { 67 + return false; 68 + } 69 + 70 + _map[entry->id()] = entry; 71 + _nsmap[entry->nsid()] = entry; 72 + return true; 73 + }; 74 + 75 + bool unregisterEntryByID(ID id) { 76 + std::unique_lock lock(_rwlock); 77 + 78 + auto it = _map.find(id); 79 + 80 + if (it == _map.end()) { 81 + return false; 82 + } 83 + 84 + std::shared_ptr<Entry> entry = (*it).second; 85 + auto it2 = _nsmap.find(entry->nsid()); 86 + 87 + if (it2 == _nsmap.end()) { 88 + return false; 89 + } 90 + 91 + _map.erase(it); 92 + _nsmap.erase(it2); 93 + return true; 94 + }; 95 + 96 + bool unregisterEntryByNSID(NSID nsid) { 97 + std::unique_lock lock(_rwlock); 98 + 99 + auto it2 = _nsmap.find(nsid); 100 + 101 + if (it2 == _nsmap.end()) { 102 + return false; 103 + } 104 + 105 + std::shared_ptr<Entry> entry = (*it2).second; 106 + auto it = _map.find(entry->id()); 107 + 108 + if (it == _map.end()) { 109 + return false; 110 + } 111 + 112 + _map.erase(it); 113 + _nsmap.erase(it2); 114 + return true; 115 + }; 116 + 117 + /** 118 + * Unregisters the given entry from this Registry. 119 + * 120 + * This is the recommended method for unregistering entries as it will 121 + * actually compare pointers to ensure the entry being unregistered is 122 + * the same as the one currently registered with the same IDs. 123 + */ 124 + bool unregisterEntry(std::shared_ptr<Entry> entry) { 125 + std::unique_lock lock(_rwlock); 126 + 127 + auto it = _map.find(entry->id()); 128 + auto it2 = _nsmap.find(entry->nsid()); 129 + 130 + if (it == _map.end() || it2 == _nsmap.end()) { 131 + return false; 132 + } 133 + 134 + // note that we *want* pointer-to-pointer comparison 135 + if ((*it).second != entry || (*it2).second != entry) { 136 + return false; 137 + } 138 + 139 + _map.erase(it); 140 + _nsmap.erase(it2); 141 + return true; 142 + }; 143 + 144 + std::optional<std::shared_ptr<Entry>> lookupEntryByID(ID id) { 145 + std::shared_lock lock(_rwlock); 146 + 147 + auto it = _map.find(id); 148 + if (it == _map.end()) { 149 + return std::nullopt; 150 + } 151 + 152 + return *it; 153 + }; 154 + 155 + std::optional<std::shared_ptr<Entry>> lookupEntryByNSID(ID nsid) { 156 + std::shared_lock lock(_rwlock); 157 + 158 + auto it2 = _nsmap.find(nsid); 159 + if (it2 == _nsmap.end()) { 160 + return std::nullopt; 161 + } 162 + 163 + return *it2; 164 + }; 165 + }; 166 + 167 + Registry<Process>& processRegistry(); 168 + Registry<Thread>& threadRegistry(); 169 + }; 170 + 171 + #endif // _DARLINGSERVER_REGISTRY_HPP_
+65
src/darlingserver/internal-include/darlingserver/server.hpp
··· 1 + /** 2 + * This file is part of Darling. 3 + * 4 + * Copyright (C) 2021 Darling developers 5 + * 6 + * Darling is free software: you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation, either version 3 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * Darling is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + 20 + #ifndef _DARLINGSERVER_SERVER_HPP_ 21 + #define _DARLINGSERVER_SERVER_HPP_ 22 + 23 + #include <string> 24 + #include <sys/epoll.h> 25 + #include <thread> 26 + 27 + #include <darlingserver/message.hpp> 28 + #include <darlingserver/workers.hpp> 29 + #include <darlingserver/call.hpp> 30 + #include <darlingserver/registry.hpp> 31 + 32 + namespace DarlingServer { 33 + // NOTE: server instances MUST be created with `new` rather than as a normal local/stack variable 34 + class Server { 35 + private: 36 + int _listenerSocket; 37 + std::string _prefix; 38 + std::string _socketPath; 39 + int _epollFD; 40 + MessageQueue _inbox; 41 + MessageQueue _outbox; 42 + WorkQueue<Message> _workQueue; 43 + 44 + void _worker(DarlingServer::Message message); 45 + 46 + public: 47 + Server(std::string prefix); 48 + ~Server(); 49 + 50 + Server(const Server&) = delete; 51 + Server& operator=(const Server&) = delete; 52 + Server(Server&&) = delete; 53 + Server& operator=(Server&&) = delete; 54 + 55 + void start(); 56 + 57 + void monitorProcess(std::shared_ptr<Process> process); 58 + 59 + std::string prefix() const; 60 + 61 + static Server& sharedInstance(); 62 + }; 63 + }; 64 + 65 + #endif // _DARLINGSERVER_SERVER_HPP_
+79
src/darlingserver/internal-include/darlingserver/thread.hpp
··· 1 + /** 2 + * This file is part of Darling. 3 + * 4 + * Copyright (C) 2021 Darling developers 5 + * 6 + * Darling is free software: you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation, either version 3 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * Darling is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + 20 + #ifndef _DARLINGSERVER_THREAD_HPP_ 21 + #define _DARLINGSERVER_THREAD_HPP_ 22 + 23 + #include <memory> 24 + #include <sys/types.h> 25 + #include <mutex> 26 + #include <shared_mutex> 27 + 28 + #include <darlingserver/message.hpp> 29 + 30 + namespace DarlingServer { 31 + class Process; 32 + class Call; 33 + 34 + class Thread: public std::enable_shared_from_this<Thread> { 35 + friend class Process; 36 + 37 + private: 38 + pid_t _tid; 39 + pid_t _nstid; 40 + std::weak_ptr<Process> _process; 41 + std::shared_ptr<Call> _pendingCall; 42 + Address _address; 43 + mutable std::shared_mutex _rwlock; 44 + 45 + public: 46 + using ID = pid_t; 47 + using NSID = ID; 48 + 49 + Thread(std::shared_ptr<Process> process, NSID nsid); 50 + ~Thread() noexcept(false); 51 + 52 + void registerWithProcess(); 53 + 54 + Thread(const Thread&) = delete; 55 + Thread& operator=(const Thread&) = delete; 56 + Thread(Thread&&) = delete; 57 + Thread& operator=(Thread&&) = delete; 58 + 59 + std::shared_ptr<Process> process() const; 60 + 61 + std::shared_ptr<Call> pendingCall() const; 62 + void setPendingCall(std::shared_ptr<Call> newPendingCall); 63 + 64 + /** 65 + * The TID of this Thread as seen from darlingserver's namespace. 66 + */ 67 + ID id() const; 68 + 69 + /** 70 + * The TID of this Thread as seen from within the container (i.e. launchd's namespace). 71 + */ 72 + NSID nsid() const; 73 + 74 + Address address() const; 75 + void setAddress(Address address); 76 + }; 77 + }; 78 + 79 + #endif // _DARLINGSERVER_THREAD_HPP_
+176
src/darlingserver/internal-include/darlingserver/workers.hpp
··· 1 + /** 2 + * This file is part of Darling. 3 + * 4 + * Copyright (C) 2021 Darling developers 5 + * 6 + * Darling is free software: you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation, either version 3 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * Darling is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + 20 + #ifndef _DARLINGSERVER_WORKERS_HPP_ 21 + #define _DARLINGSERVER_WORKERS_HPP_ 22 + 23 + #include <queue> 24 + #include <functional> 25 + #include <thread> 26 + #include <vector> 27 + #include <mutex> 28 + #include <condition_variable> 29 + #include <unordered_map> 30 + #include <chrono> 31 + #include <optional> 32 + 33 + namespace DarlingServer { 34 + template<class WorkItem> 35 + class WorkQueue { 36 + private: 37 + std::mutex _queueMutex; 38 + std::queue<WorkItem> _workItems; 39 + std::function<void(WorkItem)> _workerFunction; 40 + std::unordered_map<std::thread::id, std::thread> _workerThreads; 41 + size_t _threadsAvailable = 0; 42 + std::condition_variable _cv; 43 + bool _dying = false; 44 + 45 + static constexpr unsigned int minimumThreads() { 46 + return 2; 47 + }; 48 + 49 + static unsigned int maximumThreads() { 50 + unsigned int hw = std::thread::hardware_concurrency(); 51 + if (hw > 0 && hw - 1 > minimumThreads()) { 52 + // minus one for the main thread 53 + return hw - 1; 54 + } else { 55 + return minimumThreads(); 56 + } 57 + }; 58 + 59 + static constexpr std::chrono::seconds temporaryWorkerWaitPeriod() { 60 + return std::chrono::seconds(15); 61 + }; 62 + 63 + void worker(bool permanent) { 64 + std::optional<WorkItem> workItem = std::nullopt; 65 + std::unique_lock lock(_queueMutex, std::defer_lock); 66 + 67 + while (true) { 68 + // acquire the lock for the condition variable 69 + lock.lock(); 70 + 71 + // we're going to wait for work, so we're available 72 + ++_threadsAvailable; 73 + 74 + bool waitResult; 75 + auto predicate = [&, this]() { 76 + // if we're dying, stop waiting 77 + if (_dying) { 78 + --_threadsAvailable; 79 + return true; 80 + } 81 + 82 + // if we have a work item ready, take it and stop waiting 83 + if (_workItems.size() > 0) { 84 + workItem = std::move(_workItems.front()); 85 + _workItems.pop(); 86 + --_threadsAvailable; 87 + return true; 88 + } 89 + 90 + // otherwise, let's keep waiting 91 + return false; 92 + }; 93 + 94 + if (permanent) { 95 + // permanent workers just wait until there's work available (or the queue is being destroyed) 96 + _cv.wait(lock, predicate); 97 + waitResult = true; 98 + } else { 99 + // temporary workers wait for a certain period of time for work; if there's no work available after that period, they die 100 + waitResult = _cv.wait_for(lock, temporaryWorkerWaitPeriod(), predicate); 101 + } 102 + 103 + // if the queue is being destroyed, let the thread die 104 + if (_dying) { 105 + return; 106 + } 107 + 108 + // if we failed to find any work items, let's die 109 + if (!waitResult) { 110 + auto thisThread = std::move(_workerThreads[std::this_thread::get_id()]); 111 + _workerThreads.erase(thisThread.get_id()); 112 + thisThread.detach(); 113 + return; 114 + } 115 + 116 + // drop the lock to perform the work 117 + lock.unlock(); 118 + 119 + // perform the work 120 + _workerFunction(std::move(workItem.value())); 121 + workItem = std::nullopt; 122 + } 123 + }; 124 + 125 + public: 126 + WorkQueue(std::function<void(WorkItem)> workerFunction): 127 + _workerFunction(workerFunction) 128 + { 129 + std::scoped_lock lock(_queueMutex); 130 + 131 + // start the permanent worker threads 132 + for (size_t i = 0; i < minimumThreads(); ++i) { 133 + std::thread worker = std::thread(&WorkQueue::worker, this, true); 134 + _workerThreads[worker.get_id()] = std::move(worker); 135 + } 136 + }; 137 + ~WorkQueue() { 138 + // tell all the threads that we're being destroyed 139 + std::unique_lock lock(_queueMutex); 140 + _dying = true; 141 + lock.unlock(); 142 + _cv.notify_all(); 143 + 144 + // now wait for all of the threads to die 145 + for (auto& [id, thread]: _workerThreads) { 146 + thread.join(); 147 + } 148 + }; 149 + 150 + WorkQueue(const WorkQueue&) = delete; 151 + WorkQueue& operator=(const WorkQueue&) = delete; 152 + WorkQueue(WorkQueue&&) = delete; 153 + WorkQueue& operator=(WorkQueue&&) = delete; 154 + 155 + void push(WorkItem workItem) { 156 + std::unique_lock lock(_queueMutex); 157 + _workItems.push(std::move(workItem)); 158 + 159 + // if there are no threads available to perform the work AND we have less than the 160 + // maximum number of worker threads currently active, spawn a temporary worker thread. 161 + if (_threadsAvailable == 0 && _workerThreads.size() < maximumThreads()) { 162 + std::thread worker = std::thread(&WorkQueue::worker, this, false); 163 + _workerThreads[worker.get_id()] = std::move(worker); 164 + 165 + // note that we don't need to notify anyone in this case, since we want the new worker thread 166 + // to take care of the new work item (it's not a problem if another thread gets to it first, though). 167 + } else { 168 + // otherwise, notify one of the available threads that there's work available 169 + lock.unlock(); 170 + _cv.notify_one(); 171 + } 172 + }; 173 + }; 174 + }; 175 + 176 + #endif // _DARLINGSERVER_WORKERS_HPP_
+563
src/darlingserver/scripts/generate-rpc-wrappers.py
··· 1 + #!/usr/bin/env python3 2 + 3 + import os 4 + import sys 5 + from collections import OrderedDict 6 + import textwrap 7 + from datetime import datetime 8 + 9 + # NOTE: in Python 3.7+, we can rely on dictionaries having their items in insertion order. 10 + # unfortunately, we can't expect everyone building Darling to have Python 3.7+ installed. 11 + calls = [ 12 + ('checkin', [], []), 13 + 14 + ('checkout', [], []), 15 + 16 + ('vchroot_path', [ 17 + ('buffer', 'char*'), 18 + ('buffer_size', 'size_t'), 19 + ], [ 20 + ('length', 'size_t'), 21 + ]), 22 + ] 23 + 24 + if len(sys.argv) < 5: 25 + sys.exit("Usage: " + sys.argv[0] + " <public-header-path> <internal-header-path> <library-source-path> <library-import>") 26 + 27 + os.makedirs(os.path.dirname(sys.argv[1]), exist_ok=True) 28 + os.makedirs(os.path.dirname(sys.argv[2]), exist_ok=True) 29 + os.makedirs(os.path.dirname(sys.argv[3]), exist_ok=True) 30 + 31 + def to_camel_case(snake_str): 32 + components = snake_str.split('_') 33 + return ''.join(x.title() for x in components) 34 + 35 + public_header = open(sys.argv[1], "w") 36 + internal_header = open(sys.argv[2], "w") 37 + library_source = open(sys.argv[3], "w") 38 + library_import = sys.argv[4] 39 + 40 + license_header = """\ 41 + // This file has been auto-generated by generate-rpc-wrappers.py for use with darlingserver 42 + 43 + /** 44 + * This file is part of Darling. 45 + * 46 + * Copyright (C) {} Darling developers 47 + * 48 + * Darling is free software: you can redistribute it and/or modify 49 + * it under the terms of the GNU General Public License as published by 50 + * the Free Software Foundation, either version 3 of the License, or 51 + * (at your option) any later version. 52 + * 53 + * Darling is distributed in the hope that it will be useful, 54 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 55 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 56 + * GNU General Public License for more details. 57 + * 58 + * You should have received a copy of the GNU General Public License 59 + * along with Darling. If not, see <http://www.gnu.org/licenses/>. 60 + */ 61 + 62 + """.format(datetime.now().year) 63 + 64 + public_header.write(license_header) 65 + library_source.write(license_header) 66 + internal_header.write(license_header) 67 + 68 + public_header.write("""\ 69 + #ifndef _DARLINGSERVER_API_H_ 70 + #define _DARLINGSERVER_API_H_ 71 + 72 + #include <sys/types.h> 73 + 74 + #ifdef __cplusplus 75 + extern "C" { 76 + #endif 77 + 78 + """) 79 + 80 + public_header.write("enum dserver_callnum {\n") 81 + public_header.write("\tdserver_callnum_invalid,\n") 82 + for call in calls: 83 + call_name = call[0] 84 + call_parameters = call[1] 85 + reply_parameters = call[2] 86 + 87 + public_header.write("\tdserver_callnum_" + call_name + ",\n") 88 + public_header.write("};\n") 89 + 90 + public_header.write("""\ 91 + 92 + typedef enum dserver_callnum dserver_callnum_t; 93 + 94 + typedef struct dserver_rpc_callhdr { 95 + pid_t pid; 96 + pid_t tid; 97 + dserver_callnum_t number; 98 + } dserver_rpc_callhdr_t; 99 + 100 + typedef struct dserver_rpc_replyhdr { 101 + dserver_callnum_t number; 102 + int code; 103 + } dserver_rpc_replyhdr_t; 104 + 105 + """) 106 + 107 + library_source.write("""\ 108 + #include {} 109 + 110 + #if !defined(dserver_rpc_hooks_msghdr_t) || !defined(dserver_rpc_hooks_iovec_t) || !defined(dserver_rpc_hooks_cmsghdr_t) || !defined(DSERVER_RPC_HOOKS_CMSG_SPACE) || !defined(DSERVER_RPC_HOOKS_CMSG_FIRSTHDR) || !defined(DSERVER_RPC_HOOKS_SOL_SOCKET) || !defined(DSERVER_RPC_HOOKS_SCM_RIGHTS) || !defined(DSERVER_RPC_HOOKS_CMSG_LEN) || !defined(DSERVER_RPC_HOOKS_CMSG_DATA) || !defined(DSERVER_RPC_HOOKS_ATTRIBUTE) 111 + #error Missing definitions 112 + #endif 113 + 114 + #ifndef dserver_rpc_hooks_get_pid 115 + DSERVER_RPC_HOOKS_ATTRIBUTE pid_t dserver_rpc_hooks_get_pid(void); 116 + #endif 117 + 118 + #ifndef dserver_rpc_hooks_get_tid 119 + DSERVER_RPC_HOOKS_ATTRIBUTE pid_t dserver_rpc_hooks_get_tid(void); 120 + #endif 121 + 122 + #ifndef dserver_rpc_hooks_get_server_address 123 + DSERVER_RPC_HOOKS_ATTRIBUTE void* dserver_rpc_hooks_get_server_address(void); 124 + #endif 125 + 126 + #ifndef dserver_rpc_hooks_get_server_address_length 127 + DSERVER_RPC_HOOKS_ATTRIBUTE size_t dserver_rpc_hooks_get_server_address_length(void); 128 + #endif 129 + 130 + #ifndef dserver_rpc_hooks_memcpy 131 + DSERVER_RPC_HOOKS_ATTRIBUTE void* dserver_rpc_hooks_memcpy(void* destination, const void* source, size_t length); 132 + #endif 133 + 134 + #ifndef dserver_rpc_hooks_send_message 135 + DSERVER_RPC_HOOKS_ATTRIBUTE long int dserver_rpc_hooks_send_message(int socket, const dserver_rpc_hooks_msghdr_t* message); 136 + #endif 137 + 138 + #ifndef dserver_rpc_hooks_receive_message 139 + DSERVER_RPC_HOOKS_ATTRIBUTE long int dserver_rpc_hooks_receive_message(int socket, dserver_rpc_hooks_msghdr_t* out_message); 140 + #endif 141 + 142 + #ifndef dserver_rpc_hooks_get_bad_message_status 143 + DSERVER_RPC_HOOKS_ATTRIBUTE int dserver_rpc_hooks_get_bad_message_status(void); 144 + #endif 145 + 146 + #ifndef dserver_rpc_hooks_get_communication_error_status 147 + DSERVER_RPC_HOOKS_ATTRIBUTE int dserver_rpc_hooks_get_communication_error_status(void); 148 + #endif 149 + 150 + #ifndef dserver_rpc_hooks_get_broken_pipe_status 151 + DSERVER_RPC_HOOKS_ATTRIBUTE int dserver_rpc_hooks_get_broken_pipe_status(void); 152 + #endif 153 + 154 + #ifndef dserver_rpc_hooks_close_fd 155 + DSERVER_RPC_HOOKS_ATTRIBUTE void dserver_rpc_hooks_close_fd(int fd); 156 + #endif 157 + 158 + #ifndef dserver_rpc_hooks_get_socket 159 + DSERVER_RPC_HOOKS_ATTRIBUTE int dserver_rpc_hooks_get_socket(void); 160 + #endif 161 + 162 + """.format(library_import)) 163 + 164 + internal_header.write("#define DSERVER_VALID_CALLNUM_CASES \\\n") 165 + for call in calls: 166 + call_name = call[0] 167 + call_parameters = call[1] 168 + reply_parameters = call[2] 169 + 170 + internal_header.write("\tcase dserver_callnum_" + call_name + ": \\\n") 171 + internal_header.write("\n") 172 + 173 + internal_header.write("#define DSERVER_CONSTRUCT_CASES \\\n") 174 + for call in calls: 175 + call_name = call[0] 176 + call_parameters = call[1] 177 + reply_parameters = call[2] 178 + camel_name = to_camel_case(call_name) 179 + 180 + internal_header.write("\tCALL_CASE(" + call_name + ", " + camel_name + "); \\\n") 181 + internal_header.write("\n") 182 + 183 + internal_header.write("#define DSERVER_ENUM_VALUES \\\n") 184 + for call in calls: 185 + call_name = call[0] 186 + call_parameters = call[1] 187 + reply_parameters = call[2] 188 + camel_name = to_camel_case(call_name) 189 + 190 + internal_header.write("\t" + camel_name + " = dserver_callnum_" + call_name + ", \\\n") 191 + internal_header.write("\n") 192 + 193 + internal_header.write("#define DSERVER_CLASS_DECLS \\\n") 194 + for call in calls: 195 + call_name = call[0] 196 + call_parameters = call[1] 197 + reply_parameters = call[2] 198 + camel_name = to_camel_case(call_name) 199 + 200 + internal_header.write("\tclass " + camel_name + "; \\\n") 201 + internal_header.write("\n") 202 + 203 + internal_header.write("#define DSERVER_CLASS_DEFS \\\n") 204 + for call in calls: 205 + call_name = call[0] 206 + call_parameters = call[1] 207 + reply_parameters = call[2] 208 + camel_name = to_camel_case(call_name) 209 + fd_count_in_reply = 0 210 + 211 + internal_header.write(textwrap.indent(textwrap.dedent("""\ 212 + class Call::{1}: public Call {{ \\ 213 + friend class Call; \\ 214 + private: \\ 215 + {2} 216 + public: \\ 217 + {1}(MessageQueue& replyQueue, std::shared_ptr<Thread> thread, dserver_rpc_call_{0}_t* data, Message&& requestMessage): \\ 218 + Call(replyQueue, thread, requestMessage.address()){3} \\ 219 + {4} 220 + {{ \\ 221 + """), '\t').format( 222 + call_name, 223 + camel_name, 224 + ("dserver_call_" + call_name + "_t _body; \\") if len(call_parameters) > 0 else "\\", 225 + "," if len(call_parameters) > 0 else "", 226 + "_body(data->body) \\" if len(call_parameters) > 0 else "\\" 227 + ) 228 + ) 229 + 230 + for param in call_parameters: 231 + param_name = param[0] 232 + param_type = param[1] 233 + 234 + if param_type != "@fd": 235 + continue 236 + 237 + param_type = "int" 238 + 239 + internal_header.write("\t\t\t_body." + param_name + " = requestMessage.extractDescriptorAtIndex(_body." + param_name + "); \\\n") 240 + internal_header.write("\t\t}; \\\n") 241 + 242 + internal_header.write("\t\t~" + camel_name + "() { \\\n") 243 + for param in call_parameters: 244 + param_name = param[0] 245 + param_type = param[1] 246 + 247 + if param_type != "@fd": 248 + continue 249 + 250 + param_type = "int" 251 + 252 + internal_header.write("\t\t\tif (_body." + param_name + " != -1) { \\\n") 253 + internal_header.write("\t\t\t\tclose(_body." + param_name + "); \\\n") 254 + internal_header.write("\t\t} \\\n") 255 + 256 + internal_header.write(textwrap.indent(textwrap.dedent("""\ 257 + }}; \\ 258 + virtual Call::Number number() const {{ \\ 259 + return Call::Number::{0}; \\ 260 + }}; \\ 261 + virtual void processCall(); \\ 262 + private: \\ 263 + """), '\t').format(camel_name)) 264 + 265 + internal_header.write("\t\tvoid _sendReply(int resultCode") 266 + for param in reply_parameters: 267 + param_name = param[0] 268 + param_type = param[1] 269 + 270 + if param_type == "@fd": 271 + param_type = "int" 272 + fd_count_in_reply += 1 273 + 274 + internal_header.write(", " + param_type + " " + param_name) 275 + internal_header.write(") { \\\n") 276 + 277 + internal_header.write(textwrap.indent(textwrap.dedent("""\ 278 + Message reply(sizeof(dserver_rpc_reply_{0}_t), {1}); \\ 279 + reply.setAddress(_replyAddress); \\ 280 + auto replyStruct = reinterpret_cast<dserver_rpc_reply_{0}_t*>(reply.data().data()); \\ 281 + replyStruct->header.number = dserver_callnum_{0}; \\ 282 + replyStruct->header.code = resultCode; \\ 283 + """), '\t\t\t').format(call_name, fd_count_in_reply)) 284 + 285 + fd_index = 0 286 + for param in reply_parameters: 287 + param_name = param[0] 288 + param_type = param[1] 289 + val = param_name 290 + 291 + if param_type == "@fd": 292 + param_type = "int" 293 + val = str(fd_index) 294 + internal_header.write("\t\t\treply.pushDescriptor(" + param_name + "); \\\n") 295 + 296 + internal_header.write("\t\t\treplyStruct->body." + param_name + " = " + val + "; \\\n") 297 + internal_header.write("\t\t\t_replyQueue.push(std::move(reply)); \\\n") 298 + internal_header.write("\t\t}; \\\n") 299 + 300 + internal_header.write("\t}; \\\n") 301 + internal_header.write("\n") 302 + 303 + for call in calls: 304 + call_name = call[0] 305 + call_parameters = call[1] 306 + reply_parameters = call[2] 307 + fd_count_in_call = 0 308 + fd_count_in_reply = 0 309 + 310 + # define the RPC call body structure 311 + if len(call_parameters) > 0: 312 + public_header.write("typedef struct dserver_call_" + call_name + " dserver_call_" + call_name + "_t;\n") 313 + public_header.write("struct dserver_call_" + call_name + " {\n") 314 + for param in call_parameters: 315 + param_name = param[0] 316 + param_type = param[1] 317 + 318 + if param_type == "@fd": 319 + param_type = "int" 320 + fd_count_in_call += 1 321 + 322 + public_header.write("\t" + param_type + " " + param_name + ";\n") 323 + public_header.write("};\n") 324 + 325 + # define the RPC call structure 326 + public_header.write(textwrap.dedent("""\ 327 + typedef struct dserver_rpc_call_{0} dserver_rpc_call_{0}_t; 328 + struct dserver_rpc_call_{0} {{ 329 + dserver_rpc_callhdr_t header; 330 + """).format(call_name)) 331 + if len(call_parameters) > 0: 332 + public_header.write("\tdserver_call_" + call_name + "_t body;\n") 333 + public_header.write("};\n") 334 + 335 + # define the RPC reply body structure 336 + if len(reply_parameters) > 0: 337 + public_header.write("typedef struct dserver_reply_" + call_name + " dserver_reply_" + call_name + "_t;\n") 338 + public_header.write("struct dserver_reply_" + call_name + " {\n") 339 + for param in reply_parameters: 340 + param_name = param[0] 341 + param_type = param[1] 342 + 343 + if param_type == "@fd": 344 + param_type = "int" 345 + fd_count_in_reply += 1 346 + 347 + public_header.write("\t" + param_type + " " + param_name + ";\n") 348 + public_header.write("};\n") 349 + 350 + # define the RPC reply structure 351 + public_header.write(textwrap.dedent("""\ 352 + typedef struct dserver_rpc_reply_{0} dserver_rpc_reply_{0}_t; 353 + struct dserver_rpc_reply_{0} {{ 354 + dserver_rpc_replyhdr_t header; 355 + """).format(call_name)) 356 + if len(reply_parameters) > 0: 357 + public_header.write("\tdserver_reply_" + call_name + "_t body;\n") 358 + public_header.write("};\n") 359 + 360 + # declare the RPC call wrapper function 361 + # (and output the prototype part of the function definition) 362 + tmp = "int dserver_rpc_" + call_name + "(" 363 + public_header.write(tmp) 364 + library_source.write(tmp) 365 + is_first = True 366 + for param in call_parameters: 367 + param_name = param[0] 368 + param_type = param[1] 369 + 370 + if param_type == "@fd": 371 + param_type = "int" 372 + 373 + if is_first: 374 + is_first = False 375 + tmp = "" 376 + else: 377 + tmp = ", " 378 + tmp += param_type + " " + param_name 379 + public_header.write(tmp) 380 + library_source.write(tmp) 381 + 382 + for param in reply_parameters: 383 + param_name = param[0] 384 + param_type = param[1] 385 + 386 + if param_type == "@fd": 387 + param_type = "int" 388 + 389 + if is_first: 390 + is_first = False 391 + tmp = "" 392 + else: 393 + tmp = ", " 394 + tmp += param_type + "* out_" + param_name 395 + public_header.write(tmp) 396 + library_source.write(tmp) 397 + public_header.write(");\n\n") 398 + library_source.write(") {\n") 399 + 400 + # define the RPC call wrapper function 401 + library_source.write(textwrap.indent(textwrap.dedent("""\ 402 + int status = 0; 403 + dserver_rpc_call_{0}_t call = {{ 404 + .header = {{ 405 + .pid = dserver_rpc_hooks_get_pid(), 406 + .tid = dserver_rpc_hooks_get_tid(), 407 + .number = dserver_callnum_{0}, 408 + }}, 409 + """), '\t').format(call_name)) 410 + 411 + if len(call_parameters) > 0: 412 + library_source.write("\t\t.body = {\n") 413 + fd_index = 0 414 + for param in call_parameters: 415 + param_name = param[0] 416 + param_type = param[1] 417 + val = param_name 418 + 419 + if param_type == "@fd": 420 + param_type = "int" 421 + val = "(" + param_name + " < 0) ? -1 : " + str(fd_index) 422 + fd_index += 1 423 + 424 + library_source.write("\t\t\t." + param_name + " = " + val + ",\n") 425 + library_source.write("\t\t},\n") 426 + 427 + library_source.write("\t};\n") 428 + library_source.write("\tdserver_rpc_reply_" + call_name + "_t reply;\n") 429 + 430 + if fd_count_in_call > 0 or fd_count_in_reply > 0: 431 + library_source.write("\tint fds[" + max(fd_count_in_call, fd_count_in_reply) + "]") 432 + if fd_count_in_call > 0: 433 + library_source.write(" = { ") 434 + is_first = True 435 + for param in call_parameters: 436 + param_name = param[0] 437 + param_type = param[1] 438 + 439 + if param_type != "@fd": 440 + continue 441 + 442 + if is_first: 443 + is_first = False 444 + else: 445 + library_source.write(", ") 446 + library_source.write(param_name) 447 + library_source.write(" }") 448 + library_source.write(";\n") 449 + library_source.write("\tchar controlbuf[DSERVER_RPC_HOOKS_CMSG_SPACE(sizeof(fds))];\n") 450 + 451 + library_source.write(textwrap.indent(textwrap.dedent("""\ 452 + dserver_rpc_hooks_iovec_t call_data = { 453 + .iov_base = &call, 454 + .iov_len = sizeof(call), 455 + }; 456 + dserver_rpc_hooks_msghdr_t callmsg = { 457 + .msg_name = dserver_rpc_hooks_get_server_address(), 458 + .msg_namelen = dserver_rpc_hooks_get_server_address_length(), 459 + .msg_iov = &call_data, 460 + .msg_iovlen = 1, 461 + """), '\t')) 462 + 463 + if fd_count_in_call == 0: 464 + library_source.write("\t\t.msg_control = NULL,\n") 465 + library_source.write("\t\t.msg_controllen = 0,\n") 466 + else: 467 + library_source.write("\t\t.msg_control = controlbuf,\n") 468 + library_source.write("\t\t.msg_controllen = sizeof(controlbuf),\n") 469 + 470 + library_source.write("\t};\n") 471 + 472 + if fd_count_in_call > 0: 473 + library_source.write(textwrap.indent(textwrap.dedent("""\ 474 + dserver_rpc_hooks_cmsghdr_t* call_cmsg = DSERVER_RPC_HOOKS_CMSG_FIRSTHDR(&callmsg); 475 + call_cmsg->cmsg_level = DSERVER_RPC_HOOKS_SOL_SOCKET; 476 + call_cmsg->cmsg_type = DSERVER_RPC_HOOKS_SCM_RIGHTS; 477 + call_cmsg->cmsg_len = DSERVER_RPC_HOOKS_CMSG_LEN(sizeof(int) * {0}); 478 + dserver_rpc_hooks_memcpy(DSERVER_RPC_HOOKS_CMSG_DATA(call_cmsg), fds, sizeof(int) * {0}); 479 + """), '\t').format(fd_count_in_call)) 480 + 481 + library_source.write(textwrap.indent(textwrap.dedent("""\ 482 + dserver_rpc_hooks_iovec_t reply_data = { 483 + .iov_base = &reply, 484 + .iov_len = sizeof(reply), 485 + }; 486 + dserver_rpc_hooks_msghdr_t replymsg = { 487 + .msg_name = NULL, 488 + .msg_namelen = 0, 489 + .msg_iov = &reply_data, 490 + .msg_iovlen = 1, 491 + """), '\t')) 492 + 493 + if fd_count_in_reply == 0: 494 + library_source.write("\t\t.msg_control = NULL,\n") 495 + library_source.write("\t\t.msg_controllen = 0,\n") 496 + else: 497 + library_source.write("\t\t.msg_control = controlbuf,\n") 498 + library_source.write("\t\t.msg_controllen = sizeof(controlbuf),\n") 499 + 500 + library_source.write("\t};\n\n") 501 + 502 + library_source.write("\tint socket = dserver_rpc_hooks_get_socket();\n") 503 + library_source.write("\tif (socket < 0) {") 504 + library_source.write("\t\treturn dserver_rpc_hooks_get_broken_pipe_status();\n") 505 + library_source.write("\t}\n\n") 506 + 507 + library_source.write("\tlong int long_status = dserver_rpc_hooks_send_message(socket, &callmsg);\n") 508 + library_source.write("\tif (long_status < 0) {\n") 509 + library_source.write("\t\treturn (int)long_status;\n") 510 + library_source.write("\t}\n\n") 511 + library_source.write("\tif (long_status != sizeof(call)) {\n") 512 + library_source.write("\t\treturn dserver_rpc_hooks_get_communication_error_status();\n") 513 + library_source.write("\t}\n\n") 514 + 515 + library_source.write("\tlong_status = dserver_rpc_hooks_receive_message(socket, &replymsg);\n") 516 + library_source.write("\tif (long_status < 0) {\n") 517 + library_source.write("\t\treturn (int)long_status;\n") 518 + library_source.write("\t}\n\n") 519 + library_source.write("\tif (long_status != sizeof(reply)) {\n") 520 + library_source.write("\t\treturn dserver_rpc_hooks_get_communication_error_status();\n") 521 + library_source.write("\t}\n\n") 522 + 523 + if fd_count_in_reply != 0: 524 + library_source.write(textwrap.indent(textwrap.dedent("""\ 525 + dserver_rpc_hooks_cmsghdr_t* reply_cmsg = DSERVER_RPC_HOOKS_CMSG_FIRSTHDR(&replymsg); 526 + if (!reply_cmsg || reply_cmsg->cmsg_level != DSERVER_RPC_HOOKS_SOL_SOCKET || reply_cmsg->cmsg_type != DSERVER_RPC_HOOKS_SCM_RIGHTS || reply_cmsg->cmsg_len != DSERVER_RPC_HOOKS_CMSG_LEN(sizeof(int) * {0})) {{ 527 + status = dserver_rpc_hooks_get_bad_message_status(); 528 + return status; 529 + }} 530 + dserver_rpc_hooks_memcpy(fds, DSERVER_RPC_HOOKS_CMSG_DATA(reply_cmsg), sizeof(int) * {0}); 531 + """), '\t').format(fd_count_in_reply)) 532 + 533 + for param in reply_parameters: 534 + param_name = param[0] 535 + param_type = param[1] 536 + 537 + if param_type == "@fd": 538 + param_type = "int" 539 + library_source.write("\tif (out_" + param_name + ") {\n") 540 + library_source.write("\t\t*out_" + param_name + " = fds[reply.body." + param_name + "];\n") 541 + library_source.write("\t} else {\n") 542 + library_source.write("\t\tdserver_rpc_hooks_close_fd(fds[reply.body." + param_name + "]);\n") 543 + library_source.write("\t}\n") 544 + else: 545 + library_source.write("\tif (out_" + param_name + ") {\n") 546 + library_source.write("\t\t*out_" + param_name + " = reply.body." + param_name + ";\n") 547 + library_source.write("\t}\n") 548 + 549 + library_source.write("\treturn status;\n") 550 + 551 + library_source.write("};\n\n") 552 + 553 + public_header.write("""\ 554 + #ifdef __cplusplus 555 + }; 556 + #endif 557 + 558 + #endif // _DARLINGSERVER_API_H_ 559 + """) 560 + 561 + public_header.close() 562 + internal_header.close() 563 + library_source.close()
+162
src/darlingserver/src/call.cpp
··· 1 + /** 2 + * This file is part of Darling. 3 + * 4 + * Copyright (C) 2021 Darling developers 5 + * 6 + * Darling is free software: you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation, either version 3 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * Darling is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + 20 + #define _GNU_SOURCE 1 21 + #include <darlingserver/call.hpp> 22 + #include <darlingserver/server.hpp> 23 + #include <sys/uio.h> 24 + 25 + std::shared_ptr<DarlingServer::Call> DarlingServer::Call::callFromMessage(Message&& requestMessage, MessageQueue& replyQueue) { 26 + if (requestMessage.data().size() < sizeof(dserver_rpc_callhdr_t)) { 27 + throw std::invalid_argument("Message buffer was too small for call header"); 28 + } 29 + 30 + dserver_rpc_callhdr_t* header = reinterpret_cast<dserver_rpc_callhdr_t*>(requestMessage.data().data()); 31 + std::shared_ptr<Call> result = nullptr; 32 + std::shared_ptr<Process> process = nullptr; 33 + std::shared_ptr<Thread> thread = nullptr; 34 + 35 + // first, make sure we know this call number 36 + switch (header->number) { 37 + DSERVER_VALID_CALLNUM_CASES 38 + break; 39 + 40 + default: 41 + throw std::invalid_argument("Invalid call number"); 42 + } 43 + 44 + // now let's lookup (and possibly create) the process and thread making this call 45 + process = processRegistry().registerIfAbsent(header->pid, [&]() { 46 + auto tmp = std::make_shared<Process>(requestMessage.pid(), header->pid); 47 + Server::sharedInstance().monitorProcess(tmp); 48 + return tmp; 49 + }); 50 + thread = threadRegistry().registerIfAbsent(header->tid, [&]() { 51 + auto tmp = std::make_shared<Thread>(process, header->tid); 52 + tmp->setAddress(requestMessage.address()); 53 + tmp->registerWithProcess(); 54 + return tmp; 55 + }); 56 + 57 + // finally, let's construct the call class 58 + 59 + #define CALL_CASE(_callName, _className) \ 60 + case dserver_callnum_ ## _callName: { \ 61 + if (requestMessage.data().size() < sizeof(dserver_rpc_call_ ## _callName ## _t)) { \ 62 + throw std::invalid_argument("Message buffer was too small for dserver_call_" #_callName "_t"); \ 63 + } \ 64 + result = std::make_shared<_className>(replyQueue, thread, reinterpret_cast<dserver_rpc_call_ ## _callName ## _t*>(header), std::move(requestMessage)); \ 65 + } break; 66 + 67 + switch (header->number) { 68 + DSERVER_CONSTRUCT_CASES 69 + 70 + default: 71 + throw std::invalid_argument("Invalid call number"); 72 + } 73 + 74 + #undef CALL_CASE 75 + 76 + thread->setPendingCall(result); 77 + 78 + return result; 79 + }; 80 + 81 + DarlingServer::Call::Call(MessageQueue& replyQueue, std::shared_ptr<Thread> thread, Address replyAddress): 82 + _replyQueue(replyQueue), 83 + _thread(thread), 84 + _replyAddress(replyAddress) 85 + {}; 86 + 87 + DarlingServer::Call::~Call() {}; 88 + 89 + /** 90 + * Note that you MUST NOT have any local variables referencing `this` when this method is called. 91 + * 92 + * This Call object MAY be destroyed upon the return of this method. 93 + */ 94 + void DarlingServer::Call::_stopPending() { 95 + // we're done processing this call; dump it 96 + if (auto thread = _thread.lock()) { 97 + thread->setPendingCall(nullptr); 98 + } 99 + }; 100 + 101 + void DarlingServer::Call::Checkin::processCall() { 102 + // the Call creation already took care of registering the process and thread 103 + _sendReply(0); 104 + _stopPending(); 105 + }; 106 + 107 + void DarlingServer::Call::Checkout::processCall() { 108 + int code = 0; 109 + 110 + if (auto thread = _thread.lock()) { 111 + if (auto process = thread->process()) { 112 + threadRegistry().unregisterEntry(thread); 113 + 114 + if (thread->id() == process->id()) { 115 + processRegistry().unregisterEntry(process); 116 + } 117 + } else { 118 + code = -ESRCH; 119 + } 120 + } else { 121 + code = -ESRCH; 122 + } 123 + 124 + _sendReply(code); 125 + _stopPending(); 126 + }; 127 + 128 + void DarlingServer::Call::VchrootPath::processCall() { 129 + int code = 0; 130 + size_t fullLength = 0; 131 + 132 + if (auto thread = _thread.lock()) { 133 + if (auto process = thread->process()) { 134 + if (_body.buffer_size > 0) { 135 + struct iovec local; 136 + struct iovec remote; 137 + auto tmpstr = process->vchrootPath().substr(0, _body.buffer_size - 1); 138 + auto len = std::min(tmpstr.length() + 1, _body.buffer_size); 139 + 140 + fullLength = process->vchrootPath().length(); 141 + 142 + // note that, despite the const cast, this is safe because the local iovec data is not modified by the call 143 + local.iov_base = const_cast<char*>(tmpstr.c_str()); 144 + local.iov_len = len; 145 + 146 + remote.iov_base = _body.buffer; 147 + remote.iov_len = len; 148 + 149 + if (process_vm_writev(process->id(), &local, 1, &remote, 1, 0) < 0) { 150 + code = -errno; 151 + } 152 + } 153 + } else { 154 + code = -ESRCH; 155 + } 156 + } else { 157 + code = -ESRCH; 158 + } 159 + 160 + _sendReply(code, fullLength); 161 + _stopPending(); 162 + };
+532
src/darlingserver/src/darlingserver.cpp
··· 1 + /** 2 + * This file is part of Darling. 3 + * 4 + * Copyright (C) 2021 Darling developers 5 + * 6 + * Darling is free software: you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation, either version 3 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * Darling is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + 20 + #define _GNU_SOURCE 1 21 + #include <stdio.h> 22 + #include <unistd.h> 23 + #include <sched.h> 24 + #include <stdlib.h> 25 + #include <sys/mount.h> 26 + #include <errno.h> 27 + #include <string.h> 28 + #include <stdbool.h> 29 + #include <sys/prctl.h> 30 + #include <dirent.h> 31 + #include <sys/stat.h> 32 + #include <pwd.h> 33 + #include <sys/mman.h> 34 + #include <fcntl.h> 35 + #include <sys/resource.h> 36 + #include <iostream> 37 + #include <linux/sched.h> 38 + #include <sys/syscall.h> 39 + #include <sys/signal.h> 40 + 41 + #include <darling-config.h> 42 + 43 + #include <darlingserver/server.hpp> 44 + 45 + // TODO: most of the code here was ported over from startup/darling.c; we should C++-ify it. 46 + 47 + #define MLDR_PATH LIBEXEC_PATH "/usr/libexec/darling/mldr" 48 + 49 + void fixPermissionsRecursive(const char* path, uid_t originalUID, gid_t originalGID) 50 + { 51 + DIR* dir; 52 + struct dirent* ent; 53 + 54 + if (chown(path, originalUID, originalGID) == -1) 55 + fprintf(stderr, "Cannot chown %s: %s\n", path, strerror(errno)); 56 + 57 + dir = opendir(path); 58 + if (!dir) 59 + return; 60 + 61 + while ((ent = readdir(dir)) != NULL) 62 + { 63 + if (ent->d_type == DT_DIR) 64 + { 65 + char* subdir; 66 + 67 + if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) 68 + continue; 69 + 70 + subdir = (char*) malloc(strlen(path) + 2 + strlen(ent->d_name)); 71 + sprintf(subdir, "%s/%s", path, ent->d_name); 72 + 73 + fixPermissionsRecursive(subdir, originalUID, originalGID); 74 + 75 + free(subdir); 76 + } 77 + } 78 + 79 + closedir(dir); 80 + } 81 + 82 + const char* xdgDirectory(const char* name) 83 + { 84 + static char dir[4096]; 85 + char* cmd = (char*) malloc(16 + strlen(name)); 86 + 87 + sprintf(cmd, "xdg-user-dir %s", name); 88 + 89 + FILE* proc = popen(cmd, "r"); 90 + 91 + free(cmd); 92 + 93 + if (!proc) 94 + return NULL; 95 + 96 + fgets(dir, sizeof(dir)-1, proc); 97 + 98 + pclose(proc); 99 + 100 + size_t len = strlen(dir); 101 + if (len <= 1) 102 + return NULL; 103 + 104 + if (dir[len-1] == '\n') 105 + dir[len-1] = '\0'; 106 + return dir; 107 + } 108 + 109 + void setupUserHome(const char* prefix, uid_t originalUID) 110 + { 111 + char buf[4096], buf2[4096]; 112 + 113 + snprintf(buf, sizeof(buf), "%s/Users", prefix); 114 + 115 + // Remove the old /Users symlink that may exist 116 + unlink(buf); 117 + 118 + // mkdir /Users 119 + mkdir(buf, 0777); 120 + 121 + // mkdir /Users/Shared 122 + strcat(buf, "/Shared"); 123 + mkdir(buf, 0777); 124 + 125 + const char* home = getenv("HOME"); 126 + 127 + const char* login = NULL; 128 + struct passwd* pw = getpwuid(originalUID); 129 + 130 + if (pw != NULL) 131 + login = pw->pw_name; 132 + 133 + if (!login) 134 + login = getlogin(); 135 + 136 + if (!login) 137 + { 138 + fprintf(stderr, "Cannot determine your user name\n"); 139 + exit(1); 140 + } 141 + if (!home) 142 + { 143 + fprintf(stderr, "Cannot determine your home directory\n"); 144 + exit(1); 145 + } 146 + 147 + snprintf(buf, sizeof(buf), "%s/Users/%s", prefix, login); 148 + 149 + // mkdir /Users/$LOGIN 150 + mkdir(buf, 0755); 151 + 152 + snprintf(buf2, sizeof(buf2), "/Volumes/SystemRoot%s", home); 153 + 154 + strcat(buf, "/LinuxHome"); 155 + unlink(buf); 156 + 157 + // symlink /Users/$LOGIN/LinuxHome -> $HOME 158 + symlink(buf2, buf); 159 + 160 + static const char* xdgmap[][2] = { 161 + { "DESKTOP", "Desktop" }, 162 + { "DOWNLOAD", "Downloads" }, 163 + { "PUBLICSHARE", "Public" }, 164 + { "DOCUMENTS", "Documents" }, 165 + { "MUSIC", "Music" }, 166 + { "PICTURES", "Pictures" }, 167 + { "VIDEOS", "Movies" }, 168 + }; 169 + 170 + for (int i = 0; i < sizeof(xdgmap) / sizeof(xdgmap[0]); i++) 171 + { 172 + const char* dir = xdgDirectory(xdgmap[i][0]); 173 + if (!dir) 174 + continue; 175 + 176 + snprintf(buf2, sizeof(buf2), "/Volumes/SystemRoot%s", dir); 177 + snprintf(buf, sizeof(buf), "%s/Users/%s/%s", prefix, login, xdgmap[i][1]); 178 + 179 + unlink(buf); 180 + symlink(buf2, buf); 181 + } 182 + } 183 + 184 + void setupCoredumpPattern(void) 185 + { 186 + FILE* f = fopen("/proc/sys/kernel/core_pattern", "w"); 187 + if (f != NULL) 188 + { 189 + // This is how macOS saves core dumps 190 + fputs("/cores/core.%p\n", f); 191 + fclose(f); 192 + } 193 + } 194 + 195 + static void wipeDir(const char* dirpath) 196 + { 197 + char path[4096]; 198 + struct dirent* ent; 199 + DIR* dir = opendir(dirpath); 200 + 201 + if (!dir) 202 + return; 203 + 204 + while ((ent = readdir(dir)) != NULL) 205 + { 206 + if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) 207 + continue; 208 + 209 + snprintf(path, sizeof(path), "%s/%s", dirpath, ent->d_name); 210 + 211 + if (ent->d_type == DT_DIR) 212 + { 213 + wipeDir(path); 214 + rmdir(path); 215 + } 216 + else 217 + unlink(path); 218 + } 219 + 220 + closedir(dir); 221 + } 222 + 223 + void darlingPreInit(const char* prefix) 224 + { 225 + // TODO: Run /usr/libexec/makewhatis 226 + const char* dirs[] = { 227 + "/var/tmp", 228 + "/var/run" 229 + }; 230 + 231 + char fullpath[4096]; 232 + strcpy(fullpath, prefix); 233 + const size_t prefixLen = strlen(fullpath); 234 + 235 + for (size_t i = 0; i < sizeof(dirs)/sizeof(dirs[0]); i++) 236 + { 237 + fullpath[prefixLen] = 0; 238 + strcat(fullpath, dirs[i]); 239 + wipeDir(fullpath); 240 + } 241 + } 242 + 243 + void spawnLaunchd(const char* prefix) 244 + { 245 + puts("Bootstrapping the container with launchd..."); 246 + 247 + // putenv("KQUEUE_DEBUG=1"); 248 + 249 + auto tmp = (std::string(prefix) + "/var/run/darlingserver.sock"); 250 + 251 + setenv("DYLD_ROOT_PATH", LIBEXEC_PATH, 1); 252 + setenv("__mldr_sockpath", tmp.c_str(), 1); 253 + execl(MLDR_PATH, "mldr!" LIBEXEC_PATH "/usr/libexec/darling/vchroot", "vchroot", prefix, "/sbin/launchd", NULL); 254 + 255 + fprintf(stderr, "Failed to exec launchd: %s\n", strerror(errno)); 256 + abort(); 257 + } 258 + 259 + static bool testEnvVar(const char* var_name) { 260 + const char* var = getenv(var_name); 261 + 262 + if (!var) { 263 + return false; 264 + } 265 + 266 + auto len = strlen(var); 267 + 268 + if (len > 0 && (var[0] == '1' || var[0] == 't' || var[0] == 'T')) { 269 + return true; 270 + } 271 + 272 + return false; 273 + }; 274 + 275 + static void temp_drop_privileges(uid_t uid, gid_t gid) { 276 + // it's important to drop GID first, because non-root users can't change their GID 277 + if (setresgid(gid, gid, 0) < 0) { 278 + fprintf(stderr, "Failed to temporarily drop group privileges\n"); 279 + exit(1); 280 + } 281 + if (setresuid(uid, uid, 0) < 0) { 282 + fprintf(stderr, "Failed to temporarily drop user privileges\n"); 283 + exit(1); 284 + } 285 + }; 286 + 287 + static void perma_drop_privileges(uid_t uid, gid_t gid) { 288 + if (setresgid(gid, gid, gid) < 0) { 289 + fprintf(stderr, "Failed to drop group privileges\n"); 290 + exit(1); 291 + } 292 + if (setresuid(uid, uid, uid) < 0) { 293 + fprintf(stderr, "Failed to drop user privileges\n"); 294 + exit(1); 295 + } 296 + }; 297 + 298 + static void regain_privileges() { 299 + if (seteuid(0) < 0) { 300 + fprintf(stderr, "Failed to regain root EUID\n"); 301 + exit(1); 302 + } 303 + if (setegid(0) < 0) { 304 + fprintf(stderr, "Failed to regain root EGID\n"); 305 + exit(1); 306 + } 307 + 308 + if (setresuid(0, 0, 0) < 0) { 309 + fprintf(stderr, "Failed to regain root privileges\n"); 310 + exit(1); 311 + } 312 + if (setresgid(0, 0, 0) < 0) { 313 + fprintf(stderr, "Failed to regain root privileges\n"); 314 + exit(1); 315 + } 316 + }; 317 + 318 + int main(int argc, char** argv) { 319 + const char* prefix = NULL; 320 + uid_t originalUID = -1; 321 + gid_t originalGID = -1; 322 + int pipefd = -1; 323 + bool fix_permissions = false; 324 + pid_t launchdGlobalPID = -1; 325 + size_t prefix_length = 0; 326 + struct rlimit default_limit; 327 + struct rlimit increased_limit; 328 + FILE* nr_open_file = NULL; 329 + int childWaitFDs[2]; 330 + 331 + char *opts; 332 + char putOld[4096]; 333 + char *p; 334 + 335 + if (argc < 6) { 336 + fprintf(stderr, "darlingserver is not meant to be started manually\n"); 337 + exit(1); 338 + } 339 + 340 + prefix = argv[1]; 341 + sscanf(argv[2], "%d", &originalUID); 342 + sscanf(argv[3], "%d", &originalGID); 343 + sscanf(argv[4], "%d", &pipefd); 344 + 345 + if (argv[5][0] == '1') { 346 + fix_permissions = true; 347 + } 348 + 349 + prefix_length = strlen(prefix); 350 + 351 + if (getuid() != 0 || getgid() != 0) { 352 + fprintf(stderr, "darlingserver needs to start as root\n"); 353 + exit(1); 354 + } 355 + 356 + // temporarily drop privileges to perform some prefix work 357 + temp_drop_privileges(originalUID, originalGID); 358 + setupUserHome(prefix, originalUID); 359 + //setupCoredumpPattern(); 360 + regain_privileges(); 361 + 362 + // read the default rlimit so we can restore it for our children 363 + if (getrlimit(RLIMIT_NOFILE, &default_limit) != 0) { 364 + fprintf(stderr, "Failed to read default FD rlimit: %s\n", strerror(errno)); 365 + exit(1); 366 + } 367 + 368 + // read the system maximum 369 + nr_open_file = fopen("/proc/sys/fs/nr_open", "r"); 370 + if (nr_open_file == NULL) { 371 + fprintf(stderr, "Failed to open /proc/sys/fs/nr_open: %s\n", strerror(errno)); 372 + exit(1); 373 + } 374 + if (fscanf(nr_open_file, "%lu", &increased_limit.rlim_max) != 1) { 375 + fprintf(stderr, "Failed to read /proc/sys/fs/nr_open: %s\n", strerror(errno)); 376 + exit(1); 377 + } 378 + increased_limit.rlim_cur = increased_limit.rlim_max; 379 + if (fclose(nr_open_file) != 0) { 380 + fprintf(stderr, "Failed to close /proc/sys/fs/nr_open: %s\n", strerror(errno)); 381 + exit(1); 382 + } 383 + 384 + // now set our increased rlimit 385 + if (setrlimit(RLIMIT_NOFILE, &increased_limit) != 0) { 386 + fprintf(stderr, "Failed to increase FD rlimit: %s\n", strerror(errno)); 387 + exit(1); 388 + } 389 + 390 + // Since overlay cannot be mounted inside user namespaces, we have to setup a new mount namespace 391 + // and do the mount while we can be root 392 + if (unshare(CLONE_NEWNS) != 0) 393 + { 394 + fprintf(stderr, "Cannot unshare PID and mount namespaces: %s\n", strerror(errno)); 395 + exit(1); 396 + } 397 + 398 + // Because systemd marks / as MS_SHARED and we would inherit this into the overlay mount, 399 + // causing it not to be unmounted once the init process dies. 400 + if (mount(NULL, "/", NULL, MS_REC | MS_SLAVE, NULL) != 0) 401 + { 402 + fprintf(stderr, "Cannot remount / as slave: %s\n", strerror(errno)); 403 + exit(1); 404 + } 405 + 406 + umount("/dev/shm"); 407 + if (mount("tmpfs", "/dev/shm", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV, NULL) != 0) 408 + { 409 + fprintf(stderr, "Cannot mount new /dev/shm: %s\n", strerror(errno)); 410 + exit(1); 411 + } 412 + 413 + opts = (char*) malloc(strlen(prefix)*2 + sizeof(LIBEXEC_PATH) + 100); 414 + 415 + const char* opts_fmt = "lowerdir=%s,upperdir=%s,workdir=%s.workdir,index=off"; 416 + 417 + sprintf(opts, opts_fmt, LIBEXEC_PATH, prefix, prefix); 418 + 419 + // Mount overlay onto our prefix 420 + if (mount("overlay", prefix, "overlay", 0, opts) != 0) 421 + { 422 + fprintf(stderr, "Cannot mount overlay: %s\n", strerror(errno)); 423 + exit(1); 424 + } 425 + 426 + free(opts); 427 + 428 + // This is executed once at prefix creation 429 + if (fix_permissions) { 430 + const char* extra_paths[] = { 431 + "/private/etc/passwd", 432 + "/private/etc/master.passwd", 433 + "/private/etc/group", 434 + }; 435 + char path[4096]; 436 + 437 + fixPermissionsRecursive(prefix, originalUID, originalGID); 438 + 439 + path[sizeof(path) - 1] = '\0'; 440 + strncpy(path, prefix, sizeof(path) - 1); 441 + for (size_t i = 0; i < sizeof(extra_paths) / sizeof(*extra_paths); ++i) { 442 + path[prefix_length] = '\0'; 443 + strncat(path, extra_paths[i], sizeof(path) - 1); 444 + fixPermissionsRecursive(path, originalUID, originalGID); 445 + } 446 + } 447 + 448 + snprintf(putOld, sizeof(putOld), "%s/proc", prefix); 449 + 450 + // mount procfs for our new PID namespace 451 + if (mount("proc", putOld, "proc", 0, "") != 0) 452 + { 453 + fprintf(stderr, "Cannot mount procfs: %s\n", strerror(errno)); 454 + exit(1); 455 + } 456 + 457 + // temporarily drop privileges and do some prefix work 458 + temp_drop_privileges(originalUID, originalGID); 459 + darlingPreInit(prefix); 460 + regain_privileges(); 461 + 462 + // Tell the parent we're ready 463 + write(pipefd, ".", 1); 464 + close(pipefd); 465 + 466 + if (pipe(childWaitFDs) != 0) { 467 + std::cerr << "Failed to create child waiting pipe: " << strerror(errno) << std::endl; 468 + exit(1); 469 + } 470 + 471 + // we have to use `clone` rather than `fork` to create the process in its own PID namespace 472 + // and still be able to spawn new processes and threads of our own 473 + struct clone_args launchdCloneArgs; 474 + launchdCloneArgs.flags = CLONE_NEWPID; 475 + launchdCloneArgs.pidfd = 0; 476 + launchdCloneArgs.child_tid = 0; 477 + launchdCloneArgs.parent_tid = 0; 478 + launchdCloneArgs.exit_signal = SIGCHLD; 479 + launchdCloneArgs.stack = 0; 480 + launchdCloneArgs.stack_size = 0; 481 + launchdCloneArgs.tls = 0; 482 + launchdCloneArgs.set_tid = 0; 483 + launchdCloneArgs.set_tid_size = 0; 484 + launchdCloneArgs.cgroup = 0; 485 + launchdGlobalPID = syscall(SYS_clone3, &launchdCloneArgs, sizeof(launchdCloneArgs)); 486 + 487 + // drop privileges in both parent and child 488 + perma_drop_privileges(originalUID, originalGID); 489 + prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); 490 + 491 + if (launchdGlobalPID < 0) { 492 + fprintf(stderr, "Failed to fork to start launchd: %s\n", strerror(errno)); 493 + exit(1); 494 + } else if (launchdGlobalPID == 0) { 495 + // this is the child 496 + char buf[1]; 497 + 498 + close(childWaitFDs[1]); 499 + 500 + // decrease the FD limit back to the default 501 + if (setrlimit(RLIMIT_NOFILE, &default_limit) != 0) { 502 + fprintf(stderr, "Failed to decrease FD limit back down for launchd: %s\n", strerror(errno)); 503 + exit(1); 504 + } 505 + 506 + // wait for the parent to give us the green light 507 + read(childWaitFDs[0], buf, 1); 508 + close(childWaitFDs[0]); 509 + 510 + spawnLaunchd(prefix); 511 + __builtin_unreachable(); 512 + } 513 + 514 + // this is the parent 515 + close(childWaitFDs[0]); 516 + 517 + // create the server 518 + auto server = new DarlingServer::Server(prefix); 519 + 520 + // tell the child to go ahead; the socket has been created 521 + write(childWaitFDs[1], ".", 1); 522 + close(childWaitFDs[1]); 523 + 524 + // start the main loop 525 + server->start(); 526 + 527 + // this should never happen 528 + std::cerr << "Server exited main loop!" << std::endl; 529 + delete server; 530 + 531 + return 1; 532 + };
+87
src/darlingserver/src/logging.cpp
··· 1 + /** 2 + * This file is part of Darling. 3 + * 4 + * Copyright (C) 2021 Darling developers 5 + * 6 + * Darling is free software: you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation, either version 3 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * Darling is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + 20 + #include <darlingserver/logging.hpp> 21 + #include <darlingserver/server.hpp> 22 + #include <filesystem> 23 + #include <fcntl.h> 24 + #include <unistd.h> 25 + 26 + DarlingServer::Log::Log(std::string category): 27 + _category(category) 28 + {}; 29 + 30 + DarlingServer::Log::Stream::Stream(Type type, Log& log): 31 + _type(type), 32 + _log(log) 33 + {}; 34 + template<> 35 + DarlingServer::Log::Stream& DarlingServer::Log::Stream::operator<<<EndLog>(EndLog value) { 36 + _log._log(_type, _buffer.str()); 37 + return *this; 38 + }; 39 + 40 + DarlingServer::Log::Stream DarlingServer::Log::debug() { 41 + return Stream(Type::Debug, *this); 42 + }; 43 + 44 + DarlingServer::Log::Stream DarlingServer::Log::info() { 45 + return Stream(Type::Info, *this); 46 + }; 47 + 48 + DarlingServer::Log::Stream DarlingServer::Log::warning() { 49 + return Stream(Type::Warning, *this); 50 + }; 51 + 52 + DarlingServer::Log::Stream DarlingServer::Log::error() { 53 + return Stream(Type::Error, *this); 54 + }; 55 + 56 + std::string DarlingServer::Log::_typeToString(Type type) { 57 + switch (type) { 58 + case Type::Debug: 59 + return "Debug"; 60 + case Type::Info: 61 + return "Info"; 62 + case Type::Warning: 63 + return "Warning"; 64 + case Type::Error: 65 + return "Error"; 66 + default: 67 + return "Unknown"; 68 + } 69 + }; 70 + 71 + 72 + void DarlingServer::Log::_log(Type type, std::string message) { 73 + // NOTE: we use POSIX file APIs because we want to append each message to the log file atomically, 74 + // and as far as i can tell, C++ fstreams provide no such guarantee (that they won't write in chunks). 75 + static int logFile = []() { 76 + std::filesystem::path path(Server::sharedInstance().prefix() + "/private/var/log/"); 77 + std::filesystem::create_directories(path.parent_path()); 78 + return open(path.c_str(), O_WRONLY | O_APPEND); 79 + }(); 80 + 81 + struct timespec time; 82 + clock_gettime(CLOCK_REALTIME, &time); 83 + double secs = (double)time.tv_sec + ((double)time.tv_nsec / 1.0e9); 84 + std::string messageToLog = "[" + std::to_string(secs) + "](" + _category + ", " + _typeToString(type) + ") " + message + "\n"; 85 + 86 + write(logFile, messageToLog.c_str(), messageToLog.size()); 87 + };
+539
src/darlingserver/src/message.cpp
··· 1 + /** 2 + * This file is part of Darling. 3 + * 4 + * Copyright (C) 2021 Darling developers 5 + * 6 + * Darling is free software: you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation, either version 3 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * Darling is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + 20 + #include <darlingserver/message.hpp> 21 + #include <unistd.h> 22 + #include <cstdlib> 23 + #include <stdexcept> 24 + #include <new> 25 + #include <mutex> 26 + 27 + DarlingServer::Address::Address() { 28 + _address.sun_family = AF_UNIX; 29 + memset(_address.sun_path, 0, sizeof(_address.sun_path)); 30 + _size = sizeof(_address); 31 + }; 32 + 33 + DarlingServer::Address::Address(const struct sockaddr_un& rawAddress, size_t addressLength) { 34 + _address = rawAddress; 35 + _size = addressLength; 36 + }; 37 + 38 + struct sockaddr_un& DarlingServer::Address::raw() { 39 + return _address; 40 + }; 41 + 42 + const struct sockaddr_un& DarlingServer::Address::raw() const { 43 + return _address; 44 + }; 45 + 46 + size_t DarlingServer::Address::rawSize() const { 47 + return _size; 48 + }; 49 + 50 + void DarlingServer::Address::setRawSize(size_t newRawSize) { 51 + _size = newRawSize; 52 + }; 53 + 54 + DarlingServer::Message::Message(size_t bufferSpace, size_t descriptorSpace) { 55 + size_t controlLen = CMSG_SPACE(sizeof(struct ucred)) + (descriptorSpace > 0 ? CMSG_SPACE(sizeof(int) * descriptorSpace) : 0); 56 + _controlHeader = static_cast<decltype(_controlHeader)>(malloc(controlLen)); 57 + 58 + if (!_controlHeader) { 59 + throw std::bad_alloc(); 60 + } 61 + 62 + _controlHeader->cmsg_len = CMSG_LEN(sizeof(struct ucred)); 63 + _controlHeader->cmsg_level = SOL_SOCKET; 64 + _controlHeader->cmsg_type = SCM_CREDENTIALS; 65 + struct ucred ourCreds; 66 + ourCreds.pid = getpid(); 67 + ourCreds.uid = getuid(); 68 + ourCreds.gid = getgid(); 69 + // the cmsg man page says memcpy should be used with CMSG_DATA instead of direct pointer access 70 + memcpy(CMSG_DATA(_controlHeader), &ourCreds, sizeof(ourCreds)); 71 + 72 + if (descriptorSpace > 0) { 73 + auto fdHeader = reinterpret_cast<decltype(_controlHeader)>(reinterpret_cast<char*>(_controlHeader) + CMSG_SPACE(sizeof(struct ucred))); 74 + fdHeader->cmsg_len = CMSG_LEN(sizeof(int) * descriptorSpace); 75 + fdHeader->cmsg_level = SOL_SOCKET; 76 + fdHeader->cmsg_type = SCM_RIGHTS; 77 + for (size_t i = 0; i < descriptorSpace; ++i) { 78 + const int fd = -1; 79 + memcpy(CMSG_DATA(fdHeader) + (sizeof(int) * i), &fd, sizeof(int)); 80 + } 81 + } 82 + 83 + _buffer.resize(bufferSpace); 84 + 85 + _header.msg_name = &_socketAddress.raw(); 86 + _header.msg_namelen = _socketAddress.rawSize(); 87 + _header.msg_iov = &_dataDescriptor; 88 + _header.msg_iovlen = 1; 89 + _header.msg_control = _controlHeader; 90 + _header.msg_controllen = controlLen; 91 + _header.msg_flags = 0; 92 + 93 + _dataDescriptor.iov_base = _buffer.data(); 94 + _dataDescriptor.iov_len = _buffer.capacity(); 95 + }; 96 + 97 + void DarlingServer::Message::_initWithOther(Message&& other) { 98 + _buffer = std::move(other._buffer); 99 + _socketAddress = std::move(other._socketAddress); 100 + _controlHeader = std::move(other._controlHeader); 101 + 102 + other._controlHeader = nullptr; 103 + 104 + _header = std::move(other._header); 105 + _header.msg_name = &_socketAddress.raw(); 106 + _header.msg_iov = &_dataDescriptor; 107 + 108 + _dataDescriptor.iov_base = _buffer.data(); 109 + _dataDescriptor.iov_len = _buffer.capacity(); 110 + }; 111 + 112 + void DarlingServer::Message::_cleanupSelf() { 113 + if (_controlHeader) { 114 + auto fdHeader = _descriptorHeader(); 115 + if (fdHeader) { 116 + for (size_t i = 0; i < _descriptorSpace(); ++i) { 117 + int fd = -1; 118 + memcpy(&fd, CMSG_DATA(fdHeader) + (sizeof(int) * i), sizeof(int)); 119 + if (fd != -1) { 120 + close(fd); 121 + } 122 + } 123 + } 124 + free(_controlHeader); 125 + _controlHeader = nullptr; 126 + } 127 + }; 128 + 129 + struct cmsghdr* DarlingServer::Message::_credentialsHeader() { 130 + return const_cast<struct cmsghdr*>(const_cast<const Message*>(this)->_credentialsHeader()); 131 + }; 132 + 133 + const struct cmsghdr* DarlingServer::Message::_credentialsHeader() const { 134 + const struct cmsghdr* hdr = CMSG_FIRSTHDR(&_header); 135 + 136 + while (hdr) { 137 + if (hdr->cmsg_level == SOL_SOCKET && hdr->cmsg_type == SCM_CREDENTIALS) { 138 + return hdr; 139 + } 140 + 141 + hdr = CMSG_NXTHDR(const_cast<struct msghdr*>(&_header), const_cast<struct cmsghdr*>(hdr)); 142 + } 143 + 144 + return nullptr; 145 + }; 146 + 147 + struct cmsghdr* DarlingServer::Message::_descriptorHeader() { 148 + return const_cast<struct cmsghdr*>(const_cast<const Message*>(this)->_descriptorHeader()); 149 + }; 150 + 151 + const struct cmsghdr* DarlingServer::Message::_descriptorHeader() const { 152 + const struct cmsghdr* hdr = CMSG_FIRSTHDR(&_header); 153 + 154 + while (hdr) { 155 + if (hdr->cmsg_level == SOL_SOCKET && hdr->cmsg_type == SCM_RIGHTS) { 156 + return hdr; 157 + } 158 + 159 + hdr = CMSG_NXTHDR(const_cast<struct msghdr*>(&_header), const_cast<struct cmsghdr*>(hdr)); 160 + } 161 + 162 + return nullptr; 163 + }; 164 + 165 + size_t DarlingServer::Message::_descriptorSpace() const { 166 + auto hdr = _descriptorHeader(); 167 + 168 + if (!hdr) { 169 + return 0; 170 + } 171 + 172 + return (hdr->cmsg_len - (reinterpret_cast<uintptr_t>(CMSG_DATA(hdr)) - reinterpret_cast<uintptr_t>(hdr))) / sizeof(int); 173 + }; 174 + 175 + DarlingServer::Message::~Message() { 176 + _cleanupSelf(); 177 + }; 178 + 179 + DarlingServer::Message::Message(Message&& other) { 180 + _initWithOther(std::move(other)); 181 + }; 182 + 183 + DarlingServer::Message& DarlingServer::Message::operator=(Message&& other) { 184 + _cleanupSelf(); 185 + _initWithOther(std::move(other)); 186 + return *this; 187 + }; 188 + 189 + struct msghdr& DarlingServer::Message::rawHeader() { 190 + return _header; 191 + }; 192 + 193 + const struct msghdr& DarlingServer::Message::rawHeader() const { 194 + return _header; 195 + }; 196 + 197 + std::vector<uint8_t>& DarlingServer::Message::data() { 198 + return _buffer; 199 + }; 200 + 201 + const std::vector<uint8_t>& DarlingServer::Message::data() const { 202 + return _buffer; 203 + }; 204 + 205 + DarlingServer::Address DarlingServer::Message::address() const { 206 + return _socketAddress; 207 + }; 208 + 209 + void DarlingServer::Message::setAddress(Address address) { 210 + _socketAddress = address; 211 + _header.msg_namelen = _socketAddress.rawSize(); 212 + }; 213 + 214 + std::vector<int> DarlingServer::Message::descriptors() const { 215 + std::vector<int> descriptors; 216 + auto fdHeader = _descriptorHeader(); 217 + 218 + if (fdHeader) { 219 + for (size_t i = 0; i < _descriptorSpace(); ++i) { 220 + int fd = -1; 221 + memcpy(&fd, CMSG_DATA(fdHeader) + (sizeof(int) * i), sizeof(int)); 222 + if (fd != -1) { 223 + descriptors.push_back(fd); 224 + } 225 + } 226 + } 227 + 228 + return descriptors; 229 + }; 230 + 231 + void DarlingServer::Message::pushDescriptor(int descriptor) { 232 + if (auto fdHeader = _descriptorHeader()) { 233 + for (size_t i = 0; i < _descriptorSpace(); ++i) { 234 + int fd = -1; 235 + memcpy(&fd, CMSG_DATA(fdHeader) + (sizeof(int) * i), sizeof(int)); 236 + if (fd != -1) { 237 + continue; 238 + } 239 + 240 + memcpy(CMSG_DATA(fdHeader) + (sizeof(int) * i), &descriptor, sizeof(int)); 241 + return; 242 + } 243 + } 244 + 245 + auto oldSpace = _descriptorSpace(); 246 + _ensureDescriptorHeader(oldSpace + 1); 247 + memcpy(CMSG_DATA(_descriptorHeader()) + (sizeof(int) * oldSpace), &descriptor, sizeof(int)); 248 + }; 249 + 250 + int DarlingServer::Message::extractDescriptor(int descriptor) { 251 + if (auto fdHeader = _descriptorHeader()) { 252 + for (size_t i = 0; i < _descriptorSpace(); ++i) { 253 + int fd = -1; 254 + memcpy(&fd, CMSG_DATA(fdHeader) + (sizeof(int) * i), sizeof(int)); 255 + if (fd != descriptor) { 256 + continue; 257 + } 258 + 259 + fd = -1; 260 + memcpy(CMSG_DATA(fdHeader) + (sizeof(int) * i), &fd, sizeof(int)); 261 + return descriptor; 262 + } 263 + } 264 + 265 + return -1; 266 + }; 267 + 268 + int DarlingServer::Message::extractDescriptorAtIndex(size_t index) { 269 + if (auto fdHeader = _descriptorHeader()) { 270 + for (size_t i = 0, presentIndex = 0; i < _descriptorSpace(); ++i) { 271 + int fd = -1; 272 + memcpy(&fd, CMSG_DATA(fdHeader) + (sizeof(int) * i), sizeof(int)); 273 + if (fd == -1) { 274 + continue; 275 + } 276 + 277 + if (presentIndex++ != index) { 278 + continue; 279 + } 280 + 281 + int tmp = -1; 282 + memcpy(CMSG_DATA(fdHeader) + (sizeof(int) * i), &tmp, sizeof(int)); 283 + return fd; 284 + } 285 + } 286 + 287 + return -1; 288 + }; 289 + 290 + void DarlingServer::Message::replaceDescriptors(const std::vector<int>& newDescriptors) { 291 + _ensureDescriptorHeader(newDescriptors.size()); 292 + memcpy(CMSG_DATA(_descriptorHeader()), newDescriptors.data(), sizeof(int) * newDescriptors.size()); 293 + }; 294 + 295 + bool DarlingServer::Message::copyCredentialsOut(struct ucred& outputCredentials) const { 296 + auto credHeader = _credentialsHeader(); 297 + 298 + if (credHeader) { 299 + memcpy(&outputCredentials, CMSG_DATA(credHeader), sizeof(outputCredentials)); 300 + return true; 301 + } else { 302 + return false; 303 + } 304 + }; 305 + 306 + void DarlingServer::Message::copyCredentialsIn(const struct ucred& inputCredentials) { 307 + _ensureCredentialsHeader(); 308 + memcpy(CMSG_DATA(_credentialsHeader()), &inputCredentials, sizeof(inputCredentials)); 309 + }; 310 + 311 + void DarlingServer::Message::_ensureCredentialsHeader() { 312 + if (_credentialsHeader()) { 313 + return; 314 + } 315 + 316 + auto fdHeader = _descriptorHeader(); 317 + auto descSpace = _descriptorSpace(); 318 + size_t controlLen = CMSG_SPACE(sizeof(struct ucred)) + (descSpace > 0 ? CMSG_SPACE(sizeof(int) * descSpace) : 0); 319 + auto tmp = static_cast<decltype(_controlHeader)>(malloc(controlLen)); 320 + auto old = _controlHeader; 321 + struct cmsghdr* credHeader = nullptr; 322 + 323 + if (!tmp) { 324 + throw std::bad_alloc(); 325 + } 326 + 327 + _controlHeader = tmp; 328 + _header.msg_control = _controlHeader; 329 + 330 + credHeader = _controlHeader; 331 + 332 + if (descSpace > 0) { 333 + auto newFdHeader = reinterpret_cast<decltype(_controlHeader)>(reinterpret_cast<char*>(_controlHeader) + CMSG_SPACE(sizeof(struct ucred))); 334 + memcpy(newFdHeader, fdHeader, CMSG_SPACE(sizeof(int) * descSpace)); 335 + } 336 + 337 + free(old); 338 + 339 + _header.msg_controllen = controlLen; 340 + 341 + credHeader->cmsg_len = CMSG_LEN(sizeof(struct ucred)); 342 + credHeader->cmsg_level = SOL_SOCKET; 343 + credHeader->cmsg_type = SCM_CREDENTIALS; 344 + 345 + struct ucred creds; 346 + creds.pid = getpid(); 347 + creds.uid = getuid(); 348 + creds.gid = getgid(); 349 + memcpy(CMSG_DATA(credHeader), &creds, sizeof(struct ucred)); 350 + }; 351 + 352 + void DarlingServer::Message::_ensureDescriptorHeader(size_t newSpace) { 353 + if (_descriptorSpace() == newSpace) { 354 + return; 355 + } 356 + 357 + auto credHeader = _credentialsHeader(); 358 + size_t oldSpace = _descriptorSpace(); 359 + size_t controlLen = (credHeader ? CMSG_SPACE(sizeof(struct ucred)) : 0) + (newSpace == 0 ? 0 : CMSG_SPACE(sizeof(int) * newSpace)); 360 + 361 + if (controlLen == 0) { 362 + free(_controlHeader); 363 + _controlHeader = nullptr; 364 + _header.msg_control = nullptr; 365 + _header.msg_controllen = 0; 366 + return; 367 + } 368 + 369 + auto tmp = static_cast<decltype(_controlHeader)>(malloc(controlLen)); 370 + auto old = _controlHeader; 371 + struct cmsghdr* fdHeader = nullptr; 372 + 373 + if (!tmp) { 374 + throw std::bad_alloc(); 375 + } 376 + 377 + _controlHeader = tmp; 378 + _header.msg_control = _controlHeader; 379 + 380 + if (credHeader) { 381 + memcpy(_controlHeader, credHeader, CMSG_SPACE(sizeof(struct ucred))); 382 + fdHeader = reinterpret_cast<decltype(_controlHeader)>(reinterpret_cast<char*>(_controlHeader) + CMSG_SPACE(sizeof(struct ucred))); 383 + } else { 384 + fdHeader = _controlHeader; 385 + } 386 + 387 + free(old); 388 + 389 + _header.msg_controllen = controlLen; 390 + 391 + fdHeader->cmsg_len = CMSG_LEN(sizeof(int) * newSpace); 392 + fdHeader->cmsg_level = SOL_SOCKET; 393 + fdHeader->cmsg_type = SCM_RIGHTS; 394 + 395 + for (size_t i = oldSpace; i < newSpace; ++i) { 396 + int fd = -1; 397 + memcpy(CMSG_DATA(fdHeader) + (sizeof(int) * i), &fd, sizeof(int)); 398 + } 399 + }; 400 + 401 + pid_t DarlingServer::Message::pid() const { 402 + struct ucred creds; 403 + 404 + if (copyCredentialsOut(creds)) { 405 + return creds.pid; 406 + } else { 407 + return -1; 408 + } 409 + }; 410 + 411 + void DarlingServer::Message::setPID(pid_t pid) { 412 + _ensureCredentialsHeader(); 413 + struct ucred creds; 414 + copyCredentialsOut(creds); 415 + creds.pid = pid; 416 + copyCredentialsIn(creds); 417 + }; 418 + 419 + uid_t DarlingServer::Message::uid() const { 420 + struct ucred creds; 421 + 422 + if (copyCredentialsOut(creds)) { 423 + return creds.uid; 424 + } else { 425 + return -1; 426 + } 427 + }; 428 + 429 + void DarlingServer::Message::setUID(uid_t uid) { 430 + _ensureCredentialsHeader(); 431 + struct ucred creds; 432 + copyCredentialsOut(creds); 433 + creds.uid = uid; 434 + copyCredentialsIn(creds); 435 + }; 436 + 437 + gid_t DarlingServer::Message::gid() const { 438 + struct ucred creds; 439 + 440 + if (copyCredentialsOut(creds)) { 441 + return creds.gid; 442 + } else { 443 + return -1; 444 + } 445 + }; 446 + 447 + void DarlingServer::Message::setGID(gid_t gid) { 448 + _ensureCredentialsHeader(); 449 + struct ucred creds; 450 + copyCredentialsOut(creds); 451 + creds.gid = gid; 452 + copyCredentialsIn(creds); 453 + }; 454 + 455 + void DarlingServer::MessageQueue::push(Message&& message) { 456 + std::scoped_lock lock(_lock); 457 + _messages.push_back(std::move(message)); 458 + }; 459 + 460 + std::optional<DarlingServer::Message> DarlingServer::MessageQueue::pop() { 461 + std::scoped_lock lock(_lock); 462 + if (_messages.size() > 0) { 463 + Message message = std::move(_messages.front()); 464 + _messages.pop_front(); 465 + return message; 466 + } else { 467 + return std::nullopt; 468 + } 469 + }; 470 + 471 + void DarlingServer::MessageQueue::sendMany(int socket) { 472 + std::scoped_lock lock(_lock); 473 + struct mmsghdr mmsgs[16]; 474 + size_t len = 0; 475 + int ret = 0; 476 + 477 + while (ret >= 0) { 478 + len = 0; 479 + for (size_t i = 0; i < _messages.size() && i < sizeof(mmsgs) / sizeof(*mmsgs); ++i) { 480 + mmsgs[i].msg_hdr = _messages[i].rawHeader(); 481 + mmsgs[i].msg_len = 0; 482 + ++len; 483 + } 484 + 485 + if (len == 0) { 486 + break; 487 + } 488 + 489 + ret = sendmmsg(socket, mmsgs, len, MSG_DONTWAIT); 490 + 491 + if (ret < 0) { 492 + if (errno == EAGAIN) { 493 + break; 494 + } else if (errno == EINTR) { 495 + ret = 0; 496 + } else { 497 + throw std::system_error(errno, std::generic_category(), "Failed to send messages through socket"); 498 + } 499 + } 500 + 501 + for (size_t i = 0; i < ret; ++i) { 502 + _messages.pop_front(); 503 + } 504 + } 505 + }; 506 + 507 + void DarlingServer::MessageQueue::receiveMany(int socket) { 508 + std::scoped_lock lock(_lock); 509 + struct mmsghdr mmsgs[16]; 510 + int ret = 0; 511 + 512 + while (ret >= 0) { 513 + std::array<Message, sizeof(mmsgs) / sizeof(*mmsgs)> messages; 514 + 515 + for (size_t i = 0; i < messages.size(); ++i) { 516 + mmsgs[i].msg_hdr = messages[i].rawHeader(); 517 + mmsgs[i].msg_len = 0; 518 + } 519 + 520 + ret = recvmmsg(socket, mmsgs, sizeof(mmsgs) / sizeof(*mmsgs), MSG_CMSG_CLOEXEC | MSG_DONTWAIT, nullptr); 521 + 522 + if (ret < 0) { 523 + if (errno == EAGAIN) { 524 + break; 525 + } else if (errno == EINTR) { 526 + ret = 0; 527 + } else { 528 + throw std::system_error(errno, std::generic_category(), "Failed to receive messages through socket"); 529 + } 530 + } 531 + 532 + for (size_t i = 0; i < ret; ++i) { 533 + messages[i].rawHeader() = mmsgs[i].msg_hdr; 534 + messages[i].data().resize(mmsgs[i].msg_len); 535 + messages[i].setAddress(Address(*(const struct sockaddr_un*)mmsgs[i].msg_hdr.msg_name, mmsgs[i].msg_hdr.msg_namelen)); 536 + _messages.push_back(std::move(messages[i])); 537 + } 538 + } 539 + };
+83
src/darlingserver/src/process.cpp
··· 1 + /** 2 + * This file is part of Darling. 3 + * 4 + * Copyright (C) 2021 Darling developers 5 + * 6 + * Darling is free software: you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation, either version 3 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * Darling is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + 20 + #include <darlingserver/process.hpp> 21 + #include <darlingserver/registry.hpp> 22 + #include <sys/syscall.h> 23 + #include <unistd.h> 24 + 25 + DarlingServer::Process::Process(ID id, NSID nsid): 26 + _pid(id), 27 + _nspid(nsid) 28 + { 29 + _pidfd = syscall(SYS_pidfd_open, _pid, 0); 30 + if (_pidfd < 0) { 31 + throw std::system_error(errno, std::generic_category(), "Failed to open pidfd for process"); 32 + } 33 + }; 34 + 35 + DarlingServer::Process::~Process() { 36 + close(_pidfd); 37 + 38 + _unregisterThreads(); 39 + }; 40 + 41 + void DarlingServer::Process::_unregisterThreads() { 42 + std::unique_lock lock(_rwlock); 43 + while (!_threads.empty()) { 44 + auto thread = _threads.front().lock(); 45 + lock.unlock(); 46 + if (thread) { 47 + thread->_process = std::weak_ptr<Process>(); 48 + threadRegistry().unregisterEntry(thread); 49 + } 50 + lock.lock(); 51 + } 52 + }; 53 + 54 + DarlingServer::Process::ID DarlingServer::Process::id() const { 55 + return _pid; 56 + }; 57 + 58 + DarlingServer::Process::NSID DarlingServer::Process::nsid() const { 59 + return _nspid; 60 + }; 61 + 62 + std::vector<std::shared_ptr<DarlingServer::Thread>> DarlingServer::Process::threads() const { 63 + std::vector<std::shared_ptr<DarlingServer::Thread>> result; 64 + std::shared_lock lock(_rwlock); 65 + 66 + for (auto& maybeThread: _threads) { 67 + if (auto thread = maybeThread.lock()) { 68 + result.push_back(thread); 69 + } 70 + } 71 + 72 + return result; 73 + }; 74 + 75 + std::string DarlingServer::Process::vchrootPath() const { 76 + std::shared_lock lock(_rwlock); 77 + return _vchrootPath; 78 + }; 79 + 80 + void DarlingServer::Process::setVchrootPath(std::string path) { 81 + std::unique_lock lock(_rwlock); 82 + _vchrootPath = path; 83 + };
+30
src/darlingserver/src/registry.cpp
··· 1 + /** 2 + * This file is part of Darling. 3 + * 4 + * Copyright (C) 2021 Darling developers 5 + * 6 + * Darling is free software: you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation, either version 3 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * Darling is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + 20 + #include <darlingserver/registry.hpp> 21 + 22 + DarlingServer::Registry<DarlingServer::Process>& DarlingServer::processRegistry() { 23 + static Registry<Process> registry; 24 + return registry; 25 + }; 26 + 27 + DarlingServer::Registry<DarlingServer::Thread>& DarlingServer::threadRegistry() { 28 + static Registry<Thread> registry; 29 + return registry; 30 + };
+152
src/darlingserver/src/server.cpp
··· 1 + /** 2 + * This file is part of Darling. 3 + * 4 + * Copyright (C) 2021 Darling developers 5 + * 6 + * Darling is free software: you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation, either version 3 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * Darling is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + 20 + #include <darlingserver/server.hpp> 21 + #include <sys/socket.h> 22 + #include <stdexcept> 23 + #include <errno.h> 24 + #include <cstring> 25 + #include <unistd.h> 26 + #include <sys/un.h> 27 + #include <sys/epoll.h> 28 + #include <fcntl.h> 29 + #include <system_error> 30 + #include <thread> 31 + #include <array> 32 + #include <darlingserver/registry.hpp> 33 + 34 + static DarlingServer::Server* sharedInstancePointer = nullptr; 35 + 36 + DarlingServer::Server::Server(std::string prefix): 37 + _prefix(prefix), 38 + _socketPath(_prefix + "/var/run/darlingserver.sock"), 39 + _workQueue(std::bind(&Server::_worker, this, std::placeholders::_1)) 40 + { 41 + sharedInstancePointer = this; 42 + 43 + // remove the old socket (if it exists) 44 + unlink(_socketPath.c_str()); 45 + 46 + // create the socket 47 + _listenerSocket = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); 48 + if (_listenerSocket < 0) { 49 + throw std::system_error(errno, std::generic_category(), "Failed to create socket"); 50 + } 51 + 52 + int passCred = 1; 53 + if (setsockopt(_listenerSocket, SOL_SOCKET, SO_PASSCRED, &passCred, sizeof(passCred)) < 0) { 54 + throw std::system_error(errno, std::generic_category(), "Failed to set SO_PASSCRED on socket"); 55 + } 56 + 57 + struct sockaddr_un addr; 58 + addr.sun_family = AF_UNIX; 59 + addr.sun_path[sizeof(addr.sun_path) - 1] = '\0'; 60 + strncpy(addr.sun_path, _socketPath.c_str(), sizeof(addr.sun_path) - 1); 61 + 62 + if (bind(_listenerSocket, (struct sockaddr*)&addr, sizeof(addr)) != 0) { 63 + throw std::system_error(errno, std::generic_category(), "Failed to bind socket"); 64 + } 65 + 66 + _epollFD = epoll_create1(EPOLL_CLOEXEC); 67 + if (_epollFD < 0) { 68 + throw std::system_error(errno, std::generic_category(), "Failed to create epoll context"); 69 + } 70 + 71 + struct epoll_event settings; 72 + settings.data.ptr = this; 73 + settings.events = EPOLLIN | EPOLLOUT; 74 + 75 + if (epoll_ctl(_epollFD, EPOLL_CTL_ADD, _listenerSocket, &settings) < 0) { 76 + throw std::system_error(errno, std::generic_category(), "Failed to add listener socket to epoll context"); 77 + } 78 + }; 79 + 80 + DarlingServer::Server::~Server() { 81 + close(_epollFD); 82 + close(_listenerSocket); 83 + unlink(_socketPath.c_str()); 84 + }; 85 + 86 + void DarlingServer::Server::start() { 87 + while (true) { 88 + struct epoll_event events[16]; 89 + int ret = epoll_wait(_epollFD, events, 16, -1); 90 + 91 + if (ret < 0) { 92 + if (errno == EINTR) { 93 + continue; 94 + } 95 + 96 + throw std::system_error(errno, std::generic_category(), "Failed to wait on epoll context"); 97 + } 98 + 99 + for (size_t i = 0; i < ret; ++i) { 100 + struct epoll_event* event = &events[i]; 101 + 102 + if (event->data.ptr == this) { 103 + if (event->events & EPOLLIN) { 104 + _inbox.receiveMany(_listenerSocket); 105 + 106 + // TODO: receive messages directly onto the work queue 107 + while (auto msg = _inbox.pop()) { 108 + _workQueue.push(std::move(msg.value())); 109 + } 110 + } 111 + 112 + if (event->events & EPOLLOUT) { 113 + _outbox.sendMany(_listenerSocket); 114 + } 115 + } else if (event->events & EPOLLIN) { 116 + std::shared_ptr<Process>& process = *reinterpret_cast<std::shared_ptr<Process>*>(event->data.ptr); 117 + 118 + if (epoll_ctl(_epollFD, EPOLL_CTL_DEL, process->_pidfd, NULL) < 0) { 119 + throw std::system_error(errno, std::generic_category(), "Failed to remove process handle from epoll context"); 120 + } 121 + 122 + process->_unregisterThreads(); 123 + processRegistry().unregisterEntry(process); 124 + 125 + delete &process; 126 + } 127 + } 128 + } 129 + }; 130 + 131 + void DarlingServer::Server::monitorProcess(std::shared_ptr<Process> process) { 132 + struct epoll_event settings; 133 + settings.data.ptr = new std::shared_ptr<Process>(process); 134 + settings.events = EPOLLIN; 135 + 136 + if (epoll_ctl(_epollFD, EPOLL_CTL_ADD, process->_pidfd, &settings) < 0) { 137 + throw std::system_error(errno, std::generic_category(), "Failed to add process descriptor to epoll context"); 138 + } 139 + }; 140 + 141 + DarlingServer::Server& DarlingServer::Server::sharedInstance() { 142 + return *sharedInstancePointer; 143 + }; 144 + 145 + std::string DarlingServer::Server::prefix() const { 146 + return _prefix; 147 + }; 148 + 149 + void DarlingServer::Server::_worker(DarlingServer::Message message) { 150 + auto call = DarlingServer::Call::callFromMessage(std::move(message), _outbox); 151 + call->processCall(); 152 + };
+122
src/darlingserver/src/thread.cpp
··· 1 + /** 2 + * This file is part of Darling. 3 + * 4 + * Copyright (C) 2021 Darling developers 5 + * 6 + * Darling is free software: you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation, either version 3 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * Darling is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + 20 + #include <darlingserver/thread.hpp> 21 + #include <darlingserver/process.hpp> 22 + #include <filesystem> 23 + #include <fstream> 24 + 25 + DarlingServer::Thread::Thread(std::shared_ptr<Process> process, NSID nsid): 26 + _nstid(nsid), 27 + _process(process) 28 + { 29 + _tid = -1; 30 + 31 + for (const auto& entry: std::filesystem::directory_iterator("/proc/" + std::to_string(process->id()) + "/task")) { 32 + std::ifstream statusFile(entry.path() / "status"); 33 + std::string line; 34 + 35 + while (std::getline(statusFile, line)) { 36 + if (line.substr(0, sizeof("NSpid") - 1) == "NSpid") { 37 + auto pos = line.find_last_of('\t'); 38 + std::string id; 39 + 40 + if (pos != line.npos) { 41 + id = line.substr(pos + 1); 42 + } 43 + 44 + if (id.empty()) { 45 + throw std::runtime_error("Failed to parse thread ID"); 46 + } 47 + 48 + if (std::stoi(id) != _nstid) { 49 + continue; 50 + } 51 + 52 + _tid = std::stoi(entry.path().filename().string()); 53 + 54 + break; 55 + } 56 + } 57 + } 58 + 59 + if (_tid == -1) { 60 + throw std::runtime_error("Failed to find thread ID within darlingserver's namespace"); 61 + } 62 + }; 63 + 64 + void DarlingServer::Thread::registerWithProcess() { 65 + auto process = _process.lock(); 66 + std::unique_lock lock(process->_rwlock); 67 + process->_threads.push_back(shared_from_this()); 68 + }; 69 + 70 + DarlingServer::Thread::~Thread() noexcept(false) { 71 + auto process = _process.lock(); 72 + if (!process) { 73 + // the process is unregistering us 74 + return; 75 + } 76 + 77 + std::unique_lock lock(process->_rwlock); 78 + auto it = process->_threads.begin(); 79 + while (it != process->_threads.end()) { 80 + if (auto thread = it->lock()) { 81 + if (thread.get() == this) { 82 + break; 83 + } 84 + } 85 + } 86 + if (it == process->_threads.end()) { 87 + throw std::runtime_error("Thread was not registered with Process"); 88 + } 89 + process->_threads.erase(it); 90 + }; 91 + 92 + DarlingServer::Thread::ID DarlingServer::Thread::id() const { 93 + return _tid; 94 + }; 95 + 96 + DarlingServer::Thread::NSID DarlingServer::Thread::nsid() const { 97 + return _nstid; 98 + }; 99 + 100 + std::shared_ptr<DarlingServer::Process> DarlingServer::Thread::process() const { 101 + return _process.lock(); 102 + }; 103 + 104 + std::shared_ptr<DarlingServer::Call> DarlingServer::Thread::pendingCall() const { 105 + std::shared_lock lock(_rwlock); 106 + return _pendingCall; 107 + }; 108 + 109 + void DarlingServer::Thread::setPendingCall(std::shared_ptr<Call> newPendingCall) { 110 + std::unique_lock lock(_rwlock); 111 + _pendingCall = newPendingCall; 112 + }; 113 + 114 + DarlingServer::Address DarlingServer::Thread::address() const { 115 + std::shared_lock lock(_rwlock); 116 + return _address; 117 + }; 118 + 119 + void DarlingServer::Thread::setAddress(Address address) { 120 + std::unique_lock lock(_rwlock); 121 + _address = address; 122 + };
+1 -1
src/kernel/emulation/linux/bsdthread/workq_kernreturn.c
··· 19 19 #include <pthread/priority_private.h> 20 20 21 21 // much easier to include than libpthread's `internal.h` 22 - #include "../../../../libelfloader/native/dthreads.h" 22 + #include "../../../../startup/mldr/elfcalls/dthreads.h" 23 23 24 24 #define WQ_MAX_THREADS 64 25 25
+6 -3
src/kernel/emulation/linux/elfcalls_wrapper.c
··· 1 1 #include "elfcalls_wrapper.h" 2 2 #include <elfcalls.h> 3 3 #include <dlfcn.h> 4 + #include "simple.h" 4 5 5 - static struct elf_calls* _elfcalls; 6 + extern struct elf_calls* _elfcalls; 6 7 7 8 struct elf_calls* elfcalls(void) 8 9 { 9 10 if (!_elfcalls) 10 11 { 11 - void* module = dlopen("/usr/lib/darling/libelfloader.dylib", RTLD_NOW); 12 + //void* module = dlopen("/usr/lib/darling/libelfloader.dylib", RTLD_NOW); 12 13 // if (!module) 13 14 // __simple_printf("Load error: %s\n", dlerror()); 14 15 15 16 // struct elf_calls** ptr = (struct elf_calls**) dlsym(module, "_elfcalls"); 16 17 // __simple_printf("_elfcalls is at %p\n", ptr); 17 18 // __simple_printf("*_elfcalls = %p\n", *ptr); 18 - _elfcalls = *(struct elf_calls**) dlsym(module, "_elfcalls"); 19 + //_elfcalls = *(struct elf_calls**) dlsym(module, "_elfcalls"); 20 + __simple_printf("elfcalls not found?\n"); 21 + __simple_abort(); 19 22 } 20 23 return _elfcalls; 21 24 }
+13 -1
src/kernel/emulation/linux/mach/lkm.c
··· 9 9 #include "../../../libsyscall/wrappers/_libkernel_init.h" 10 10 #include "../simple.h" 11 11 #include "../misc/ioctl.h" 12 + #include <elfcalls.h> 12 13 13 14 extern int sys_open(const char*, int, int); 14 15 extern int close_internal(int); ··· 22 23 23 24 static int driver_fd = -1; 24 25 26 + VISIBLE 27 + struct elf_calls* _elfcalls; 28 + 25 29 void mach_driver_init(const char** applep) 26 30 { 31 + // DARLINGSERVER/MLDR TESTING 32 + __simple_printf("We're being initialized...\n"); 33 + __builtin_unreachable(); 34 + 27 35 #ifdef VARIANT_DYLD 28 36 if (applep != NULL) 29 37 { ··· 33 41 if (strncmp(applep[i], "kernfd=", 7) == 0) 34 42 { 35 43 driver_fd = __simple_atoi(applep[i] + 7, NULL); 36 - break; 44 + } 45 + if (strncmp(applep[i], "elf_calls=", 10) == 0) 46 + { 47 + uintptr_t table = (uintptr_t) __simple_atoi16(applep[i] + 10, NULL); 48 + _elfcalls = (struct elf_calls*) table; 37 49 } 38 50 } 39 51 }
+7
src/kernel/emulation/linux/simple.c
··· 5 5 #include <linux-syscalls/linux.h> 6 6 #include <lkm/api.h> 7 7 #include "mach/lkm.h" 8 + #include "signal/kill.h" 9 + #include <sys/signal.h> 8 10 9 11 extern char* memchr(char* buf, int c, __SIZE_TYPE__ n); 10 12 ··· 569 571 return out; 570 572 } 571 573 574 + __attribute__ ((visibility ("default"))) 575 + void __simple_abort(void) { 576 + sys_kill(0, SIGABRT, 1); 577 + __builtin_unreachable(); 578 + };
+3
src/kernel/emulation/linux/simple.h
··· 34 34 void __simple_readline_init(struct simple_readline_buf* buf); 35 35 char* __simple_readline(int fd, struct simple_readline_buf* buf, char* out, int max_out); 36 36 37 + __attribute__((noreturn)) 38 + void __simple_abort(void); 39 + 37 40 #ifdef __cplusplus 38 41 }; 39 42 #endif
+1 -25
src/kernel/libsyscall/wrappers/_libkernel_init.c
··· 33 33 #include "_libkernel_init.h" 34 34 35 35 #ifdef DARLING 36 - #include <elfcalls.h> 37 - 38 36 extern int mach_init(const char** applep); 37 + extern void sigexc_setup(void); 39 38 #else 40 39 extern int mach_init(void); 41 40 #endif ··· 49 48 /* dlsym() funcptr is for legacy support in exc_catcher */ 50 49 void* (*_dlsym)(void*, const char*) __attribute__((visibility("hidden"))); 51 50 52 - #ifdef DARLING 53 - extern int strncmp(const char *s1, const char *s2, __SIZE_TYPE__ n); 54 - extern unsigned long long __simple_atoi16(const char* str, const char** endp); 55 - #endif 56 - 57 51 __attribute__((visibility("hidden"))) 58 52 _libkernel_functions_t _libkernel_functions; 59 - 60 - #ifdef DARLING 61 - __attribute__((visibility("hidden"))) 62 - struct elf_calls* _elfcalls; 63 - #endif 64 53 65 54 void 66 55 __libkernel_init(_libkernel_functions_t fns, ··· 68 57 const char *apple[], 69 58 const struct ProgramVars *vars __attribute__((unused))) 70 59 { 71 - #ifdef DARLING 72 - int i; 73 - #endif 74 - 75 60 _libkernel_functions = fns; 76 61 if (fns->dlsym) { 77 62 _dlsym = fns->dlsym; 78 63 } 79 64 80 65 #ifdef DARLING 81 - for (i = 0; apple[i] != NULL; i++) 82 - { 83 - if (strncmp(apple[i], "elf_calls=", 10) == 0) 84 - { 85 - uintptr_t table = (uintptr_t) __simple_atoi16(apple[i] + 10, NULL); 86 - _elfcalls = (struct elf_calls*) table; 87 - } 88 - } 89 - 90 66 mach_init(apple); 91 67 sigexc_setup(); 92 68 #else
-13
src/libelfloader/CMakeLists.txt
··· 1 - project(libelfloader) 2 - 3 - set(elfloader_sources 4 - loader.c 5 - ) 6 - 7 - set(DYLIB_INSTALL_NAME "/usr/lib/darling/libelfloader.dylib") 8 - add_darling_library(elfloader SHARED ${elfloader_sources}) 9 - target_link_libraries(elfloader system) 10 - make_fat(elfloader) 11 - 12 - install(TARGETS elfloader DESTINATION libexec/darling/usr/lib/darling) 13 -
-35
src/libelfloader/auxvec.h
··· 1 - #ifndef _UAPI_LINUX_AUXVEC_H 2 - #define _UAPI_LINUX_AUXVEC_H 3 - 4 - /* Symbolic values for the entries in the auxiliary table 5 - put on the initial stack */ 6 - #define AT_NULL 0 /* end of vector */ 7 - #define AT_IGNORE 1 /* entry should be ignored */ 8 - #define AT_EXECFD 2 /* file descriptor of program */ 9 - #define AT_PHDR 3 /* program headers for program */ 10 - #define AT_PHENT 4 /* size of program header entry */ 11 - #define AT_PHNUM 5 /* number of program headers */ 12 - #define AT_PAGESZ 6 /* system page size */ 13 - #define AT_BASE 7 /* base address of interpreter */ 14 - #define AT_FLAGS 8 /* flags */ 15 - #define AT_ENTRY 9 /* entry point of program */ 16 - #define AT_NOTELF 10 /* program is not ELF */ 17 - #define AT_UID 11 /* real uid */ 18 - #define AT_EUID 12 /* effective uid */ 19 - #define AT_GID 13 /* real gid */ 20 - #define AT_EGID 14 /* effective gid */ 21 - #define AT_PLATFORM 15 /* string identifying CPU for optimizations */ 22 - #define AT_HWCAP 16 /* arch dependent hints at CPU capabilities */ 23 - #define AT_CLKTCK 17 /* frequency at which times() increments */ 24 - /* AT_* values 18 through 22 are reserved */ 25 - #define AT_SECURE 23 /* secure mode boolean */ 26 - #define AT_BASE_PLATFORM 24 /* string identifying real platform, may 27 - * differ from AT_PLATFORM. */ 28 - #define AT_RANDOM 25 /* address of 16 random bytes */ 29 - #define AT_HWCAP2 26 /* extension of AT_HWCAP */ 30 - 31 - #define AT_EXECFN 31 /* filename of program */ 32 - 33 - 34 - #endif /* _UAPI_LINUX_AUXVEC_H */ 35 -
-440
src/libelfloader/loader.c
··· 1 - /* 2 - * Darling 3 - * Copyright (C) 2017 Lubos Dolezel 4 - * 5 - * This program is free software; you can redistribute it and/or 6 - * modify it under the terms of the GNU General Public License 7 - * as published by the Free Software Foundation; either version 2 8 - * of the License, or (at your option) any later version. 9 - * 10 - * This program is distributed in the hope that it will be useful, 11 - * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 - * GNU General Public License for more details. 14 - * 15 - * You should have received a copy of the GNU General Public License 16 - * along with this program; if not, write to the Free Software 17 - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 - */ 19 - 20 - #include <unistd.h> 21 - #include <sys/mman.h> 22 - #include <sys/types.h> 23 - #include <fcntl.h> 24 - #include <string.h> 25 - #include <stdio.h> 26 - #include <stdlib.h> 27 - #include <stdbool.h> 28 - #include <setjmp.h> 29 - #include <sys/random.h> 30 - #include <elf.h> 31 - #include <darling/emulation/ext/for-libelfloader.h> 32 - #include "auxvec.h" 33 - #include "loader.h" 34 - #include "native/elfcalls.h" 35 - 36 - #ifdef __APPLE__ 37 - extern int getentropy(void* buf, size_t len); 38 - #endif 39 - 40 - #define ElfW(type) _ElfW (Elf, __ELF_NATIVE_CLASS, type) 41 - #define _ElfW(e,w,t) _ElfW_1 (e, w, _##t) 42 - #define _ElfW_1(e,w,t) e##w##t 43 - 44 - #if defined(__x86_64__) || defined(__arm64__) 45 - # define DUMMY_PATH "/usr/libexec/elfloader_dummy64" 46 - # define MY_ELF_CLASS ELFCLASS64 47 - # define __ELF_NATIVE_CLASS 64 48 - #else 49 - # define DUMMY_PATH "/usr/libexec/elfloader_dummy32" 50 - # define MY_ELF_CLASS ELFCLASS32 51 - # define __ELF_NATIVE_CLASS 32 52 - #endif 53 - 54 - #define ELF_MIN_ALIGN 0x1000 55 - #define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_MIN_ALIGN-1)) 56 - #define ELF_PAGEOFFSET(_v) ((_v) & (ELF_MIN_ALIGN-1)) 57 - #define ELF_PAGEALIGN(_v) (((_v) + ELF_MIN_ALIGN - 1) & ~(ELF_MIN_ALIGN - 1)) 58 - 59 - struct loader_context 60 - { 61 - uintptr_t interp_entry; 62 - uintptr_t exec_entry; 63 - uintptr_t exec_phstart; 64 - uintptr_t exec_phentsize; 65 - uintptr_t exec_phnum; 66 - uintptr_t interp_base; 67 - }; 68 - 69 - static void run(const char* path, const char** envp); 70 - static void load(const char* path, struct loader_context* lc, bool isInterp); 71 - static void loader_return(void); 72 - static const char SYSTEM_ROOT[] = "/Volumes/SystemRoot"; 73 - 74 - static jmp_buf jmpbuf; 75 - struct elf_calls* _elfcalls; 76 - 77 - #ifndef TEST 78 - __attribute__((constructor)) 79 - static void runDummy(int argc, const char** argv, const char** envp) 80 - { 81 - _elfcalls = (struct elf_calls*) malloc(sizeof(struct elf_calls)); 82 - run(DUMMY_PATH, envp); 83 - } 84 - #endif 85 - 86 - static unsigned long processEnvVariable(const char* var) 87 - { 88 - if (strncmp(var, "HOME=", 5) == 0) 89 - { 90 - char* home = (char*) malloc(512); 91 - char* symlink_path = (char*) malloc(strlen(var+5) + 10); 92 - 93 - strcpy(symlink_path, var+5); 94 - strcat(symlink_path, "/LinuxHome"); 95 - 96 - int len = readlink(symlink_path, home, 512-1); 97 - free(symlink_path); 98 - 99 - if (len < 0) 100 - { 101 - free(home); 102 - goto out; 103 - } 104 - home[len] = '\0'; 105 - 106 - if (strncmp(home, SYSTEM_ROOT, sizeof(SYSTEM_ROOT) - 1) != 0) 107 - { 108 - free(home); 109 - goto out; 110 - } 111 - 112 - strcpy(home, "HOME="); 113 - memmove(home+5, home + sizeof(SYSTEM_ROOT) - 1, len - sizeof(SYSTEM_ROOT) + 2); 114 - 115 - return (unsigned long) home; 116 - } 117 - out: 118 - return (unsigned long) var; 119 - } 120 - 121 - void run(const char* path, const char** envp) 122 - { 123 - struct loader_context lc; 124 - unsigned long *stack, *stackmem; 125 - int ptrcount, pos = 0; 126 - uint8_t entropy[16]; 127 - char pointer1[20]; 128 - char pointer2[20]; 129 - 130 - load(path, &lc, false); 131 - if (!lc.interp_entry) 132 - return; 133 - 134 - stackmem = (unsigned long*) mmap(NULL, 4096*4, PROT_READ | PROT_WRITE, 135 - MAP_PRIVATE | MAP_ANON, -1, 0); 136 - 137 - if (stackmem == MAP_FAILED) 138 - perror("mmap"); 139 - 140 - stack = (unsigned long*) (((char*) stackmem) + 4096*4 - sizeof(unsigned long)); 141 - 142 - // AT_* 143 - ptrcount = 10*2; 144 - ptrcount++; // argc 145 - ptrcount += 4; // argv 146 - 147 - // environ 148 - for (int i = 0; envp[i] != NULL; i++) 149 - ptrcount++; 150 - ptrcount++; 151 - 152 - // Ensure 16-byte alignment 153 - if (ptrcount % 2 == 0) 154 - ptrcount++; 155 - 156 - stack -= ptrcount; 157 - 158 - stack[pos++] = 3; // argc 159 - stack[pos++] = (unsigned long) "elfloader_dummy"; 160 - 161 - snprintf(pointer1, sizeof(pointer1), "%lx", (unsigned long) _elfcalls); 162 - stack[pos++] = (unsigned long) pointer1; 163 - 164 - snprintf(pointer2, sizeof(pointer2), "%lx", (unsigned long) loader_return); 165 - stack[pos++] = (unsigned long) pointer2; 166 - stack[pos++] = 0; 167 - 168 - 169 - for (int i = 0; envp[i] != NULL; i++) 170 - stack[pos++] = processEnvVariable(envp[i]); 171 - 172 - #ifdef __linux__ // For testing 173 - getrandom(entropy, sizeof(entropy), 0); 174 - #else // Darwin 175 - getentropy(entropy, sizeof(entropy)); 176 - #endif 177 - 178 - stack[pos++] = 0; 179 - 180 - stack[pos++] = AT_PHDR; 181 - stack[pos++] = lc.exec_phstart; 182 - 183 - stack[pos++] = AT_PHENT; 184 - stack[pos++] = lc.exec_phentsize; 185 - 186 - stack[pos++] = AT_PHNUM; 187 - stack[pos++] = lc.exec_phnum; 188 - 189 - stack[pos++] = AT_ENTRY; 190 - stack[pos++] = lc.exec_entry; 191 - 192 - stack[pos++] = AT_BASE; 193 - stack[pos++] = lc.interp_base; 194 - 195 - stack[pos++] = AT_PAGESZ; 196 - stack[pos++] = 4096; 197 - 198 - stack[pos++] = AT_FLAGS; 199 - stack[pos++] = 0; 200 - 201 - stack[pos++] = AT_RANDOM; 202 - stack[pos++] = (unsigned long) entropy; 203 - 204 - stack[pos++] = AT_NULL; 205 - stack[pos++] = 0; 206 - 207 - // TODO: AT_EXECFN? 208 - #ifdef __x86_64__ 209 - # define JUMPX(stack, addr) __asm__ volatile("mov %1, %%rsp; jmpq *%0" :: "m"(addr), "r"(stack) :) 210 - #elif defined(__i386__) 211 - # define JUMPX(stack, addr) __asm__ volatile("mov %1, %%esp; jmp *%0" :: "m"(addr), "r"(stack) :) 212 - #else 213 - # error Unsupported platform! 214 - #endif 215 - 216 - if (!setjmp(jmpbuf)) 217 - JUMPX(stack, lc.interp_entry); 218 - else 219 - { 220 - // puts("Back from loaded binary"); 221 - } 222 - } 223 - 224 - // AT_PHDR -> loadaddr + phoff 225 - // AT_PHENT -> e_phentsize 226 - // AT_PHNUM -> e_phnum 227 - // AT_ENTRY -> exec->e_entry 228 - // AT_BASE -> interp_loadaddr 229 - // AT_EXECFN -> apple[0] 230 - // AT_PAGESZ -> PAGE_SIZE 231 - // AT_FLAGS -> 0 232 - // AT_NULL 233 - 234 - void load(const char* path, struct loader_context* lc, bool isInterp) 235 - { 236 - ElfW(Ehdr) elfHdr; 237 - void* phdrs = NULL; 238 - 239 - // the interpreter path should be relative to the Linux root, not the Darling prefix 240 - int fd = isInterp ? _open_for_libelfloader(path, O_RDONLY, 0) : open(path, O_RDONLY); 241 - uintptr_t slide, base; 242 - 243 - if (fd == -1) 244 - { 245 - perror("open"); 246 - return; 247 - } 248 - 249 - if (read(fd, &elfHdr, sizeof(elfHdr)) != sizeof(elfHdr)) 250 - { 251 - perror("read"); 252 - goto out; 253 - } 254 - 255 - if (memcmp(elfHdr.e_ident, ELFMAG, SELFMAG) != 0 || elfHdr.e_ident[EI_CLASS] != MY_ELF_CLASS) 256 - { 257 - fprintf(stderr, "Wrong ELF signature\n"); 258 - goto out; 259 - } 260 - 261 - if (elfHdr.e_type != ET_DYN) 262 - { 263 - fprintf(stderr, "Only position independent ELF are supported\n"); 264 - goto out; 265 - } 266 - if (!elfHdr.e_phoff) 267 - { 268 - fprintf(stderr, "ELF is not loadable\n"); 269 - goto out; 270 - } 271 - 272 - phdrs = malloc(elfHdr.e_phentsize * elfHdr.e_phnum); 273 - if (pread(fd, phdrs, elfHdr.e_phentsize * elfHdr.e_phnum, elfHdr.e_phoff) != elfHdr.e_phentsize * elfHdr.e_phnum) 274 - { 275 - fprintf(stderr, "Failed to read Elf phdrs\n"); 276 - goto out; 277 - } 278 - 279 - // First, get total virtual range needed 280 - uintptr_t minAddr = UINTPTR_MAX, maxAddr = 0; 281 - 282 - for (int i = 0; i < elfHdr.e_phnum; i++) 283 - { 284 - ElfW(Phdr)* phdr = (ElfW(Phdr)*) (((char*) phdrs) + (i * elfHdr.e_phentsize)); 285 - 286 - if (phdr->p_type == PT_LOAD) 287 - { 288 - if (phdr->p_vaddr < minAddr) 289 - minAddr = ELF_PAGESTART(phdr->p_vaddr); 290 - if (phdr->p_vaddr + phdr->p_memsz > maxAddr) 291 - maxAddr = phdr->p_vaddr + phdr->p_memsz; 292 - } 293 - else if (phdr->p_type == PT_INTERP && isInterp) 294 - { 295 - fprintf(stderr, "Interp with PT_INTERP?\n"); 296 - goto out; 297 - } 298 - } 299 - if (maxAddr == 0) 300 - { 301 - fprintf(stderr, "No PT_LOAD headers?\n"); 302 - goto out; 303 - } 304 - 305 - base = (uintptr_t) mmap(NULL, maxAddr-minAddr, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); 306 - if (base == (uintptr_t) MAP_FAILED) 307 - { 308 - perror("mmap"); 309 - fprintf(stderr, "Cannot reserve 0x%lx bytes in memory\n", maxAddr-minAddr); 310 - goto out; 311 - } 312 - slide = base - minAddr; 313 - 314 - for (int i = 0; i < elfHdr.e_phnum; i++) 315 - { 316 - ElfW(Phdr)* phdr = (ElfW(Phdr)*) (((char*) phdrs) + (i * elfHdr.e_phentsize)); 317 - 318 - if (phdr->p_type == PT_LOAD) 319 - { 320 - int prot = 0; 321 - int flags = MAP_FIXED; 322 - void* result; 323 - 324 - uintptr_t addr = phdr->p_vaddr + slide; 325 - uintptr_t size = phdr->p_filesz + ELF_PAGEOFFSET(phdr->p_vaddr); 326 - uintptr_t memsize = phdr->p_memsz + ELF_PAGEOFFSET(phdr->p_vaddr); 327 - uintptr_t off = phdr->p_offset - ELF_PAGEOFFSET(phdr->p_vaddr); 328 - 329 - addr = ELF_PAGESTART(addr); 330 - // size = ELF_PAGEALIGN(size); 331 - 332 - if (phdr->p_flags & PF_X) 333 - prot |= PROT_EXEC; 334 - if (phdr->p_flags & PF_W) 335 - prot |= PROT_WRITE; 336 - if (phdr->p_flags & PF_R) 337 - prot |= PROT_READ; 338 - 339 - //if (phdr->p_flags & PF_W) 340 - flags |= MAP_PRIVATE; 341 - //else 342 - // flags |= MAP_SHARED; 343 - 344 - bool needszeroing = size != ELF_PAGEALIGN(size); 345 - 346 - if (needszeroing) 347 - prot |= PROT_WRITE; 348 - if (mprotect((void*) (addr), memsize, prot) == -1) 349 - { 350 - perror("mprotect"); 351 - } 352 - 353 - result = mmap((void*) addr, size, prot, flags, fd, off); 354 - if (result == MAP_FAILED) 355 - { 356 - perror("mmap"); 357 - goto out; 358 - } 359 - 360 - // Based on experiments, when we provide a size that is less than a multiple of page size 361 - // mmap() will map up to the whole page of file data anyway. Many ELF files, including ld.so, 362 - // however rely on the rest of the page being zeroed out. 363 - if (needszeroing) 364 - { 365 - memset((void*)(addr + size), 0, ELF_PAGEALIGN(size) - size); 366 - if (!(phdr->p_flags & PF_W)) 367 - mprotect((void*) (addr), memsize, prot & ~PROT_WRITE); 368 - } 369 - 370 - /* 371 - if (phdr->p_filesz < phdr->p_memsz) 372 - { 373 - if (mprotect((void*) (addr + phdr->p_filesz), phdr->p_memsz - phdr->p_filesz, prot) == -1) 374 - { 375 - perror("mprotect"); 376 - goto out; 377 - } 378 - } 379 - */ 380 - } 381 - else if (phdr->p_type == PT_INTERP) 382 - { 383 - char* interp = malloc(phdr->p_filesz + 1); 384 - 385 - if (pread(fd, interp, phdr->p_filesz, phdr->p_offset) != phdr->p_filesz) 386 - { 387 - free(interp); 388 - perror("reading PT_INTERP"); 389 - goto out; 390 - } 391 - interp[phdr->p_filesz] = '\0'; 392 - 393 - // Load interpreter 394 - if (_access_for_libelfloader(interp, F_OK) != 0) 395 - { 396 - fprintf(stderr, "Cannot load interpreter at %s\n", interp); 397 - free(interp); 398 - goto out; 399 - } 400 - 401 - load(interp, lc, true); 402 - 403 - free(interp); 404 - } 405 - } 406 - 407 - if (isInterp) 408 - { 409 - lc->interp_base = base; 410 - lc->interp_entry = slide + elfHdr.e_entry; 411 - } 412 - else 413 - { 414 - lc->exec_phstart = slide + elfHdr.e_phoff; 415 - lc->exec_phentsize = elfHdr.e_phentsize; 416 - lc->exec_phnum = elfHdr.e_phnum; 417 - lc->exec_entry = slide + elfHdr.e_entry; 418 - } 419 - 420 - out: 421 - free(phdrs); 422 - close(fd); 423 - } 424 - 425 - void loader_return(void) 426 - { 427 - longjmp(jmpbuf, 1); 428 - } 429 - 430 - #ifdef TEST 431 - 432 - int main(int argc, const char** argv) 433 - { 434 - if (argc > 1) 435 - run(argv[1]); 436 - return 0; 437 - } 438 - 439 - #endif 440 -
-28
src/libelfloader/loader.h
··· 1 - /* 2 - * Darling 3 - * Copyright (C) 2017 Lubos Dolezel 4 - * 5 - * This program is free software; you can redistribute it and/or 6 - * modify it under the terms of the GNU General Public License 7 - * as published by the Free Software Foundation; either version 2 8 - * of the License, or (at your option) any later version. 9 - * 10 - * This program is distributed in the hope that it will be useful, 11 - * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 - * GNU General Public License for more details. 14 - * 15 - * You should have received a copy of the GNU General Public License 16 - * along with this program; if not, write to the Free Software 17 - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 - */ 19 - 20 - #ifndef _LOADER_H 21 - #define _LOADER_H 22 - #include "native/elfcalls.h" 23 - 24 - extern struct elf_calls* _elfcalls; 25 - 26 - #endif 27 - 28 -
-28
src/libelfloader/native/CMakeLists.txt
··· 1 - project(libelfstub) 2 - 3 - set(elfstub_sources 4 - elfcalls.c 5 - threads.c 6 - ) 7 - 8 - if (BUILD_TARGET_64BIT) 9 - add_executable(elfloader_dummy64 ${elfstub_sources}) 10 - target_link_libraries(elfloader_dummy64 -lpthread -lrt -ldl) 11 - set_target_properties(elfloader_dummy64 PROPERTIES COMPILE_FLAGS "-fPIC" 12 - LINK_FLAGS "-pie -fPIC") 13 - endif () 14 - 15 - if (BUILD_TARGET_32BIT) 16 - add_executable(elfloader_dummy32 ${elfstub_sources}) 17 - target_link_libraries(elfloader_dummy32 -lpthread -lrt -ldl) 18 - set_target_properties(elfloader_dummy32 PROPERTIES COMPILE_FLAGS "-fPIC -m32" 19 - LINK_FLAGS "-pie -fPIC -m32") 20 - endif () 21 - 22 - if (BUILD_TARGET_64BIT AND BUILD_TARGET_32BIT) 23 - install(TARGETS elfloader_dummy32 elfloader_dummy64 DESTINATION libexec/darling/usr/libexec) 24 - elseif (BUILD_TARGET_64BIT) 25 - install(TARGETS elfloader_dummy64 DESTINATION libexec/darling/usr/libexec) 26 - elseif (BUILD_TARGET_32BIT) 27 - install(TARGETS elfloader_dummy32 DESTINATION libexec/darling/usr/libexec) 28 - endif ()
src/libelfloader/native/dthreads.h src/startup/mldr/elfcalls/dthreads.h
+8 -26
src/libelfloader/native/elfcalls.c src/startup/mldr/elfcalls/elfcalls.c
··· 8 8 #include <unistd.h> 9 9 #include "elfcalls.h" 10 10 #include "threads.h" 11 + #include <sys/un.h> 11 12 12 13 static void* dlopen_simple(const char* name) 13 14 { ··· 51 52 return errno; 52 53 } 53 54 55 + extern struct sockaddr_un __dserver_socket_address_data[]; 56 + 57 + static const void* __dserver_socket_address(void) { 58 + return &__dserver_socket_address_data; 59 + }; 60 + 54 61 void elfcalls_make(struct elf_calls* calls) 55 62 { 56 63 calls->dlopen = dlopen_simple; ··· 80 87 81 88 *((void**)&calls->shm_open) = shm_open; 82 89 *((void**)&calls->shm_unlink) = shm_unlink; 83 - } 84 90 85 - int main(int argc, const char** argv) 86 - { 87 - typedef void (*retfunc)(void); 88 - 89 - struct elf_calls* calls; 90 - retfunc ret; 91 - 92 - // for (int i = 0; i < argc; i++) 93 - // printf("arg %d: %s\n", i, argv[i]); 94 - 95 - calls = (struct elf_calls*) strtoul(argv[1], NULL, 16); 96 - ret = (retfunc) strtoul(argv[2], NULL, 16); 97 - 98 - // Enable locales 99 - setlocale(LC_ALL, ""); 100 - 101 - // puts("before elfcalls_make"); 102 - 103 - elfcalls_make(calls); 104 - // puts("after elfcalls_make"); 105 - // printf("Will call %p\n", ret); 106 - ret(); 107 - 108 - __builtin_unreachable(); 91 + calls->dserver_socket_address = __dserver_socket_address; 109 92 } 110 -
+3
src/libelfloader/native/elfcalls.h src/startup/mldr/elfcalls/elfcalls.h
··· 49 49 50 50 // POSIX sysconf 51 51 long (*sysconf)(int name); 52 + 53 + // darlingserver RPC info 54 + const void* (*dserver_socket_address)(void); 52 55 }; 53 56 54 57 #endif
src/libelfloader/native/threads.c src/startup/mldr/elfcalls/threads.c
src/libelfloader/native/threads.h src/startup/mldr/elfcalls/threads.h
+5
src/libelfloader/wrapgen/CMakeLists.txt
··· 10 10 add_executable(wrapgen wrapgen.cpp) 11 11 target_link_libraries(wrapgen dl) 12 12 13 + # This tool is useful for packaging to detect ELF dependencies inside Mach-O libraries, 14 + # which standard distro tools cannot do. 15 + if (DEFINED WITH_PRINT_WRAPPED_ELF) 16 + add_executable(print_wrapped_elf print_wrapped_elf.cpp) 17 + endif (DEFINED WITH_PRINT_WRAPPED_ELF)
+2 -8
src/startup/CMakeLists.txt
··· 18 18 19 19 target_link_libraries(darling -lutil) 20 20 21 - include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 21 + include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/src/darlingserver/include) 22 22 23 23 install(TARGETS darling DESTINATION bin 24 24 PERMISSIONS ··· 27 27 WORLD_READ WORLD_EXECUTE 28 28 SETUID) 29 29 30 - 31 - # This tool is useful for packaging to detect ELF dependencies inside Mach-O libraries, 32 - # which standard distro tools cannot do. 33 - if (DEFINED WITH_PRINT_WRAPPED_ELF) 34 - add_executable(print_wrapped_elf wrapgen/print_wrapped_elf.cpp) 35 - endif (DEFINED WITH_PRINT_WRAPPED_ELF) 36 - 37 30 add_executable(rtsig rtsig.c) 38 31 add_custom_command(OUTPUT rtsig.h DEPENDS rtsig COMMAND ${CMAKE_CURRENT_BINARY_DIR}/rtsig rtsig.h COMMENT "Determining available RT signals") 39 32 add_custom_target(rtsig_h DEPENDS rtsig.h) 40 33 34 + add_subdirectory(mldr)
+12 -350
src/startup/darling.c
··· 88 88 setuid(0); 89 89 setgid(0); 90 90 91 - if (!isModuleLoaded()) 92 - loadKernelModule(); 93 - 94 91 prefix = getenv("DPREFIX"); 95 92 if (!prefix) 96 93 prefix = defaultPrefixPath(); ··· 688 685 fprintf(stderr, "Darling needs this in order to create mount and PID namespaces and to perform mounts.\n"); 689 686 } 690 687 691 - void fixDirectoryPermissions(const char* path) 692 - { 693 - DIR* dir; 694 - struct dirent* ent; 695 - 696 - dir = opendir(path); 697 - if (!dir) 698 - return; 699 - 700 - while ((ent = readdir(dir)) != NULL) 701 - { 702 - if (ent->d_type == DT_DIR) 703 - { 704 - char* subdir; 705 - 706 - if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) 707 - continue; 708 - 709 - subdir = (char*) malloc(strlen(path) + 2 + strlen(ent->d_name)); 710 - sprintf(subdir, "%s/%s", path, ent->d_name); 711 - 712 - fixDirectoryPermissions(subdir); 713 - 714 - if (chown(subdir, g_originalUid, g_originalGid) == -1) 715 - fprintf(stderr, "Cannot chown %s: %s\n", subdir, strerror(errno)); 716 - 717 - free(subdir); 718 - } 719 - } 720 - 721 - closedir(dir); 722 - } 723 - 724 688 static uint32_t linux_release(void) 725 689 { 726 690 struct utsname uts; ··· 763 727 exit(1); 764 728 } 765 729 766 - if (unshare(CLONE_NEWPID | CLONE_NEWUTS | CLONE_NEWIPC) != 0) 730 + if (unshare(CLONE_NEWUTS | CLONE_NEWIPC) != 0) 767 731 { 768 - fprintf(stderr, "Cannot unshare PID, UTS and IPC namespaces to create darling-init: %s\n", strerror(errno)); 732 + fprintf(stderr, "Cannot unshare UTS and IPC namespaces to create darling-init: %s\n", strerror(errno)); 769 733 exit(1); 770 734 } 771 735 ··· 781 745 { 782 746 // The child 783 747 784 - char *opts; 785 - char putOld[4096]; 786 - char *p; 787 - 788 - close(pipefd[0]); 789 - 790 - // Since overlay cannot be mounted inside user namespaces, we have to setup a new mount namespace 791 - // and do the mount while we can be root 792 - if (unshare(CLONE_NEWNS) != 0) 793 - { 794 - fprintf(stderr, "Cannot unshare mount namespace: %s\n", strerror(errno)); 795 - exit(1); 796 - } 797 - 798 - // Because systemd marks / as MS_SHARED and we would inherit this into the overlay mount, 799 - // causing it not to be unmounted once the init process dies. 800 - if (mount(NULL, "/", NULL, MS_REC | MS_SLAVE, NULL) != 0) 801 - { 802 - fprintf(stderr, "Cannot remount / as slave: %s\n", strerror(errno)); 803 - exit(1); 804 - } 805 - 806 - umount("/dev/shm"); 807 - if (mount("tmpfs", "/dev/shm", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV, NULL) != 0) 808 - { 809 - fprintf(stderr, "Cannot mount new /dev/shm: %s\n", strerror(errno)); 810 - exit(1); 811 - } 812 - 813 - opts = (char*) malloc(strlen(prefix)*2 + sizeof(LIBEXEC_PATH) + 100); 814 - 815 - const char* opts_fmt = "lowerdir=%s,upperdir=%s,workdir=%s.workdir,index=off"; 816 - 817 - sprintf(opts, opts_fmt, LIBEXEC_PATH, prefix, prefix); 818 - 819 - // Mount overlay onto our prefix 820 - if (mount("overlay", prefix, "overlay", 0, opts) != 0) 821 - { 822 - fprintf(stderr, "Cannot mount overlay: %s\n", strerror(errno)); 823 - exit(1); 824 - } 825 - 826 - free(opts); 827 - 828 - // This is executed once at prefix creation 829 - if (g_fixPermissions) 830 - fixDirectoryPermissions(prefix); 831 - 832 - snprintf(putOld, sizeof(putOld), "%s/proc", prefix); 833 - 834 - // mount procfs for our new PID namespace 835 - if (mount("proc", putOld, "proc", 0, "") != 0) 836 - { 837 - fprintf(stderr, "Cannot mount procfs: %s\n", strerror(errno)); 838 - exit(1); 839 - } 840 - 841 - // Drop the privileges. It's important to drop GID first, because 842 - // non-root users can't change their GID. 843 - setresgid(g_originalGid, g_originalGid, g_originalGid); 844 - setresuid(g_originalUid, g_originalUid, g_originalUid); 845 - prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); 846 - 847 - setupUserHome(); 848 - setupCoredumpPattern(); 849 - 850 - // Set name to darling-init 851 - prctl(PR_SET_NAME, DARLING_INIT_COMM, 0, 0); 852 - p = stpcpy(g_argv[0], DARLING_INIT_COMM); 853 - memset(p, 0, g_envp[0] - p); 854 - 855 - /* 856 - if (unshare(CLONE_NEWUSER) != 0) 857 - { 858 - fprintf(stderr, "Cannot unshare user namespace: %s\n", strerror(errno)); 859 - exit(1); 860 - } 861 - */ 748 + char uid_str[21]; 749 + char gid_str[21]; 750 + char pipefd_str[21]; 862 751 863 - // Tell the parent we're ready 864 - write(pipefd[1], buffer, 1); 865 - close(pipefd[1]); 752 + snprintf(uid_str, sizeof(uid_str), "%d", g_originalUid); 753 + snprintf(gid_str, sizeof(gid_str), "%d", g_originalGid); 754 + snprintf(pipefd_str, sizeof(pipefd_str), "%d", pipefd[1]); 866 755 867 - // Here's where we wait for the parent to set up UID/GID mapping 868 - // if we enable user namespaces 756 + close(pipefd[0]); 869 757 870 - darlingPreInit(); 871 - spawnLaunchd(); 758 + execl(INSTALL_PREFIX "/bin/darlingserver", "darlingserver", prefix, uid_str, gid_str, pipefd_str, g_fixPermissions ? "1" : "0", NULL); 872 759 873 - // Never returns 760 + fprintf(stderr, "Failed to start darlingserver\n"); 761 + exit(1); 874 762 } 875 763 876 764 // Wait for the child to drop UID/GIDs and unshare stuff ··· 939 827 fclose(fp); 940 828 } 941 829 942 - void spawnLaunchd(void) 943 - { 944 - puts("Bootstrapping the container with launchd..."); 945 - 946 - // putenv("KQUEUE_DEBUG=1"); 947 - 948 - setenv("DYLD_ROOT_PATH", LIBEXEC_PATH, 1); 949 - execl(LIBEXEC_PATH "/usr/libexec/darling/vchroot", "vchroot", prefix, "/sbin/launchd", NULL); 950 - 951 - fprintf(stderr, "Failed to exec launchd: %s\n", strerror(errno)); 952 - abort(); 953 - } 954 - 955 - static void wipeDir(const char* dirpath) 956 - { 957 - char path[4096]; 958 - struct dirent* ent; 959 - DIR* dir = opendir(dirpath); 960 - 961 - if (!dir) 962 - return; 963 - 964 - while ((ent = readdir(dir)) != NULL) 965 - { 966 - if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) 967 - continue; 968 - 969 - snprintf(path, sizeof(path), "%s/%s", dirpath, ent->d_name); 970 - 971 - if (ent->d_type == DT_DIR) 972 - { 973 - wipeDir(path); 974 - rmdir(path); 975 - } 976 - else 977 - unlink(path); 978 - } 979 - 980 - closedir(dir); 981 - } 982 - 983 - void darlingPreInit(void) 984 - { 985 - // TODO: Run /usr/libexec/makewhatis 986 - const char* dirs[] = { 987 - "/var/tmp", 988 - "/var/run" 989 - }; 990 - 991 - char fullpath[4096]; 992 - strcpy(fullpath, prefix); 993 - const size_t prefixLen = strlen(fullpath); 994 - 995 - for (size_t i = 0; i < sizeof(dirs)/sizeof(dirs[0]); i++) 996 - { 997 - fullpath[prefixLen] = 0; 998 - strcat(fullpath, dirs[i]); 999 - wipeDir(fullpath); 1000 - } 1001 - } 1002 - 1003 830 char* defaultPrefixPath(void) 1004 831 { 1005 832 const char defaultPath[] = "/.darling"; ··· 1320 1147 exit(1); 1321 1148 } 1322 1149 } 1323 - 1324 - int isModuleLoaded() 1325 - { 1326 - size_t len = 0; 1327 - ssize_t read = 0; 1328 - char * line = NULL; 1329 - FILE *fp = NULL; 1330 - 1331 - if ((fp = fopen("/proc/modules", "r")) == NULL) 1332 - { 1333 - fprintf(stderr, "Failure opening /proc/modules: %s\n", strerror(errno)); 1334 - fclose(fp); 1335 - return 0; 1336 - } 1337 - 1338 - while (!feof(fp)) 1339 - { 1340 - read = getline(&line, &len, fp); 1341 - if (read > 0 && strstr(line, "darling_mach") != NULL) 1342 - { 1343 - fclose(fp); 1344 - return 1; 1345 - } 1346 - } 1347 - 1348 - fclose(fp); 1349 - return 0; 1350 - } 1351 - 1352 - void loadKernelModule() 1353 - { 1354 - int status; 1355 - FILE *fp = popen("/sbin/modprobe darling-mach", "w"); 1356 - 1357 - if (fp == NULL) 1358 - { 1359 - fprintf(stderr, "Failed to run modprobe: %s\n", strerror(errno)); 1360 - exit(1); 1361 - } 1362 - 1363 - status = pclose(fp); 1364 - if (WIFEXITED(status) && WEXITSTATUS(status) == 0) 1365 - { 1366 - fprintf(stderr, "Loaded the kernel module\n"); 1367 - return; 1368 - } 1369 - else 1370 - { 1371 - fprintf(stderr, "Failed to load the kernel module\n"); 1372 - exit(1); 1373 - } 1374 - } 1375 - 1376 - void setupCoredumpPattern(void) 1377 - { 1378 - FILE* f = fopen("/proc/sys/kernel/core_pattern", "w"); 1379 - if (f != NULL) 1380 - { 1381 - // This is how macOS saves core dumps 1382 - fputs("/cores/core.%p\n", f); 1383 - fclose(f); 1384 - } 1385 - } 1386 - 1387 - const char* xdgDirectory(const char* name) 1388 - { 1389 - static char dir[4096]; 1390 - char* cmd = (char*) malloc(16 + strlen(name)); 1391 - 1392 - sprintf(cmd, "xdg-user-dir %s", name); 1393 - 1394 - FILE* proc = popen(cmd, "r"); 1395 - 1396 - free(cmd); 1397 - 1398 - if (!proc) 1399 - return NULL; 1400 - 1401 - fgets(dir, sizeof(dir)-1, proc); 1402 - 1403 - pclose(proc); 1404 - 1405 - size_t len = strlen(dir); 1406 - if (len <= 1) 1407 - return NULL; 1408 - 1409 - if (dir[len-1] == '\n') 1410 - dir[len-1] = '\0'; 1411 - return dir; 1412 - } 1413 - 1414 - void setupUserHome(void) 1415 - { 1416 - char buf[4096], buf2[4096]; 1417 - 1418 - snprintf(buf, sizeof(buf), "%s/Users", prefix); 1419 - 1420 - // Remove the old /Users symlink that may exist 1421 - unlink(buf); 1422 - 1423 - // mkdir /Users 1424 - mkdir(buf, 0777); 1425 - 1426 - // mkdir /Users/Shared 1427 - strcat(buf, "/Shared"); 1428 - mkdir(buf, 0777); 1429 - 1430 - const char* home = getenv("HOME"); 1431 - 1432 - const char* login = NULL; 1433 - struct passwd* pw = getpwuid(getuid()); 1434 - 1435 - if (pw != NULL) 1436 - login = pw->pw_name; 1437 - 1438 - if (!login) 1439 - login = getlogin(); 1440 - 1441 - if (!login) 1442 - { 1443 - fprintf(stderr, "Cannot determine your user name\n"); 1444 - exit(1); 1445 - } 1446 - if (!home) 1447 - { 1448 - fprintf(stderr, "Cannot determine your home directory\n"); 1449 - exit(1); 1450 - } 1451 - 1452 - snprintf(buf, sizeof(buf), "%s/Users/%s", prefix, login); 1453 - 1454 - // mkdir /Users/$LOGIN 1455 - mkdir(buf, 0755); 1456 - 1457 - snprintf(buf2, sizeof(buf2), "/Volumes/SystemRoot%s", home); 1458 - 1459 - strcat(buf, "/LinuxHome"); 1460 - unlink(buf); 1461 - 1462 - // symlink /Users/$LOGIN/LinuxHome -> $HOME 1463 - symlink(buf2, buf); 1464 - 1465 - static const char* xdgmap[][2] = { 1466 - { "DESKTOP", "Desktop" }, 1467 - { "DOWNLOAD", "Downloads" }, 1468 - { "PUBLICSHARE", "Public" }, 1469 - { "DOCUMENTS", "Documents" }, 1470 - { "MUSIC", "Music" }, 1471 - { "PICTURES", "Pictures" }, 1472 - { "VIDEOS", "Movies" }, 1473 - }; 1474 - 1475 - for (int i = 0; i < sizeof(xdgmap) / sizeof(xdgmap[0]); i++) 1476 - { 1477 - const char* dir = xdgDirectory(xdgmap[i][0]); 1478 - if (!dir) 1479 - continue; 1480 - 1481 - snprintf(buf2, sizeof(buf2), "/Volumes/SystemRoot%s", dir); 1482 - snprintf(buf, sizeof(buf), "%s/Users/%s/%s", prefix, login, xdgmap[i][1]); 1483 - 1484 - unlink(buf); 1485 - symlink(buf2, buf); 1486 - } 1487 - }
-113
src/startup/dirstructure.cpp
··· 1 - /* 2 - This file is part of Darling. 3 - 4 - Copyright (C) 2015 Lubos Dolezel 5 - 6 - Darling is free software: you can redistribute it and/or modify 7 - it under the terms of the GNU General Public License as published by 8 - the Free Software Foundation, either version 3 of the License, or 9 - (at your option) any later version. 10 - 11 - Darling is distributed in the hope that it will be useful, 12 - but WITHOUT ANY WARRANTY; without even the implied warranty of 13 - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 - GNU General Public License for more details. 15 - 16 - You should have received a copy of the GNU General Public License 17 - along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 - */ 19 - 20 - #include "dirstructure.h" 21 - #include <sstream> 22 - #include <unistd.h> 23 - #include <cstdlib> 24 - #include <cstring> 25 - #include <iostream> 26 - #include <errno.h> 27 - #include <sys/stat.h> 28 - #include <sys/types.h> 29 - 30 - static std::string GetUserLibrary() 31 - { 32 - const char *home, *prefix; 33 - std::stringstream ss; 34 - std::string path; 35 - 36 - home = getenv("HOME"); 37 - if (!home) 38 - return std::string(); // give up on this user 39 - 40 - ss << home << '/' << "Library" << '/'; 41 - return ss.str(); 42 - } 43 - 44 - bool HasUserDirectoryStructure() 45 - { 46 - std::string path = GetUserLibrary(); 47 - 48 - if (path.empty()) 49 - return true; // give up on this user 50 - 51 - if (::access(path.c_str(), F_OK) == -1) 52 - return false; 53 - else 54 - return true; 55 - } 56 - 57 - void SetupUserDirectoryStructure() 58 - { 59 - const char* dirs[] = { 60 - "Application Support", 61 - "Assistants", 62 - "Audio", 63 - "Audio/MIDI Drivers", 64 - "Audio/Plug-Ins", 65 - "Audio/Plug-Ins/Components", 66 - "Audio/Plug-Ins/Digidesign", 67 - "Audio/Plug-Ins/VST", 68 - "Audio/Sounds", 69 - "Audio/Sounds/Alerts", 70 - "Audio/Sounds/Banks", 71 - "Caches", 72 - "ColorPickers", 73 - "Colors", 74 - "Compositions", 75 - "Favorites", 76 - "FontCollections", 77 - "Fonts", 78 - "iMovie", 79 - "iMovie/Plug-ins", 80 - "iMovie/Sound Effects", 81 - "Input Methods", 82 - "Internet Plug-Ins", 83 - "Keyboard Layouts", 84 - "Logs", 85 - "PreferencePanes", 86 - "Preferences", 87 - "Printers", 88 - "Screen Savers", 89 - "Sounds", 90 - "Spelling", 91 - "Voices", 92 - }; 93 - 94 - std::string path = GetUserLibrary(); 95 - 96 - if (path.empty()) 97 - return; 98 - 99 - std::cerr << "Darling: Creating Library structure at " << path << std::endl; 100 - 101 - if (::mkdir(path.c_str(), 0777) == -1) 102 - std::cerr << "Darling: Cannot mkdir(" << path << "): " << strerror(errno) << std::endl; 103 - 104 - for (const char* dir : dirs) 105 - { 106 - std::string s = path; 107 - 108 - s += dir; 109 - 110 - if (::mkdir(s.c_str(), 0777) == -1) 111 - std::cerr << "Darling: Cannot mkdir(" << s << "): " << strerror(errno) << std::endl; 112 - } 113 - }
-29
src/startup/dirstructure.h
··· 1 - /* 2 - This file is part of Darling. 3 - 4 - Copyright (C) 2015 Lubos Dolezel 5 - 6 - Darling is free software: you can redistribute it and/or modify 7 - it under the terms of the GNU General Public License as published by 8 - the Free Software Foundation, either version 3 of the License, or 9 - (at your option) any later version. 10 - 11 - Darling is distributed in the hope that it will be useful, 12 - but WITHOUT ANY WARRANTY; without even the implied warranty of 13 - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 - GNU General Public License for more details. 15 - 16 - You should have received a copy of the GNU General Public License 17 - along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 - */ 19 - 20 - #ifndef DIRSTRUCTURE_H 21 - #define DIRSTRUCTURE_H 22 - 23 - // Checks whether $HOME/Library exists 24 - bool HasUserDirectoryStructure(); 25 - 26 - // Creates the default OS X $HOME/Library directory structure 27 - void SetupUserDirectoryStructure(); 28 - 29 - #endif
+47
src/startup/mldr/CMakeLists.txt
··· 1 + project(mldr) 2 + 3 + cmake_minimum_required(VERSION 3.10) 4 + 5 + include_directories(include) 6 + 7 + add_library(mldr_dserver_rpc ${CMAKE_BINARY_DIR}/src/darlingserver/src/rpc.c) 8 + add_library(mldr32_dserver_rpc ${CMAKE_BINARY_DIR}/src/darlingserver/src/rpc.c) 9 + 10 + add_dependencies(mldr_dserver_rpc generate_dserver_rpc_wrappers) 11 + add_dependencies(mldr32_dserver_rpc generate_dserver_rpc_wrappers) 12 + 13 + target_compile_options(mldr_dserver_rpc PRIVATE -include ${CMAKE_CURRENT_SOURCE_DIR}/resources/dserver-rpc-defs.h) 14 + target_compile_options(mldr32_dserver_rpc PRIVATE -include ${CMAKE_CURRENT_SOURCE_DIR}/resources/dserver-rpc-defs.h) 15 + 16 + target_compile_options(mldr32_dserver_rpc PRIVATE -m32) 17 + target_link_options(mldr32_dserver_rpc PRIVATE -m32) 18 + 19 + set(mldr_sources 20 + mldr.c 21 + commpage.c 22 + elfcalls/elfcalls.c 23 + elfcalls/threads.c 24 + ) 25 + 26 + add_executable(mldr ${mldr_sources}) 27 + 28 + add_executable(mldr32 ${mldr_sources}) 29 + target_compile_options(mldr32 PRIVATE -m32) 30 + target_link_options(mldr32 PRIVATE -m32) 31 + 32 + target_compile_options(mldr PRIVATE -pthread) 33 + target_compile_options(mldr32 PRIVATE -pthread) 34 + target_link_options(mldr PRIVATE -pthread) 35 + target_link_options(mldr32 PRIVATE -pthread) 36 + 37 + target_link_libraries(mldr PRIVATE -lrt -ldl mldr_dserver_rpc) 38 + target_link_libraries(mldr32 PRIVATE -lrt -ldl mldr32_dserver_rpc) 39 + 40 + install(TARGETS mldr mldr32 DESTINATION libexec/darling/usr/libexec/darling) 41 + 42 + include(setcap) 43 + setcap(libexec/darling/usr/libexec/darling/mldr cap_sys_rawio,cap_sys_resource+ep) 44 + setcap(libexec/darling/usr/libexec/darling/mldr32 cap_sys_rawio,cap_sys_resource+ep) 45 + 46 + configure_file(darling.conf.in darling.conf @ONLY) 47 + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/darling.conf" DESTINATION lib/binfmt.d)
+151
src/startup/mldr/commpage.c
··· 1 + #include "commpage.h" 2 + #include <sys/mman.h> 3 + #include <stdio.h> 4 + #include <errno.h> 5 + #include <string.h> 6 + #include <stdlib.h> 7 + #include <cpuid.h> 8 + #include <unistd.h> 9 + #include <sys/sysinfo.h> 10 + 11 + // Include commpage definitions 12 + #define PRIVATE 13 + #include <i386/cpu_capabilities.h> 14 + 15 + static const char* SIGNATURE32 = "commpage 32-bit"; 16 + static const char* SIGNATURE64 = "commpage 64-bit"; 17 + 18 + static uint64_t get_cpu_caps(void); 19 + 20 + #define CGET(p) (commpage + ((p)-_COMM_PAGE_START_ADDRESS)) 21 + 22 + void commpage_setup(bool _64bit) 23 + { 24 + uint8_t* commpage; 25 + uint64_t* cpu_caps64; 26 + uint32_t* cpu_caps; 27 + uint16_t* version; 28 + char* signature; 29 + uint64_t my_caps; 30 + uint8_t *ncpus, *nactivecpus; 31 + uint8_t *physcpus, *logcpus; 32 + struct sysinfo si; 33 + 34 + commpage = (uint8_t*) mmap((void*)(_64bit ? _COMM_PAGE64_BASE_ADDRESS : _COMM_PAGE32_BASE_ADDRESS), 35 + _64bit ? _COMM_PAGE64_AREA_LENGTH : _COMM_PAGE32_AREA_LENGTH, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 36 + if (commpage == MAP_FAILED) 37 + { 38 + fprintf(stderr, "Cannot mmap commpage: %s\n", strerror(errno)); 39 + exit(1); 40 + } 41 + 42 + signature = (char*)CGET(_COMM_PAGE_SIGNATURE); 43 + version = (uint16_t*)CGET(_COMM_PAGE_VERSION); 44 + cpu_caps64 = (uint64_t*)CGET(_COMM_PAGE_CPU_CAPABILITIES64); 45 + cpu_caps = (uint32_t*)CGET(_COMM_PAGE_CPU_CAPABILITIES); 46 + 47 + strcpy(signature, _64bit ? SIGNATURE64 : SIGNATURE32); 48 + *version = _COMM_PAGE_THIS_VERSION; 49 + 50 + ncpus = (uint8_t*)CGET(_COMM_PAGE_NCPUS); 51 + *ncpus = sysconf(_SC_NPROCESSORS_CONF); 52 + 53 + nactivecpus = (uint8_t*)CGET(_COMM_PAGE_ACTIVE_CPUS); 54 + *nactivecpus = sysconf(_SC_NPROCESSORS_ONLN); 55 + 56 + // Better imprecise information than no information 57 + physcpus = (uint8_t*)CGET(_COMM_PAGE_PHYSICAL_CPUS); 58 + logcpus = (uint8_t*)CGET(_COMM_PAGE_LOGICAL_CPUS); 59 + *physcpus = *logcpus = *ncpus; 60 + 61 + my_caps = get_cpu_caps(); 62 + if (*ncpus == 1) 63 + my_caps |= kUP; 64 + 65 + *cpu_caps = (uint32_t) my_caps; 66 + *cpu_caps64 = my_caps; 67 + 68 + if (sysinfo(&si) == 0) 69 + { 70 + uint64_t* memsize = (uint64_t*)CGET(_COMM_PAGE_MEMORY_SIZE); 71 + *memsize = si.totalram * si.mem_unit; 72 + } 73 + } 74 + 75 + uint64_t get_cpu_caps(void) 76 + { 77 + uint64_t caps = 0; 78 + 79 + { 80 + union cpu_flags1 eax; 81 + union cpu_flags2 ecx; 82 + union cpu_flags3 edx; 83 + uint32_t ebx; 84 + 85 + eax.reg = ecx.reg = edx.reg = 0; 86 + __cpuid(1, eax.reg, ebx, ecx.reg, edx.reg); 87 + 88 + if (ecx.mmx) 89 + caps |= kHasMMX; 90 + if (ecx.sse) 91 + caps |= kHasSSE; 92 + if (ecx.sse2) 93 + caps |= kHasSSE2; 94 + if (edx.sse3) 95 + caps |= kHasSSE3; 96 + if (ecx.ia64) 97 + caps |= k64Bit; 98 + if (edx.ssse3) 99 + caps |= kHasSupplementalSSE3; 100 + if (edx.sse41) 101 + caps |= kHasSSE4_1; 102 + if (edx.sse42) 103 + caps |= kHasSSE4_2; 104 + if (edx.aes) 105 + caps |= kHasAES; 106 + if (edx.avx) 107 + caps |= kHasAVX1_0; 108 + if (edx.rdrnd) 109 + caps |= kHasRDRAND; 110 + if (edx.fma) 111 + caps |= kHasFMA; 112 + if (edx.f16c) 113 + caps |= kHasF16C; 114 + } 115 + { 116 + union cpu_flags4 ebx; 117 + union cpu_flags5 ecx; 118 + uint32_t edx = 0, eax = 7; 119 + 120 + __cpuid(7, eax, ebx.reg, ecx.reg, edx); 121 + 122 + if (ebx.erms) 123 + caps |= kHasENFSTRG; 124 + if (ebx.avx2) 125 + caps |= kHasAVX2_0; 126 + if (ebx.bmi1) 127 + caps |= kHasBMI1; 128 + if (ebx.bmi2) 129 + caps |= kHasBMI2; 130 + if (ebx.rtm) 131 + caps |= kHasRTM; 132 + if (ebx.hle) 133 + caps |= kHasHLE; 134 + if (ebx.rdseed) 135 + caps |= kHasRDSEED; 136 + if (ebx.adx) 137 + caps |= kHasADX; 138 + if (ebx.mpx) 139 + caps |= kHasMPX; 140 + if (ebx.sgx) 141 + caps |= kHasSGX; 142 + } 143 + 144 + return caps; 145 + } 146 + 147 + unsigned long commpage_address(bool _64bit) 148 + { 149 + return _64bit ? _COMM_PAGE64_BASE_ADDRESS : _COMM_PAGE32_BASE_ADDRESS; 150 + } 151 +
+155
src/startup/mldr/commpage.h
··· 1 + #ifndef _COMMPAGE_H 2 + #define _COMMPAGE_H 3 + #include <stdbool.h> 4 + #include <stdint.h> 5 + 6 + #ifdef __cplusplus 7 + extern "C" { 8 + #endif 9 + 10 + void commpage_setup(bool _64bit); 11 + unsigned long commpage_address(bool _64bit); 12 + 13 + union cpu_flags1 { 14 + struct { 15 + uint8_t step: 4; 16 + uint8_t model: 4; 17 + uint8_t family: 4; 18 + uint8_t type: 2; 19 + uint8_t pad1: 2; 20 + uint8_t emodel: 4; 21 + uint8_t efamily: 8; 22 + uint8_t pad2: 4; 23 + }; 24 + uint32_t reg; 25 + }; 26 + 27 + union cpu_flags2 { 28 + struct { 29 + uint8_t fpu: 1; 30 + uint8_t vme: 1; 31 + uint8_t de: 1; 32 + uint8_t pse: 1; 33 + uint8_t tsc: 1; 34 + uint8_t msr: 1; 35 + uint8_t pae: 1; 36 + uint8_t mce: 1; 37 + uint8_t cx8: 1; 38 + uint8_t apic: 1; 39 + uint8_t reserved1: 1; 40 + uint8_t sep: 1; 41 + uint8_t mtrr: 1; 42 + uint8_t pge: 1; 43 + uint8_t mca: 1; 44 + uint8_t cmov: 1; 45 + uint8_t pat: 1; 46 + uint8_t pse36: 1; 47 + uint8_t psn: 1; 48 + uint8_t clfsh: 1; 49 + uint8_t reserved2: 1; 50 + uint8_t ds: 1; 51 + uint8_t acpi: 1; 52 + uint8_t mmx: 1; 53 + uint8_t fxsr: 1; 54 + uint8_t sse: 1; 55 + uint8_t sse2: 1; 56 + uint8_t ss: 1; 57 + uint8_t htt: 1; 58 + uint8_t tm: 1; 59 + uint8_t ia64: 1; 60 + uint8_t pbe: 1; 61 + }; 62 + uint32_t reg; 63 + }; 64 + 65 + union cpu_flags3 { 66 + struct { 67 + uint32_t sse3: 1; 68 + uint32_t pclmulqdq: 1; 69 + uint32_t dtes64: 1; 70 + uint32_t monitor: 1; 71 + uint32_t dscpl: 1; 72 + uint32_t vmx: 1; 73 + uint32_t smx: 1; 74 + uint32_t est: 1; 75 + uint32_t tm2: 1; 76 + uint32_t ssse3: 1; 77 + uint32_t cntxid: 1; 78 + uint32_t sdbg: 1; 79 + uint32_t fma: 1; 80 + uint32_t cx16: 1; 81 + uint32_t xtpr: 1; 82 + uint32_t pdcm: 1; 83 + uint32_t reserved1: 1; 84 + uint32_t pcid: 1; 85 + uint32_t dca: 1; 86 + uint32_t sse41: 1; 87 + uint32_t sse42: 1; 88 + uint32_t x2apic: 1; 89 + uint32_t movbe: 1; 90 + uint32_t popcnt: 1; 91 + uint32_t tscdadline: 1; 92 + uint32_t aes: 1; 93 + uint32_t xsave: 1; 94 + uint32_t osxsave: 1; 95 + uint32_t avx: 1; 96 + uint32_t f16c: 1; 97 + uint32_t rdrnd: 1; 98 + uint32_t hypervisor: 1; 99 + }; 100 + uint32_t reg; 101 + }; 102 + 103 + union cpu_flags4 { 104 + struct { 105 + uint8_t fsgsbase: 1; 106 + uint8_t ia32tscadjust: 1; 107 + uint8_t sgx: 1; 108 + uint8_t bmi1: 1; 109 + uint8_t hle: 1; 110 + uint8_t avx2: 1; 111 + uint8_t reserved1: 1; 112 + uint8_t smep: 1; 113 + uint8_t bmi2: 1; 114 + uint8_t erms: 1; 115 + uint8_t invpcid: 1; 116 + uint8_t rtm: 1; 117 + uint8_t pqm: 1; 118 + uint8_t fpucsdsdeprecated: 1; 119 + uint8_t mpx: 1; 120 + uint8_t pqe: 1; 121 + uint8_t avx512f: 1; 122 + uint8_t avx512dq: 1; 123 + uint8_t rdseed: 1; 124 + uint8_t adx: 1; 125 + uint8_t smap: 1; 126 + uint8_t avx512ifma: 1; 127 + uint8_t pcommit: 1; 128 + uint8_t clflushopt: 1; 129 + uint8_t clwb: 1; 130 + uint8_t intelproctrace: 1; 131 + uint8_t avx512pf: 1; 132 + uint8_t avx512er: 1; 133 + uint8_t avx512cd: 1; 134 + uint8_t sha: 1; 135 + uint8_t avx512bw: 1; 136 + uint8_t avx512vl: 1; 137 + }; 138 + uint32_t reg; 139 + }; 140 + 141 + union cpu_flags5 { 142 + struct { 143 + uint8_t prefetchwt1: 1; 144 + uint8_t avx512vbmi: 1; 145 + uint32_t reserved: 30; 146 + }; 147 + uint32_t reg; 148 + }; 149 + 150 + #ifdef __cplusplus 151 + } 152 + #endif 153 + 154 + #endif 155 +
+3
src/startup/mldr/darling.conf.in
··· 1 + :Mach-O-64-bit:M::\xcf\xfa\xed\xfe::@CMAKE_INSTALL_PREFIX@/libexec/darling/bin/mldr:P 2 + :Mach-O-32-bit:M::\xce\xfa\xed\xfe::@CMAKE_INSTALL_PREFIX@/libexec/darling/bin/mldr32:P 3 + :Fat-Mach-O:M::\xca\xfe\xba\xbe::@CMAKE_INSTALL_PREFIX@/libexec/darling/bin/mldr:P
+1
src/startup/mldr/include/architecture/byte_order.h
··· 1 + ../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/architecture/byte_order.h
+6
src/startup/mldr/include/i386/_types.h
··· 1 + #ifndef _MLDR_I386__TYPES_SHIM_H_ 2 + #define _MLDR_I386__TYPES_SHIM_H_ 3 + 4 + typedef unsigned int __darwin_natural_t; 5 + 6 + #endif // _MLDR_I386__TYPES_SHIM_H_
+1
src/startup/mldr/include/i386/cpu_capabilities.h
··· 1 + ../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/i386/cpu_capabilities.h
+1
src/startup/mldr/include/libkern/OSByteOrder.h
··· 1 + ../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/libkern/OSByteOrder.h
+1
src/startup/mldr/include/libkern/_OSByteOrder.h
··· 1 + ../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/libkern/_OSByteOrder.h
+1
src/startup/mldr/include/libkern/i386/OSByteOrder.h
··· 1 + ../../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/libkern/i386/OSByteOrder.h
+1
src/startup/mldr/include/libkern/i386/_OSByteOrder.h
··· 1 + ../../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/libkern/i386/_OSByteOrder.h
+1
src/startup/mldr/include/mach-o
··· 1 + ../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/mach-o
+1
src/startup/mldr/include/mach/boolean.h
··· 1 + ../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/mach/boolean.h
+1
src/startup/mldr/include/mach/i386/boolean.h
··· 1 + ../../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/mach/i386/boolean.h
+6
src/startup/mldr/include/mach/i386/vm_param.h
··· 1 + #ifndef _MLDR_MACH_I386_VM_PARAM_SHIM_H_ 2 + #define _MLDR_MACH_I386_VM_PARAM_SHIM_H_ 3 + 4 + 5 + 6 + #endif // _MLDR_MACH_I386_VM_PARAM_SHIM_H_
+1
src/startup/mldr/include/mach/i386/vm_types.h
··· 1 + ../../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/mach/i386/vm_types.h
+1
src/startup/mldr/include/mach/machine.h
··· 1 + ../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/mach/machine.h
+1
src/startup/mldr/include/mach/machine/boolean.h
··· 1 + ../../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/mach/machine/boolean.h
+6
src/startup/mldr/include/mach/machine/thread_status.h
··· 1 + #ifndef _MLDR_MACH_MACHINE_THREAD_STATUS_SHIM_H_ 2 + #define _MLDR_MACH_MACHINE_THREAD_STATUS_SHIM_H_ 3 + 4 + 5 + 6 + #endif // _MLDR_MACH_MACHINE_THREAD_STATUS_SHIM_H_
+1
src/startup/mldr/include/mach/machine/vm_types.h
··· 1 + ../../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/mach/i386/vm_types.h
+1
src/startup/mldr/include/mach/vm_prot.h
··· 1 + ../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/mach/vm_prot.h
+6
src/startup/mldr/include/sys/_types.h
··· 1 + #ifndef _MLDR_SYS__TYPES_SHIM_H_ 2 + #define _MLDR_SYS__TYPES_SHIM_H_ 3 + 4 + 5 + 6 + #endif // _MLDR_SYS__TYPES_SHIM_H_
+1
src/startup/mldr/include/sys/_types/_os_inline.h
··· 1 + ../../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_os_inline.h
+1
src/startup/mldr/include/sys/commpage.h
··· 1 + ../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/commpage.h
+330
src/startup/mldr/loader.c
··· 1 + #include <stdint.h> 2 + #include <mach-o/loader.h> 3 + #include <mach-o/fat.h> 4 + #include <stdlib.h> 5 + #include <unistd.h> 6 + #include <stdio.h> 7 + #include <stdbool.h> 8 + #include <sys/mman.h> 9 + #include <errno.h> 10 + #include <string.h> 11 + 12 + #include "loader.h" 13 + 14 + static int native_prot(int prot); 15 + static void load(const char* path, cpu_type_t cpu, bool expect_dylinker, char** argv, struct load_results* lr); 16 + static void setup_space(struct load_results* lr, bool is_64_bit); 17 + 18 + #ifndef PAGE_SIZE 19 + # define PAGE_SIZE 4096 20 + #endif 21 + #define PAGE_ALIGN(x) (x & ~(PAGE_SIZE-1)) 22 + #define PAGE_ROUNDUP(x) (((((x)-1) / PAGE_SIZE)+1) * PAGE_SIZE) 23 + 24 + // Definitions: 25 + // FUNCTION_NAME (load32/load64) 26 + // SEGMENT_STRUCT (segment_command/SEGMENT_STRUCT) 27 + // SEGMENT_COMMAND (LC_SEGMENT/SEGMENT_COMMAND) 28 + // MACH_HEADER_STRUCT (mach_header/MACH_HEADER_STRUCT) 29 + // SECTION_STRUCT (section/SECTION_STRUCT) 30 + 31 + #if defined(GEN_64BIT) 32 + # define FUNCTION_NAME load64 33 + # define SEGMENT_STRUCT segment_command_64 34 + # define SEGMENT_COMMAND LC_SEGMENT_64 35 + # define MACH_HEADER_STRUCT mach_header_64 36 + # define SECTION_STRUCT section_64 37 + # define MAP_EXTRA 0 38 + #elif defined(GEN_32BIT) 39 + # define FUNCTION_NAME load32 40 + # define SEGMENT_STRUCT segment_command 41 + # define SEGMENT_COMMAND LC_SEGMENT 42 + # define MACH_HEADER_STRUCT mach_header 43 + # define SECTION_STRUCT section 44 + # define MAP_EXTRA MAP_32BIT 45 + #else 46 + # error See above 47 + #endif 48 + 49 + void FUNCTION_NAME(int fd, bool expect_dylinker, struct load_results* lr) 50 + { 51 + struct MACH_HEADER_STRUCT header; 52 + uint8_t* cmds; 53 + uintptr_t entryPoint = 0, entryPointDylinker = 0; 54 + struct MACH_HEADER_STRUCT* mappedHeader = NULL; 55 + uintptr_t slide = 0; 56 + uintptr_t mmapSize = 0; 57 + bool pie = false; 58 + uint32_t fat_offset; 59 + void* tmp_map_base = NULL; 60 + 61 + if (!expect_dylinker) 62 + { 63 + #if defined(GEN_64BIT) 64 + setup_space(lr, true); 65 + #elif defined(GEN_32BIT) 66 + lr->_32on64 = true; 67 + setup_space(lr, false); 68 + #else 69 + #error Unsupported architecture 70 + #endif 71 + } 72 + 73 + fat_offset = lseek(fd, 0, SEEK_CUR); 74 + 75 + if (read(fd, &header, sizeof(header)) != sizeof(header)) 76 + { 77 + fprintf(stderr, "Cannot read the mach header.\n"); 78 + exit(1); 79 + } 80 + 81 + if (header.filetype != (expect_dylinker ? MH_DYLINKER : MH_EXECUTE)) 82 + { 83 + fprintf(stderr, "Found unexpected Mach-O file type: %u\n", header.filetype); 84 + exit(1); 85 + } 86 + 87 + tmp_map_base = mmap(NULL, PAGE_ROUNDUP(sizeof(header) + header.sizeofcmds), PROT_READ, MAP_PRIVATE, fd, fat_offset); 88 + if (tmp_map_base == MAP_FAILED) { 89 + fprintf(stderr, "Failed to mmap header + commands\n"); 90 + exit(1); 91 + } 92 + 93 + cmds = (void*)((char*)tmp_map_base + sizeof(header)); 94 + 95 + if ((header.filetype == MH_EXECUTE && header.flags & MH_PIE) || header.filetype == MH_DYLINKER) 96 + { 97 + uintptr_t base = -1; 98 + 99 + // Go through all SEGMENT_COMMAND commands to get the total continuous range required. 100 + for (uint32_t i = 0, p = 0; i < header.ncmds; i++) 101 + { 102 + struct SEGMENT_STRUCT* seg = (struct SEGMENT_STRUCT*) &cmds[p]; 103 + 104 + // Load commands are always sorted, so this will get us the maximum address. 105 + if (seg->cmd == SEGMENT_COMMAND && strcmp(seg->segname, "__PAGEZERO") != 0) 106 + { 107 + if (base == -1) 108 + { 109 + base = seg->vmaddr; 110 + //if (base != 0 && header.filetype == MH_DYLINKER) 111 + // goto no_slide; 112 + } 113 + mmapSize = seg->vmaddr + seg->vmsize - base; 114 + } 115 + 116 + p += seg->cmdsize; 117 + } 118 + 119 + slide = (uintptr_t) mmap((void*) base, mmapSize, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_EXTRA, -1, 0); 120 + if (slide == (uintptr_t)MAP_FAILED) 121 + { 122 + fprintf(stderr, "Cannot mmap anonymous memory range: %s\n", strerror(errno)); 123 + exit(1); 124 + } 125 + 126 + if (slide + mmapSize > lr->vm_addr_max) 127 + lr->vm_addr_max = lr->base = slide + mmapSize; 128 + slide -= base; 129 + 130 + pie = true; 131 + } 132 + no_slide: 133 + 134 + for (uint32_t i = 0, p = 0; i < header.ncmds && p < header.sizeofcmds; i++) 135 + { 136 + struct load_command* lc; 137 + 138 + lc = (struct load_command*) &cmds[p]; 139 + if (lc->cmdsize > PAGE_SIZE) 140 + { 141 + fprintf(stderr, "Broken Mach-O file, cmdsize = %d\n", lc->cmdsize); 142 + exit(1); 143 + } 144 + 145 + switch (lc->cmd) 146 + { 147 + case SEGMENT_COMMAND: 148 + { 149 + struct SEGMENT_STRUCT* seg = (struct SEGMENT_STRUCT*) lc; 150 + void* rv; 151 + 152 + // This logic is wrong and made up. But it's the only combination where 153 + // some apps stop crashing (TBD why) and LLDB recognized the memory layout 154 + // of processes started as suspended. 155 + int maxprot = native_prot(seg->maxprot); 156 + int initprot = native_prot(seg->initprot); 157 + int useprot = (initprot & PROT_EXEC) ? maxprot : initprot; 158 + 159 + if (seg->filesize < seg->vmsize) 160 + { 161 + unsigned long map_addr; 162 + if (slide != 0) 163 + { 164 + unsigned long addr = seg->vmaddr; 165 + 166 + if (addr != 0) 167 + addr += slide; 168 + 169 + // Some segments' filesize != vmsize, thus this mprotect(). 170 + rv = mmap((void*)addr, seg->vmsize, useprot, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); 171 + if (rv == (void*)MAP_FAILED) 172 + { 173 + fprintf(stderr, "Cannot mmap segment %s at %p: %s\n", seg->segname, (void*)(uintptr_t)seg->vmaddr, strerror(errno)); 174 + exit(1); 175 + } 176 + } 177 + else 178 + { 179 + size_t size = seg->vmsize - seg->filesize; 180 + rv = mmap((void*) PAGE_ALIGN(seg->vmaddr + seg->vmsize - size), PAGE_ROUNDUP(size), useprot, 181 + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); 182 + if (rv == (void*)MAP_FAILED) 183 + { 184 + fprintf(stderr, "Cannot mmap segment %s at %p: %s\n", seg->segname, (void*)(uintptr_t)seg->vmaddr, strerror(errno)); 185 + exit(1); 186 + } 187 + } 188 + } 189 + 190 + if (seg->filesize > 0) 191 + { 192 + unsigned long addr = seg->vmaddr + slide; 193 + rv = mmap((void*)addr, seg->filesize, useprot, 194 + MAP_FIXED | MAP_PRIVATE, fd, seg->fileoff + fat_offset); 195 + if (rv == (void*)MAP_FAILED) 196 + { 197 + fprintf(stderr, "Cannot mmap segment %s at %p: %s\n", seg->segname, (void*)(uintptr_t)seg->vmaddr, strerror(errno)); 198 + exit(1); 199 + } 200 + 201 + if (seg->fileoff == 0) 202 + mappedHeader = (struct MACH_HEADER_STRUCT*) (seg->vmaddr + slide); 203 + } 204 + 205 + if (seg->vmaddr + slide + seg->vmsize > lr->vm_addr_max) 206 + lr->vm_addr_max = seg->vmaddr + slide + seg->vmsize; 207 + 208 + if (strcmp(SEG_DATA, seg->segname) == 0) 209 + { 210 + // Look for section named __all_image_info for GDB integration 211 + struct SECTION_STRUCT* sect = (struct SECTION_STRUCT*) (seg+1); 212 + struct SECTION_STRUCT* end = (struct SECTION_STRUCT*) (&cmds[p + lc->cmdsize]); 213 + 214 + while (sect < end) 215 + { 216 + if (strncmp(sect->sectname, "__all_image_info", 16) == 0) 217 + { 218 + lr->dyld_all_image_location = slide + sect->addr; 219 + lr->dyld_all_image_size = sect->size; 220 + break; 221 + } 222 + sect++; 223 + } 224 + } 225 + break; 226 + } 227 + case LC_UNIXTHREAD: 228 + { 229 + #ifdef GEN_64BIT 230 + entryPoint = ((uint64_t*) lc)[18]; 231 + #endif 232 + #ifdef GEN_32BIT 233 + entryPoint = ((uint32_t*) lc)[14]; 234 + #endif 235 + entryPoint += slide; 236 + break; 237 + } 238 + case LC_LOAD_DYLINKER: 239 + { 240 + if (header.filetype != MH_EXECUTE) 241 + { 242 + // dylinker can't reference another dylinker 243 + fprintf(stderr, "Dynamic linker can't reference another dynamic linker\n"); 244 + exit(1); 245 + } 246 + 247 + struct dylinker_command* dy = (struct dylinker_command*) lc; 248 + char* path; 249 + size_t length; 250 + static char path_buffer[4096]; 251 + 252 + if (lr->root_path != NULL) 253 + { 254 + const size_t root_len = strlen(lr->root_path); 255 + const size_t linker_len = dy->cmdsize - dy->name.offset; 256 + 257 + length = linker_len + root_len; 258 + if (length > sizeof(path_buffer) - 1) { 259 + fprintf(stderr, "Dynamic loader path too long"); 260 + exit(1); 261 + } 262 + path = path_buffer; 263 + 264 + // Concat root path and linker path 265 + memcpy(path, lr->root_path, root_len); 266 + memcpy(path + root_len, ((char*) dy) + dy->name.offset, linker_len); 267 + path[length] = '\0'; 268 + } 269 + 270 + if (path == NULL) 271 + { 272 + length = dy->cmdsize - dy->name.offset; 273 + if (length > sizeof(path_buffer) - 1) { 274 + fprintf(stderr, "Dynamic loader path too long"); 275 + exit(1); 276 + } 277 + path = path_buffer; 278 + 279 + memcpy(path, ((char*) dy) + dy->name.offset, length); 280 + path[length] = '\0'; 281 + } 282 + 283 + if (path == NULL) 284 + { 285 + fprintf(stderr, "Failed to load dynamic linker for executable\n"); 286 + exit(1); 287 + } 288 + 289 + load(path, header.cputype, true, NULL, lr); 290 + 291 + break; 292 + } 293 + case LC_MAIN: 294 + { 295 + struct entry_point_command* ee = (struct entry_point_command*) lc; 296 + if (ee->stacksize > lr->stack_size) 297 + lr->stack_size = ee->stacksize; 298 + break; 299 + } 300 + case LC_UUID: 301 + { 302 + if (header.filetype == MH_EXECUTE) 303 + { 304 + struct uuid_command* ue = (struct uuid_command*) lc; 305 + memcpy(lr->uuid, ue->uuid, sizeof(ue->uuid)); 306 + } 307 + break; 308 + } 309 + } 310 + 311 + p += lc->cmdsize; 312 + } 313 + 314 + if (header.filetype == MH_EXECUTE) 315 + lr->mh = (uintptr_t) mappedHeader; 316 + if (entryPoint && !lr->entry_point) 317 + lr->entry_point = entryPoint; 318 + 319 + if (tmp_map_base) 320 + munmap(tmp_map_base, PAGE_ROUNDUP(sizeof(header) + header.sizeofcmds)); 321 + } 322 + 323 + 324 + #undef FUNCTION_NAME 325 + #undef SEGMENT_STRUCT 326 + #undef SEGMENT_COMMAND 327 + #undef MACH_HEADER_STRUCT 328 + #undef SECTION_STRUCT 329 + #undef MAP_EXTRA 330 +
+31
src/startup/mldr/loader.h
··· 1 + #ifndef _MLDR_LOADER_H_ 2 + #define _MLDR_LOADER_H_ 3 + 4 + #include <stdint.h> 5 + #include <stdbool.h> 6 + #include <stddef.h> 7 + 8 + struct load_results { 9 + unsigned long mh; 10 + unsigned long entry_point; 11 + unsigned long stack_size; 12 + unsigned long dyld_all_image_location; 13 + unsigned long dyld_all_image_size; 14 + uint8_t uuid[16]; 15 + 16 + unsigned long vm_addr_max; 17 + bool _32on64; 18 + unsigned long base; 19 + uint32_t bprefs[4]; 20 + char* root_path; 21 + unsigned long stack_top; 22 + char* socket_path; 23 + int kernfd; 24 + 25 + size_t argc; 26 + size_t envc; 27 + char** argv; 28 + char** envp; 29 + }; 30 + 31 + #endif // _MLDR_LOADER_H_
+526
src/startup/mldr/mldr.c
··· 1 + /* 2 + This file is part of Darling. 3 + 4 + Copyright (C) 2017 Lubos Dolezel 5 + 6 + Darling is free software: you can redistribute it and/or modify 7 + it under the terms of the GNU General Public License as published by 8 + the Free Software Foundation, either version 3 of the License, or 9 + (at your option) any later version. 10 + 11 + Darling is distributed in the hope that it will be useful, 12 + but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + GNU General Public License for more details. 15 + 16 + You should have received a copy of the GNU General Public License 17 + along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + 20 + #include <sys/types.h> 21 + #include <sys/stat.h> 22 + #include <fcntl.h> 23 + #include <sys/mman.h> 24 + #include <unistd.h> 25 + #include <stdio.h> 26 + #include <string.h> 27 + #include <errno.h> 28 + #include <stdlib.h> 29 + #include <stdint.h> 30 + #include <stdbool.h> 31 + #include <mach-o/loader.h> 32 + #include <mach-o/fat.h> 33 + #include <dlfcn.h> 34 + #include <endian.h> 35 + #include "commpage.h" 36 + #include "loader.h" 37 + #include <sys/resource.h> 38 + #include <sys/prctl.h> 39 + #include <sys/socket.h> 40 + #include <sys/un.h> 41 + #include <darlingserver/rpc.h> 42 + 43 + #ifndef PAGE_SIZE 44 + # define PAGE_SIZE 4096 45 + #endif 46 + #define PAGE_ALIGN(x) (x & ~(PAGE_SIZE-1)) 47 + 48 + static const char* dyld_path = INSTALL_PREFIX "/libexec/usr/lib/dyld"; 49 + 50 + struct sockaddr_un __dserver_socket_address_data = { 51 + .sun_family = AF_UNIX, 52 + .sun_path = "\0", 53 + }; 54 + 55 + int __dserver_main_thread_socket_fd = -1; 56 + 57 + // The idea of mldr is to load dyld_path into memory and set up the stack 58 + // as described in dyldStartup.S. 59 + // After that, we pass control over to dyld. 60 + // 61 + // Additionally, mldr providers access to native platforms libdl.so APIs (ELF loader). 62 + 63 + #ifdef __x86_64__ 64 + static void load64(int fd, bool expect_dylinker, struct load_results* lr); 65 + static void reexec32(char** argv); 66 + #endif 67 + static void load32(int fd, bool expect_dylinker, struct load_results* lr); 68 + static void load_fat(int fd, cpu_type_t cpu, bool expect_dylinker, char** argv, struct load_results* lr); 69 + static void load(const char* path, cpu_type_t cpu, bool expect_dylinker, char** argv, struct load_results* lr); 70 + static int native_prot(int prot); 71 + static void setup_space(struct load_results* lr, bool is_64_bit); 72 + static void process_special_env(struct load_results* lr); 73 + static void start_thread(struct load_results* lr); 74 + #ifdef __x86_64__ 75 + static void setup_stack64(const char* filepath, struct load_results* lr); 76 + #endif 77 + static void setup_stack32(const char* filepath, struct load_results* lr); 78 + 79 + // UUID of the main executable 80 + uint8_t exe_uuid[16]; 81 + 82 + // Data for TASK_DYLD_INFO 83 + uintptr_t dyld_all_image_location; 84 + size_t dyld_all_image_size; 85 + 86 + static uint32_t stack_size = 0; 87 + 88 + int main(int argc, char** argv, char** envp) 89 + { 90 + void** sp; 91 + int pushCount = 0; 92 + char *filename, *p = NULL; 93 + struct load_results lr = {0}; 94 + 95 + lr.kernfd = -1; 96 + lr.argc = argc; 97 + lr.argv = argv; 98 + 99 + while (envp[lr.envc] != NULL) { 100 + ++lr.envc; 101 + } 102 + lr.envp = envp; 103 + 104 + // sys_execve() passes the original file path appended to the mldr path in argv[0]. 105 + if (argc > 0) 106 + p = strchr(argv[0], '!'); 107 + 108 + if (argc <= 1) 109 + { 110 + if (p == NULL) { 111 + fprintf(stderr, "mldr is part of Darling. It is not to be executed directly.\n"); 112 + return 1; 113 + } 114 + else 115 + { 116 + fprintf(stderr, "mldr: warning: Executing with no argv[0]. Continuing anyway, but this is probably a bug.\n"); 117 + } 118 + } 119 + 120 + if (p != NULL) 121 + { 122 + filename = (char*) __builtin_alloca(strlen(argv[0])+1); 123 + strcpy(filename, p + 1); 124 + } 125 + else 126 + { 127 + filename = (char*) __builtin_alloca(strlen(argv[1])+1); 128 + strcpy(filename, argv[1]); 129 + } 130 + 131 + #ifdef __i386__ 132 + load(filename, CPU_TYPE_X86, false, argv, &lr); // accept i386 only 133 + #else 134 + load(filename, 0, false, argv, &lr); 135 + #endif 136 + 137 + // this was previously necessary when we were loading the binary from the LKM 138 + // (presumably because the break was detected incorrectly) 139 + // but this shouldn't be necessary for loading Mach-O's from userspace (the heap space should already be set up properly). 140 + // see https://github.com/darlinghq/darling/issues/469 for the issue this originally fixed in the LKM 141 + #if 0 142 + if (prctl(PR_SET_MM, PR_SET_MM_BRK, PAGE_ALIGN(lr.vm_addr_max), 0, 0) < 0) { 143 + fprintf(stderr, "Failed to set BRK value\n"); 144 + return 1; 145 + } 146 + 147 + if (prctl(PR_SET_MM, PR_SET_MM_START_BRK, PAGE_ALIGN(lr.vm_addr_max), 0, 0) < 0) { 148 + fprintf(stderr, "Failed to set BRK start\n"); 149 + return 1; 150 + } 151 + #endif 152 + 153 + if (prctl(PR_SET_MM, PR_SET_MM_START_STACK, lr.stack_top, 0, 0) < 0) { 154 + fprintf(stderr, "Failed to set stack start\n"); 155 + return 1; 156 + } 157 + 158 + // adjust argv (remove mldr's argv[0]) 159 + --lr.argc; 160 + for (size_t i = 0; i < lr.argc; ++i) { 161 + lr.argv[i] = lr.argv[i + 1]; 162 + } 163 + lr.argv[lr.argc] = NULL; 164 + 165 + if (lr._32on64) 166 + setup_stack32(filename, &lr); 167 + else 168 + #ifdef __x86_64__ 169 + setup_stack64(filename, &lr); 170 + #elif __aarch64__ 171 + #error TODO: aarch64 172 + #else 173 + abort(); 174 + #endif 175 + 176 + // TODO: tell darlingserver about our dyld info 177 + 178 + start_thread(&lr); 179 + 180 + __builtin_unreachable(); 181 + } 182 + 183 + void load(const char* path, cpu_type_t forced_arch, bool expect_dylinker, char** argv, struct load_results* lr) 184 + { 185 + int fd; 186 + uint32_t magic; 187 + 188 + fd = open(path, O_RDONLY); 189 + if (fd == -1) 190 + { 191 + fprintf(stderr, "Cannot open %s: %s\n", path, strerror(errno)); 192 + exit(1); 193 + } 194 + 195 + // We need to read argv[1] and detect whether it's a 32 or 64-bit application. 196 + // Then load the appropriate version of dyld from the fat file. 197 + // In case the to-be-executed executable contains both, we prefer the 64-bit version, 198 + // unless a special property has been passed to sys_posix_spawn() to force the 32-bit 199 + // version. See posix_spawnattr_setbinpref_np(). 200 + 201 + if (read(fd, &magic, sizeof(magic)) != sizeof(magic)) 202 + { 203 + fprintf(stderr, "Cannot read the file header of %s.\n", path); 204 + exit(1); 205 + } 206 + 207 + if (magic == MH_MAGIC_64 || magic == MH_CIGAM_64) 208 + { 209 + #ifdef __x86_64__ 210 + lseek(fd, 0, SEEK_SET); 211 + load64(fd, expect_dylinker, lr); 212 + #else 213 + abort(); 214 + #endif 215 + } 216 + else if (magic == MH_MAGIC || magic == MH_CIGAM) 217 + { 218 + #if !__x86_64__ 219 + lseek(fd, 0, SEEK_SET); 220 + load32(fd, expect_dylinker, lr); 221 + #else 222 + // Re-run self as mldr32 223 + reexec32(argv); 224 + #endif 225 + } 226 + else if (magic == FAT_MAGIC || magic == FAT_CIGAM) 227 + { 228 + lseek(fd, 0, SEEK_SET); 229 + load_fat(fd, forced_arch, expect_dylinker, argv, lr); 230 + } 231 + else 232 + { 233 + fprintf(stderr, "Unknown file format: %s.\n", path); 234 + exit(1); 235 + } 236 + 237 + close(fd); 238 + } 239 + 240 + static void load_fat(int fd, cpu_type_t forced_arch, bool expect_dylinker, char** argv, struct load_results* lr) { 241 + struct fat_header fhdr; 242 + struct fat_arch best_arch = {0}; 243 + int bpref_index = -1; 244 + 245 + best_arch.cputype = CPU_TYPE_ANY; 246 + 247 + if (read(fd, &fhdr, sizeof(fhdr)) != sizeof(fhdr)) 248 + { 249 + fprintf(stderr, "Cannot read fat file header.\n"); 250 + exit(1); 251 + } 252 + 253 + const bool swap = fhdr.magic == FAT_CIGAM; 254 + 255 + #define SWAP32(x) x = __bswap_32(x) 256 + 257 + if (swap) 258 + SWAP32(fhdr.nfat_arch); 259 + 260 + uint32_t i; 261 + for (i = 0; i < fhdr.nfat_arch; i++) 262 + { 263 + struct fat_arch arch; 264 + 265 + if (read(fd, &arch, sizeof(arch)) != sizeof(arch)) 266 + { 267 + fprintf(stderr, "Cannot read fat_arch header.\n"); 268 + exit(1); 269 + } 270 + 271 + if (swap) 272 + { 273 + SWAP32(arch.cputype); 274 + SWAP32(arch.cpusubtype); 275 + SWAP32(arch.offset); 276 + SWAP32(arch.size); 277 + SWAP32(arch.align); 278 + } 279 + 280 + if (!forced_arch) 281 + { 282 + int j; 283 + for (j = 0; j < 4; j++) 284 + { 285 + if (lr->bprefs[j] && arch.cputype == lr->bprefs[j]) 286 + { 287 + if (bpref_index == -1 || bpref_index > j) 288 + { 289 + best_arch = arch; 290 + bpref_index = j; 291 + break; 292 + } 293 + } 294 + } 295 + 296 + if (bpref_index == -1) 297 + { 298 + #if defined(__x86_64__) 299 + if (arch.cputype == CPU_TYPE_X86_64) 300 + best_arch = arch; 301 + else if (best_arch.cputype == CPU_TYPE_ANY && arch.cputype == CPU_TYPE_X86) 302 + best_arch = arch; 303 + #elif defined(__i386__) 304 + if (arch.cputype == CPU_TYPE_X86) 305 + best_arch = arch; 306 + #elif defined (__aarch64__) 307 + #error TODO: arm 308 + #else 309 + #error Unsupported CPU architecture 310 + #endif 311 + } 312 + } 313 + else 314 + { 315 + if (arch.cputype == forced_arch) 316 + best_arch = arch; 317 + } 318 + } 319 + 320 + if (best_arch.cputype == CPU_TYPE_ANY) 321 + { 322 + fprintf(stderr, "No supported architecture found in fat binary.\n"); 323 + exit(1); 324 + } 325 + 326 + if (lseek(fd, best_arch.offset, SEEK_SET) == -1) 327 + { 328 + fprintf(stderr, "Cannot seek to selected arch in fat binary.\n"); 329 + exit(1); 330 + } 331 + 332 + if (best_arch.cputype & CPU_ARCH_ABI64) { 333 + #ifdef __x86_64__ 334 + load64(fd, expect_dylinker, lr); 335 + #elif __aarch64__ 336 + #error TODO: aarch64 337 + #else 338 + abort(); 339 + #endif 340 + } else { 341 + #if !__x86_64__ 342 + load32(fd, expect_dylinker, lr); 343 + #else 344 + // Re-run self as mldr32 345 + reexec32(argv); 346 + #endif 347 + } 348 + }; 349 + 350 + #ifdef __x86_64__ 351 + #define GEN_64BIT 352 + #include "loader.c" 353 + #include "stack.c" 354 + #undef GEN_64BIT 355 + #endif 356 + 357 + #define GEN_32BIT 358 + #include "loader.c" 359 + #include "stack.c" 360 + #undef GEN_32BIT 361 + 362 + int native_prot(int prot) 363 + { 364 + int protOut = 0; 365 + 366 + if (prot & VM_PROT_READ) 367 + protOut |= PROT_READ; 368 + if (prot & VM_PROT_WRITE) 369 + protOut |= PROT_WRITE; 370 + if (prot & VM_PROT_EXECUTE) 371 + protOut |= PROT_EXEC; 372 + 373 + return protOut; 374 + } 375 + 376 + static void reexec32(char** argv) 377 + { 378 + char selfpath[1024]; 379 + ssize_t len; 380 + 381 + len = readlink("/proc/self/exe", selfpath, sizeof(selfpath)-3); 382 + if (len == -1) 383 + { 384 + perror("Cannot readlink /proc/self/exe"); 385 + abort(); 386 + } 387 + 388 + selfpath[len] = '\0'; 389 + strcat(selfpath, "32"); 390 + 391 + execv(selfpath, argv); 392 + 393 + perror("Cannot re-execute as 32-bit process"); 394 + abort(); 395 + } 396 + 397 + // Given that there's no proper way of passing special parameters to the binary loader 398 + // via execve(), we must do this via env variables 399 + static void process_special_env(struct load_results* lr) { 400 + const char* str; 401 + static char root_path[4096]; 402 + 403 + lr->bprefs[0] = lr->bprefs[1] = lr->bprefs[2] = lr->bprefs[3] = 0; 404 + str = getenv("__mldr_bprefs"); 405 + 406 + if (str != NULL) { 407 + sscanf(str, "%x,%x,%x,%x", &lr->bprefs[0], &lr->bprefs[1], &lr->bprefs[2], &lr->bprefs[3]); 408 + unsetenv("__mldr_bprefs"); 409 + } 410 + 411 + str = getenv("__mldr_sockpath"); 412 + 413 + if (str != NULL) { 414 + if (strlen(str) > sizeof(__dserver_socket_address_data.sun_path) - 1) { 415 + fprintf(stderr, "darlingserver socket path is too long\n"); 416 + exit(1); 417 + } 418 + strncpy(__dserver_socket_address_data.sun_path, str, sizeof(__dserver_socket_address_data.sun_path) - 1); 419 + __dserver_socket_address_data.sun_path[sizeof(__dserver_socket_address_data.sun_path) - 1] = '\0'; 420 + unsetenv("__mldr_sockpath"); 421 + 422 + lr->socket_path = __dserver_socket_address_data.sun_path; 423 + } 424 + 425 + str = getenv("DYLD_ROOT_PATH"); 426 + 427 + if (str != NULL && lr->root_path == NULL) { 428 + strncpy(root_path, str, sizeof(root_path) - 1); 429 + root_path[sizeof(root_path) - 1] = '\0'; 430 + lr->root_path = root_path; 431 + } 432 + }; 433 + 434 + static void setup_space(struct load_results* lr, bool is_64_bit) { 435 + commpage_setup(is_64_bit); 436 + 437 + // Using the default stack top would cause the stack to be placed just above the commpage 438 + // and would collide with it eventually. 439 + // Instead, we manually allocate a new stack below the commpage. 440 + #if __x86_64__ 441 + lr->stack_top = commpage_address(true); 442 + #elif __i386__ 443 + lr->stack_top = commpage_address(false); 444 + #else 445 + #error Unsupported architecture 446 + #endif 447 + 448 + struct rlimit limit; 449 + getrlimit(RLIMIT_STACK, &limit); 450 + // allocate a few pages 16 pages if it's less than the limit; otherwise, allocate the limit 451 + unsigned long size = PAGE_SIZE * 16; 452 + if (limit.rlim_cur != RLIM_INFINITY && limit.rlim_cur < size) { 453 + size = limit.rlim_cur; 454 + } 455 + 456 + if (mmap((void*)lr->stack_top, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_GROWSDOWN, -1, 0) == MAP_FAILED) { 457 + fprintf(stderr, "Failed to allocate stack of %lu bytes: %d (%s)\n", size, errno, strerror(errno)); 458 + exit(1); 459 + } 460 + 461 + process_special_env(lr); 462 + 463 + lr->kernfd = socket(AF_UNIX, SOCK_DGRAM, 0); 464 + if (lr->kernfd < 0) { 465 + fprintf(stderr, "Failed to create socket\n"); 466 + exit(1); 467 + } 468 + 469 + sa_family_t family = AF_UNIX; 470 + if (bind(lr->kernfd, (const struct sockaddr*)&family, sizeof(family)) < 0) { 471 + fprintf(stderr, "Failed to autobind socket\n"); 472 + exit(1); 473 + } 474 + 475 + __dserver_main_thread_socket_fd = lr->kernfd; 476 + 477 + if (dserver_rpc_checkin() < 0) { 478 + fprintf(stderr, "Failed to checkin with darlingserver\n"); 479 + exit(1); 480 + } 481 + 482 + static char vchroot_buffer[4096]; 483 + size_t vchroot_path_length = 0; 484 + 485 + if (dserver_rpc_vchroot_path(vchroot_buffer, sizeof(vchroot_buffer), &vchroot_path_length) < 0) { 486 + fprintf(stderr, "Failed to retrieve vchroot path from darlingserver\n"); 487 + exit(1); 488 + } 489 + 490 + if (vchroot_path_length > 0) { 491 + lr->root_path = vchroot_buffer; 492 + } 493 + }; 494 + 495 + static void start_thread(struct load_results* lr) { 496 + #ifdef __x86_64__ 497 + __asm__ volatile( 498 + "mov %1, %%rsp\n" 499 + "jmpq *%0" 500 + :: 501 + "m"(lr->entry_point), 502 + "r"(lr->stack_top) 503 + : 504 + ); 505 + #elif defined(__i386__) 506 + __asm__ volatile( 507 + "mov %1, %%esp\n" 508 + "jmp *%0" 509 + :: 510 + "m"(lr->entry_point), 511 + "r"(lr->stack_top) 512 + : 513 + ); 514 + #elif defined(__arm__) 515 + __asm__ volatile( 516 + "mov sp, %1\n" 517 + "bx %0" 518 + :: 519 + "r"(lr->entry_point), 520 + "r"(lr->stack_top) 521 + : 522 + ); 523 + #else 524 + # error Unsupported platform! 525 + #endif 526 + };
+58
src/startup/mldr/resources/dserver-rpc-defs.h
··· 1 + #include <sys/socket.h> 2 + #include <sys/un.h> 3 + #include <unistd.h> 4 + #include <sys/syscall.h> 5 + #include <stdlib.h> 6 + #include <string.h> 7 + #include <errno.h> 8 + 9 + #define dserver_rpc_hooks_msghdr_t struct msghdr 10 + #define dserver_rpc_hooks_iovec_t struct iovec 11 + #define dserver_rpc_hooks_cmsghdr_t struct cmsghdr 12 + #define DSERVER_RPC_HOOKS_CMSG_SPACE CMSG_SPACE 13 + #define DSERVER_RPC_HOOKS_CMSG_FIRSTHDR CMSG_FIRSTHDR 14 + #define DSERVER_RPC_HOOKS_SOL_SOCKET SOL_SOCKET 15 + #define DSERVER_RPC_HOOKS_SCM_RIGHTS SCM_RIGHTS 16 + #define DSERVER_RPC_HOOKS_CMSG_LEN CMSG_LEN 17 + #define DSERVER_RPC_HOOKS_CMSG_DATA CMSG_DATA 18 + #define DSERVER_RPC_HOOKS_ATTRIBUTE static 19 + 20 + #define dserver_rpc_hooks_get_pid getpid 21 + 22 + #define dserver_rpc_hooks_get_tid() ((pid_t)syscall(SYS_gettid)) 23 + 24 + extern struct sockaddr_un __dserver_socket_address_data; 25 + 26 + #define dserver_rpc_hooks_get_server_address() ((void*)&__dserver_socket_address_data) 27 + 28 + #define dserver_rpc_hooks_get_server_address_length() sizeof(__dserver_socket_address_data) 29 + 30 + #define dserver_rpc_hooks_memcpy memcpy 31 + 32 + static long int dserver_rpc_hooks_send_message(int socket, const dserver_rpc_hooks_msghdr_t* message) { 33 + ssize_t ret = sendmsg(socket, message, 0); 34 + if (ret < 0) { 35 + return -errno; 36 + } 37 + return ret; 38 + }; 39 + 40 + static long int dserver_rpc_hooks_receive_message(int socket, dserver_rpc_hooks_msghdr_t* out_message) { 41 + ssize_t ret = recvmsg(socket, out_message, 0); 42 + if (ret < 0) { 43 + return -errno; 44 + } 45 + return ret; 46 + }; 47 + 48 + #define dserver_rpc_hooks_get_bad_message_status() EBADMSG 49 + 50 + #define dserver_rpc_hooks_get_communication_error_status() ECOMM 51 + 52 + #define dserver_rpc_hooks_get_broken_pipe_status() EPIPE 53 + 54 + #define dserver_rpc_hooks_close_fd close 55 + 56 + extern int __dserver_main_thread_socket_fd; 57 + 58 + #define dserver_rpc_hooks_get_socket() __dserver_main_thread_socket_fd
+260
src/startup/mldr/stack.c
··· 1 + /* 2 + * This file is part of Darling. 3 + * Copyright (C) 2021 Darling developers 4 + * 5 + * Originally part of the Darling Mach Linux Kernel Module 6 + * Copyright (C) 2017 Lubos Dolezel 7 + * 8 + * This program is free software; you can redistribute it and/or 9 + * modify it under the terms of the GNU General Public License 10 + * as published by the Free Software Foundation; either version 2 11 + * of the License, or (at your option) any later version. 12 + * 13 + * This program is distributed in the hope that it will be useful, 14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 + * GNU General Public License for more details. 17 + * 18 + * You should have received a copy of the GNU General Public License 19 + * along with this program; if not, write to the Free Software 20 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 + */ 22 + 23 + #include <stddef.h> 24 + #include <string.h> 25 + #include <unistd.h> 26 + #include <stdlib.h> 27 + #include <stdio.h> 28 + #include "loader.h" 29 + #include <darling-config.h> 30 + #include "elfcalls/elfcalls.h" 31 + #include <sys/prctl.h> 32 + 33 + #if defined(GEN_64BIT) 34 + #define FUNCTION_NAME setup_stack64 35 + #define user_long_t unsigned long 36 + #elif defined(GEN_32BIT) 37 + #define FUNCTION_NAME setup_stack32 38 + #define user_long_t unsigned int 39 + #else 40 + #error See above 41 + #endif 42 + 43 + #define __user 44 + 45 + #define EXECUTABLE_PATH "executable_path=" 46 + 47 + #define __put_user(value, pointer) ({ \ 48 + __typeof__(value) _tmpval = (value); \ 49 + memcpy((pointer), &_tmpval, sizeof(_tmpval)); \ 50 + 0; \ 51 + }) 52 + 53 + void elfcalls_make(struct elf_calls* calls); 54 + 55 + static struct elf_calls _elfcalls; 56 + 57 + void FUNCTION_NAME(const char* filepath, struct load_results* lr) 58 + { 59 + int err = 0; 60 + // unsigned char rand_bytes[16]; 61 + char *executable_path; 62 + static char executable_buf[4096]; 63 + user_long_t __user* argv; 64 + user_long_t __user* envp; 65 + user_long_t __user* applep; 66 + user_long_t __user* sp; 67 + char __user* exepath_user; 68 + size_t exepath_len; 69 + char __user* kernfd_user; 70 + char kernfd[12]; 71 + char __user* elfcalls_user; 72 + char elfcalls[27]; 73 + char __user* applep_contents[4]; 74 + char* vchroot_path = NULL; 75 + 76 + #define user_long_count(_val) (((_val) + (sizeof(user_long_t) - 1)) / sizeof(user_long_t)) 77 + 78 + elfcalls_make(&_elfcalls); 79 + 80 + // Produce executable_path=... for applep 81 + executable_buf[sizeof(executable_buf) - 1] = '\0'; 82 + strncpy(executable_buf, filepath, 4096); 83 + if (executable_buf[sizeof(executable_buf) - 1] != '\0') 84 + { 85 + fprintf(stderr, "File path was too big\n"); 86 + exit(1); 87 + } 88 + 89 + executable_path = executable_buf; 90 + 91 + // TODO: ask darlingserver for our vchroot path 92 + 93 + if (vchroot_path != NULL) 94 + { 95 + const size_t vchroot_len = strlen(vchroot_path); 96 + exepath_len = strlen(executable_path); 97 + 98 + if (strncmp(executable_path, vchroot_path, vchroot_len) == 0) 99 + { 100 + memmove(executable_buf, executable_path + vchroot_len, exepath_len - vchroot_len + 1); 101 + } 102 + else 103 + { 104 + memcpy(executable_buf, SYSTEM_ROOT, sizeof(SYSTEM_ROOT) - 1); 105 + memmove(executable_buf + sizeof(SYSTEM_ROOT) - 1, executable_path, exepath_len + 1); 106 + } 107 + executable_path = executable_buf; 108 + } 109 + 110 + // printk(KERN_NOTICE "Stack top: %p\n", bprm->p); 111 + exepath_len = strlen(executable_path); 112 + sp = (user_long_t*) (lr->stack_top & ~(sizeof(user_long_t)-1)); 113 + 114 + // 1 pointer for the mach header 115 + // 1 user_long_t for the argument count 116 + // `argc`-count pointers for arguments (+1 for NULL) 117 + // `envc`-count pointers for env vars (+1 for NULL) 118 + // `sizeof(applep_contents) / sizeof(*applep_contents)`-count pointers for applep arguments (already includes NULL) 119 + // space for exepath, kernfd, and elfcalls 120 + sp -= 1 + 1 + (lr->argc + 1) + (lr->envc + 1) + (sizeof(applep_contents) / sizeof(*applep_contents)) + user_long_count(exepath_len + sizeof(EXECUTABLE_PATH) + sizeof(kernfd) + sizeof(elfcalls)); 121 + 122 + exepath_user = (char __user*) lr->stack_top - exepath_len - sizeof(EXECUTABLE_PATH); 123 + memcpy(exepath_user, EXECUTABLE_PATH, sizeof(EXECUTABLE_PATH)-1); 124 + memcpy(exepath_user + sizeof(EXECUTABLE_PATH)-1, executable_path, exepath_len + 1); 125 + 126 + snprintf(kernfd, sizeof(kernfd), "kernfd=%d", lr->kernfd); 127 + kernfd_user = exepath_user - sizeof(kernfd); 128 + memcpy(kernfd_user, kernfd, sizeof(kernfd)); 129 + 130 + #if defined(GEN_64BIT) 131 + #define POINTER_FORMAT "%lx" 132 + #elif defined(GEN_32BIT) 133 + #define POINTER_FORMAT "%x" 134 + #endif 135 + 136 + snprintf(elfcalls, sizeof(elfcalls), "elf_calls=" POINTER_FORMAT, (unsigned long)(uintptr_t)&_elfcalls); 137 + elfcalls_user = kernfd_user - sizeof(elfcalls); 138 + memcpy(elfcalls_user, elfcalls, sizeof(elfcalls)); 139 + 140 + applep_contents[0] = exepath_user; 141 + applep_contents[1] = kernfd_user; 142 + applep_contents[2] = elfcalls_user; 143 + applep_contents[3] = NULL; 144 + 145 + lr->stack_top = (unsigned long) sp; 146 + 147 + // XXX: skip this for static executables, but we don't support them anyway... 148 + if (__put_user((user_long_t) lr->mh, sp++)) 149 + { 150 + fprintf(stderr, "Failed to copy mach header address to stack\n"); 151 + exit(1); 152 + } 153 + if (__put_user((user_long_t) lr->argc, sp++)) 154 + { 155 + fprintf(stderr, "Failed to copy argument count to stack\n"); 156 + exit(1); 157 + } 158 + 159 + // Fill in argv pointers 160 + argv = sp; 161 + if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, argv, 0, 0) < 0) { 162 + // maybe arg_end was behind arg_start; try moving it first 163 + if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, argv, 0, 0) < 0) { 164 + fprintf(stderr, "Failed to set arg end\n"); 165 + exit(1); 166 + } 167 + if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, argv, 0, 0) < 0) { 168 + fprintf(stderr, "Failed to set arg start\n"); 169 + exit(1); 170 + } 171 + } 172 + for (int i = 0; i < lr->argc; ++i) 173 + { 174 + if (!lr->argv[i]) { 175 + lr->argc = i; 176 + break; 177 + } 178 + if (__put_user((user_long_t) lr->argv[i], argv++)) 179 + { 180 + fprintf(stderr, "Failed to copy an argument pointer to stack\n"); 181 + exit(1); 182 + } 183 + } 184 + if (__put_user((user_long_t) 0, argv++)) 185 + { 186 + fprintf(stderr, "Failed to null-terminate the argument pointer array\n"); 187 + exit(1); 188 + } 189 + if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, argv, 0, 0) < 0) { 190 + fprintf(stderr, "Failed to set arg end\n"); 191 + exit(1); 192 + } 193 + 194 + // Fill in envp pointers 195 + envp = argv; 196 + if (prctl(PR_SET_MM, PR_SET_MM_ENV_START, envp, 0, 0) < 0) { 197 + // maybe env_end was behind env_start; try moving it first 198 + if (prctl(PR_SET_MM, PR_SET_MM_ENV_END, envp, 0, 0) < 0) { 199 + fprintf(stderr, "Failed to set env end\n"); 200 + exit(1); 201 + } 202 + if (prctl(PR_SET_MM, PR_SET_MM_ENV_START, envp, 0, 0) < 0) { 203 + fprintf(stderr, "Failed to set env start\n"); 204 + exit(1); 205 + } 206 + } 207 + for (int i = 0; i < lr->envc; ++i) 208 + { 209 + if (!lr->envp[i]) { 210 + lr->envc = i; 211 + break; 212 + } 213 + 214 + size_t len = strlen((void __user*) lr->envp[i]) + 1; 215 + 216 + // Don't pass these special env vars down to userland 217 + #define SKIP_VAR(_name) \ 218 + if (len > sizeof(_name) - 1 && strncmp(lr->envp[i], _name, sizeof(_name) - 1) == 0) { \ 219 + continue; \ 220 + } 221 + 222 + SKIP_VAR("__mldr_bprefs="); 223 + SKIP_VAR("__mldr_sockpath="); 224 + 225 + if (__put_user((user_long_t) lr->envp[i], envp++)) 226 + { 227 + fprintf(stderr, "Failed to copy an environment variable pointer to stack\n"); 228 + exit(1); 229 + } 230 + } 231 + if (__put_user((user_long_t) 0, envp++)) 232 + { 233 + fprintf(stderr, "Failed to null-terminate the environment variable pointer array\n"); 234 + exit(1); 235 + } 236 + if (prctl(PR_SET_MM, PR_SET_MM_ENV_END, envp, 0, 0) < 0) { 237 + fprintf(stderr, "Failed to set env end\n"); 238 + exit(1); 239 + } 240 + 241 + applep = envp; // envp is now at the end of env pointers 242 + 243 + for (int i = 0; i < sizeof(applep_contents)/sizeof(applep_contents[0]); i++) 244 + { 245 + if (__put_user((user_long_t)(unsigned long) applep_contents[i], applep++)) 246 + { 247 + fprintf(stderr, "Failed to copy an applep value to stack\n"); 248 + exit(1); 249 + } 250 + } 251 + 252 + // get_random_bytes(rand_bytes, sizeof(rand_bytes)); 253 + 254 + // TODO: produce stack_guard, e.g. stack_guard=0xcdd5c48c061b00fd (must contain 00 somewhere!) 255 + // TODO: produce malloc_entropy, e.g. malloc_entropy=0x9536cc569d9595cf,0x831942e402da316b 256 + // TODO: produce main_stack? 257 + } 258 + 259 + #undef FUNCTION_NAME 260 + #undef user_long_t