diff --git a/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/elfcalls/elfcalls.h b/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/elfcalls/elfcalls.h
index 8669bd680..8712f103f 120000
--- a/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/elfcalls/elfcalls.h
+++ b/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/elfcalls/elfcalls.h
@@ -1 +1 @@
-../../../../../../../../../src/libelfloader/native/elfcalls.h
\ No newline at end of file
+../../../../../../../../../src/startup/mldr/elfcalls/elfcalls.h
\ No newline at end of file
diff --git a/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/elfcalls/threads.h b/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/elfcalls/threads.h
index dc1fcc557..e349893f0 120000
--- a/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/elfcalls/threads.h
+++ b/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/elfcalls/threads.h
@@ -1 +1 @@
-../../../../../../../../../src/libelfloader/native/threads.h
\ No newline at end of file
+../../../../../../../../../src/startup/mldr/elfcalls/threads.h
\ No newline at end of file
diff --git a/cmake/setcap.cmake b/cmake/setcap.cmake
index d42b80f12..bd146ae9c 100644
--- a/cmake/setcap.cmake
+++ b/cmake/setcap.cmake
@@ -4,7 +4,7 @@ function(setcap file caps)
install(CODE "execute_process(
COMMAND
${SETCAP_EXECUTABLE}
- cap_sys_rawio+ep
+ ${caps}
${CMAKE_INSTALL_PREFIX}/${file}
RESULT_VARIABLE
_SETCAP_RESULT
diff --git a/cmake/wrap_elf.cmake b/cmake/wrap_elf.cmake
index 8dc2cc5d4..0ec024f38 100644
--- a/cmake/wrap_elf.cmake
+++ b/cmake/wrap_elf.cmake
@@ -21,9 +21,9 @@ function(wrap_elf name elfname)
endif (ARGC LESS 3)
set(DYLIB_INSTALL_NAME "${destination}/lib${name}.dylib")
- include_directories(${CMAKE_SOURCE_DIR}/src/libelfloader/native)
+ include_directories(${CMAKE_SOURCE_DIR}/src/startup/mldr/elfcalls)
add_darling_library(${name} SHARED ${CMAKE_CURRENT_BINARY_DIR}/${name}.c)
- target_link_libraries(${name} PRIVATE system elfloader)
+ target_link_libraries(${name} PRIVATE system)
make_fat(${name})
install(TARGETS ${name} DESTINATION libexec/darling/${destination})
endfunction(wrap_elf)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f81367baa..1c45efd1d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -10,7 +10,6 @@ include(mig)
include(pyc)
#add_subdirectory(external/xcbuild)
-add_subdirectory(libelfloader/native)
add_subdirectory(bsdln)
add_definitions(
@@ -44,6 +43,9 @@ include_directories(SYSTEM ${COMPILER_INC_PATH})
find_package(BISON)
find_package(FLEX)
+set(DARLING_SDK_RELATIVE_PATH "Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk")
+set(DARLING_ROOT_RELATIVE_TO_SDK "../../../../../..")
+
add_subdirectory(external/cctools-port/cctools/ld64/src)
add_subdirectory(external/cctools-port/cctools/ar)
#add_subdirectory(external/cctools-port/cctools/as)
@@ -55,6 +57,7 @@ add_subdirectory(external/cctools-port/cctools/misc)
add_subdirectory(buildtools)
add_subdirectory(libelfloader/wrapgen)
add_subdirectory(startup)
+add_subdirectory(darlingserver)
include_directories(${CMAKE_SOURCE_DIR}/basic-headers)
@@ -78,9 +81,6 @@ add_definitions(-target ${APPLE_TARGET_TRIPLET_PRIMARY})
include(darling_lib)
include(darling_static_lib)
-set(DARLING_SDK_RELATIVE_PATH "Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk")
-set(DARLING_ROOT_RELATIVE_TO_SDK "../../../../../..")
-
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/${DARLING_SDK_RELATIVE_PATH}/usr/include)
include_directories(
@@ -140,7 +140,6 @@ add_subdirectory(keymgr)
add_subdirectory(libcache)
add_subdirectory(ncurses)
add_subdirectory(libiconv)
-add_subdirectory(libelfloader)
add_subdirectory(duct/src)
add_subdirectory(libresolv)
add_subdirectory(external/corecrypto)
diff --git a/src/darlingserver/CMakeLists.txt b/src/darlingserver/CMakeLists.txt
new file mode 100644
index 000000000..dcd357505
--- /dev/null
+++ b/src/darlingserver/CMakeLists.txt
@@ -0,0 +1,60 @@
+project(darlingserver)
+
+cmake_minimum_required(VERSION 3.10)
+
+include_directories(
+ include
+ internal-include
+ ${CMAKE_CURRENT_BINARY_DIR}/include
+ ${CMAKE_CURRENT_BINARY_DIR}/internal-include
+)
+
+add_custom_command(
+ OUTPUT
+ ${CMAKE_CURRENT_BINARY_DIR}/include/darlingserver/rpc.h
+ ${CMAKE_CURRENT_BINARY_DIR}/internal-include/darlingserver/rpc.internal.h
+ ${CMAKE_CURRENT_BINARY_DIR}/src/rpc.c
+ COMMAND
+ ${CMAKE_CURRENT_SOURCE_DIR}/scripts/generate-rpc-wrappers.py
+ ${CMAKE_CURRENT_BINARY_DIR}/include/darlingserver/rpc.h
+ ${CMAKE_CURRENT_BINARY_DIR}/internal-include/darlingserver/rpc.internal.h
+ ${CMAKE_CURRENT_BINARY_DIR}/src/rpc.c
+ \"../include/darlingserver/rpc.h\"
+ VERBATIM
+ MAIN_DEPENDENCY
+ ${CMAKE_CURRENT_SOURCE_DIR}/scripts/generate-rpc-wrappers.py
+)
+
+add_custom_target(generate_dserver_rpc_wrappers
+ ALL
+ DEPENDS
+ ${CMAKE_CURRENT_BINARY_DIR}/include/darlingserver/rpc.h
+ ${CMAKE_CURRENT_BINARY_DIR}/internal-include/darlingserver/rpc.internal.h
+ ${CMAKE_CURRENT_BINARY_DIR}/src/rpc.c
+)
+
+add_executable(darlingserver
+ src/darlingserver.cpp
+ src/server.cpp
+ src/message.cpp
+ src/call.cpp
+ src/registry.cpp
+ src/logging.cpp
+ src/process.cpp
+ src/thread.cpp
+)
+
+add_dependencies(darlingserver
+ generate_dserver_rpc_wrappers
+)
+
+target_compile_options(darlingserver PRIVATE -pthread -std=c++17)
+target_link_options(darlingserver PRIVATE -pthread)
+
+install(TARGETS darlingserver DESTINATION bin)
+
+#file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/${DARLING_SDK_RELATIVE_PATH}/usr/include/darlingserver")
+#create_symlink(
+# "${DARLING_ROOT_RELATIVE_TO_SDK}/../../../src/darlingserver/include/darlingserver/rpc.h"
+# "${CMAKE_BINARY_DIR}/${DARLING_SDK_RELATIVE_PATH}/usr/include/darlingserver/rpc.h"
+#)
diff --git a/src/darlingserver/internal-include/darlingserver/call.hpp b/src/darlingserver/internal-include/darlingserver/call.hpp
new file mode 100644
index 000000000..cada9c488
--- /dev/null
+++ b/src/darlingserver/internal-include/darlingserver/call.hpp
@@ -0,0 +1,65 @@
+/**
+ * This file is part of Darling.
+ *
+ * Copyright (C) 2021 Darling developers
+ *
+ * Darling is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Darling is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Darling. If not, see .
+ */
+
+#ifndef _DARLINGSERVER_CALL_HPP_
+#define _DARLINGSERVER_CALL_HPP_
+
+#include
+#include
+
+#include
+#include
+
+#include
+
+namespace DarlingServer {
+ class CallWithReply;
+
+ class Call {
+ public:
+ enum class Number {
+ Invalid = dserver_callnum_invalid,
+ DSERVER_ENUM_VALUES
+ };
+
+ protected:
+ std::weak_ptr _thread;
+ MessageQueue& _replyQueue;
+ Address _replyAddress;
+
+ void _stopPending();
+
+ public:
+ Call(MessageQueue& replyQueue, std::shared_ptr thread, Address replyAddress);
+ virtual ~Call();
+
+ static std::shared_ptr callFromMessage(Message&& requestMessage, MessageQueue& replyQueue);
+
+ virtual Number number() const = 0;
+ std::shared_ptr thread() const;
+
+ virtual void processCall() = 0;
+
+ DSERVER_CLASS_DECLS;
+ };
+
+ DSERVER_CLASS_DEFS;
+};
+
+#endif // _DARLINGSERVER_CALL_HPP_
diff --git a/src/darlingserver/internal-include/darlingserver/logging.hpp b/src/darlingserver/internal-include/darlingserver/logging.hpp
new file mode 100644
index 000000000..72ecaa91e
--- /dev/null
+++ b/src/darlingserver/internal-include/darlingserver/logging.hpp
@@ -0,0 +1,88 @@
+/**
+ * This file is part of Darling.
+ *
+ * Copyright (C) 2021 Darling developers
+ *
+ * Darling is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Darling is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Darling. If not, see .
+ */
+
+#ifndef _DARLINGSERVER_LOGGING_HPP_
+#define _DARLINGSERVER_LOGGING_HPP_
+
+#include
+#include
+#include
+
+namespace DarlingServer {
+ static struct EndLog {} endLog;
+
+ class Log {
+ private:
+ std::string _category;
+
+ enum class Type {
+ Debug,
+ Info,
+ Warning,
+ Error,
+ };
+
+ void _log(Type type, std::string message);
+ static std::string _typeToString(Type type);
+
+ public:
+ Log(std::string category);
+
+ Log(const Log&) = delete;
+ Log& operator=(const Log&) = delete;
+ Log(Log&&) = delete;
+ Log& operator=(Log&&) = delete;
+
+ class Stream;
+ friend class Stream;
+
+ Stream debug();
+ Stream info();
+ Stream warning();
+ Stream error();
+ };
+
+ class Log::Stream {
+ friend class Log;
+
+ private:
+ Type _type;
+ Log& _log;
+ std::ostringstream _buffer;
+
+ Stream(Type type, Log& log);
+
+ public:
+ Stream(const Stream&) = delete;
+ Stream& operator=(const Stream&) = delete;
+ Stream(Stream&&) = delete;
+ Stream& operator=(Stream&&) = delete;
+
+ template
+ Stream& operator<<(T value) {
+ _buffer << value;
+ return *this;
+ };
+
+ template<>
+ Stream& operator<<(EndLog value);
+ };
+};
+
+#endif // _DARLINGSERVER_LOGGING_HPP_
diff --git a/src/darlingserver/internal-include/darlingserver/message.hpp b/src/darlingserver/internal-include/darlingserver/message.hpp
new file mode 100644
index 000000000..b49db3862
--- /dev/null
+++ b/src/darlingserver/internal-include/darlingserver/message.hpp
@@ -0,0 +1,170 @@
+/**
+ * This file is part of Darling.
+ *
+ * Copyright (C) 2021 Darling developers
+ *
+ * Darling is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Darling is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Darling. If not, see .
+ */
+
+#ifndef _DARLINGSERVER_MESSAGE_HPP_
+#define _DARLINGSERVER_MESSAGE_HPP_
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace DarlingServer {
+ class Address {
+ private:
+ struct sockaddr_un _address;
+ size_t _size;
+
+ public:
+ Address();
+ Address(const struct sockaddr_un& rawAddress, size_t addressSize = sizeof(struct sockaddr_un));
+
+ struct sockaddr_un& raw();
+ const struct sockaddr_un& raw() const;
+
+ size_t rawSize() const;
+ void setRawSize(size_t newRawSize);
+ };
+
+ /**
+ * A C++ wrapper for local (Unix) SOCK_DGRAM/SOCK_SEQPACKET socket messages, with support for file descriptors (SCM_RIGHTS) and credentials (SCM_CREDENTIALS).
+ */
+ class Message {
+ private:
+ struct msghdr _header;
+ struct iovec _dataDescriptor;
+ std::vector _buffer;
+ struct cmsghdr* _controlHeader = nullptr;
+ Address _socketAddress;
+
+ void _initWithOther(Message&&);
+ void _cleanupSelf();
+
+ struct cmsghdr* _credentialsHeader();
+ const struct cmsghdr* _credentialsHeader() const;
+ struct cmsghdr* _descriptorHeader();
+ const struct cmsghdr* _descriptorHeader() const;
+ size_t _descriptorSpace() const;
+
+ void _ensureCredentialsHeader();
+ void _ensureDescriptorHeader(size_t descriptorSpace);
+
+ public:
+ /**
+ * Default-initializes a Message.
+ *
+ * By default, the data buffer holds 256 bytes and the control data buffer has space for 4 descriptors.
+ *
+ * If this Message is being used to receive a message from a socket, you must preallocate
+ * the buffers for the recvmsg/recvmmsg call. The buffers are already preallocated to the
+ * sizes specified by in this constructor, however, if you wish to grow the data buffer,
+ * you can call pushData() with a null buffer pointer and a non-zero size. If you wish to
+ * grow the control data buffer, you can call pushDescriptor() with a descriptor value of `-1`.
+ */
+ Message(size_t bufferSpace = 256, size_t descriptorSpace = 4);
+ ~Message();
+
+ Message(Message&&);
+ Message& operator=(Message&&);
+
+ Message(const Message&) = delete;
+ Message& operator=(const Message&) = delete;
+
+ struct msghdr& rawHeader();
+ const struct msghdr& rawHeader() const;
+
+ std::vector& data();
+ const std::vector& data() const;
+
+ Address address() const;
+ void setAddress(Address address);
+
+ bool copyCredentialsOut(struct ucred& outputCredentials) const;
+ void copyCredentialsIn(const struct ucred& inputCredentials);
+
+ pid_t pid() const;
+ void setPID(pid_t pid);
+
+ uid_t uid() const;
+ void setUID(uid_t uid);
+
+ gid_t gid() const;
+ void setGID(gid_t gid);
+
+ /**
+ * Returns an array of all the descritors currently owned by this Message.
+ *
+ * Note that the descriptors are still owned by this Message during and after this call.
+ * Callers should NOT close the descriptors they receive in the returned vector.
+ *
+ * See extractDescriptor() for a method that will transfer ownership of a descriptor to the caller.
+ */
+ std::vector descriptors() const;
+
+ /**
+ * Acquires ownership of the given descriptor.
+ *
+ * Note that callers should NOT close the descriptor after a call to this method.
+ * This Message becomes the owner of the descriptor and will take care of closing it.
+ */
+ void pushDescriptor(int descriptor);
+
+ /**
+ * Extracts a single descriptor from this Message, transferring ownership of it to the caller.
+ *
+ * @returns The descriptor that was extracted, or `-1` if it was not found.
+ */
+ int extractDescriptor(int descriptor);
+
+ /**
+ * Like extractDescriptor(), but extracts it based on its index in the vector returned by descriptors().
+ */
+ int extractDescriptorAtIndex(size_t index);
+
+ /**
+ * Gives up ownership of the descriptors previously held by this Message and acquires ownership
+ * of the descriptors passed in the given vector.
+ *
+ * Note that this method does not return the old descriptors in any way,
+ * so if the caller wishes to know what they were, they must call descriptors() before calling this method.
+ */
+ void replaceDescriptors(const std::vector& newDescriptors);
+ };
+
+ /**
+ * A thread-safe Message queue with methods for sending and receiving messages.
+ */
+ class MessageQueue {
+ private:
+ std::deque _messages;
+ std::mutex _lock;
+
+ public:
+ void push(Message&& message);
+ std::optional pop();
+
+ void sendMany(int socket);
+ void receiveMany(int socket);
+ };
+};
+
+#endif // _DARLINGSERVER_MESSAGE_HPP_
diff --git a/src/darlingserver/internal-include/darlingserver/process.hpp b/src/darlingserver/internal-include/darlingserver/process.hpp
new file mode 100644
index 000000000..0c7e36d20
--- /dev/null
+++ b/src/darlingserver/internal-include/darlingserver/process.hpp
@@ -0,0 +1,76 @@
+/**
+ * This file is part of Darling.
+ *
+ * Copyright (C) 2021 Darling developers
+ *
+ * Darling is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Darling is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Darling. If not, see .
+ */
+
+#ifndef _DARLINGSERVER_PROCESS_HPP_
+#define _DARLINGSERVER_PROCESS_HPP_
+
+#include
+#include
+#include
+#include
+#include
+
+namespace DarlingServer {
+ class Thread;
+ class Server;
+
+ class Process {
+ friend class Thread;
+ friend class Server;
+
+ private:
+ pid_t _pid;
+ pid_t _nspid;
+ int _pidfd;
+ mutable std::shared_mutex _rwlock;
+ std::vector> _threads;
+ std::string _vchrootPath;
+
+ void _unregisterThreads();
+
+ public:
+ using ID = pid_t;
+ using NSID = ID;
+
+ Process(ID id, NSID nsid);
+ ~Process();
+
+ Process(const Process&) = delete;
+ Process& operator=(const Process&) = delete;
+ Process(Process&&) = delete;
+ Process& operator=(Process&&) = delete;
+
+ /**
+ * The PID of this Process as seen from darlingserver's namespace.
+ */
+ ID id() const;
+
+ /**
+ * The PID of this Process as seen from within the container (i.e. launchd's namespace).
+ */
+ NSID nsid() const;
+
+ std::vector> threads() const;
+
+ std::string vchrootPath() const;
+ void setVchrootPath(std::string path);
+ };
+};
+
+#endif // _DARLINGSERVER_PROCESS_HPP_
diff --git a/src/darlingserver/internal-include/darlingserver/registry.hpp b/src/darlingserver/internal-include/darlingserver/registry.hpp
new file mode 100644
index 000000000..d727f19c2
--- /dev/null
+++ b/src/darlingserver/internal-include/darlingserver/registry.hpp
@@ -0,0 +1,171 @@
+/**
+ * This file is part of Darling.
+ *
+ * Copyright (C) 2021 Darling developers
+ *
+ * Darling is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Darling is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Darling. If not, see .
+ */
+
+#ifndef _DARLINGSERVER_REGISTRY_HPP_
+#define _DARLINGSERVER_REGISTRY_HPP_
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+
+namespace DarlingServer {
+ template
+ class Registry {
+ private:
+ std::unordered_map> _map;
+ std::unordered_map> _nsmap;
+ std::shared_mutex _rwlock;
+
+ public:
+ using ID = typename Entry::ID;
+ using NSID = typename Entry::NSID;
+
+ std::shared_ptr registerIfAbsent(NSID nsid, std::function()> entryFactory) {
+ std::unique_lock lock(_rwlock);
+
+ auto it2 = _nsmap.find(nsid);
+ if (it2 != _nsmap.end()) {
+ return (*it2).second;
+ }
+
+ auto entry = entryFactory();
+ _map[entry->id()] = entry;
+ _nsmap[entry->nsid()] = entry;
+ return entry;
+ };
+
+ bool registerEntry(std::shared_ptr entry, bool replace = false) {
+ std::unique_lock lock(_rwlock);
+
+ if (!replace && (_map.find(entry->id()) != _map.end() || _nsmap.find(entry->nsid()) != _nsmap.end())) {
+ return false;
+ }
+
+ _map[entry->id()] = entry;
+ _nsmap[entry->nsid()] = entry;
+ return true;
+ };
+
+ bool unregisterEntryByID(ID id) {
+ std::unique_lock lock(_rwlock);
+
+ auto it = _map.find(id);
+
+ if (it == _map.end()) {
+ return false;
+ }
+
+ std::shared_ptr entry = (*it).second;
+ auto it2 = _nsmap.find(entry->nsid());
+
+ if (it2 == _nsmap.end()) {
+ return false;
+ }
+
+ _map.erase(it);
+ _nsmap.erase(it2);
+ return true;
+ };
+
+ bool unregisterEntryByNSID(NSID nsid) {
+ std::unique_lock lock(_rwlock);
+
+ auto it2 = _nsmap.find(nsid);
+
+ if (it2 == _nsmap.end()) {
+ return false;
+ }
+
+ std::shared_ptr entry = (*it2).second;
+ auto it = _map.find(entry->id());
+
+ if (it == _map.end()) {
+ return false;
+ }
+
+ _map.erase(it);
+ _nsmap.erase(it2);
+ return true;
+ };
+
+ /**
+ * Unregisters the given entry from this Registry.
+ *
+ * This is the recommended method for unregistering entries as it will
+ * actually compare pointers to ensure the entry being unregistered is
+ * the same as the one currently registered with the same IDs.
+ */
+ bool unregisterEntry(std::shared_ptr entry) {
+ std::unique_lock lock(_rwlock);
+
+ auto it = _map.find(entry->id());
+ auto it2 = _nsmap.find(entry->nsid());
+
+ if (it == _map.end() || it2 == _nsmap.end()) {
+ return false;
+ }
+
+ // note that we *want* pointer-to-pointer comparison
+ if ((*it).second != entry || (*it2).second != entry) {
+ return false;
+ }
+
+ _map.erase(it);
+ _nsmap.erase(it2);
+ return true;
+ };
+
+ std::optional> lookupEntryByID(ID id) {
+ std::shared_lock lock(_rwlock);
+
+ auto it = _map.find(id);
+ if (it == _map.end()) {
+ return std::nullopt;
+ }
+
+ return *it;
+ };
+
+ std::optional> lookupEntryByNSID(ID nsid) {
+ std::shared_lock lock(_rwlock);
+
+ auto it2 = _nsmap.find(nsid);
+ if (it2 == _nsmap.end()) {
+ return std::nullopt;
+ }
+
+ return *it2;
+ };
+ };
+
+ Registry& processRegistry();
+ Registry& threadRegistry();
+};
+
+#endif // _DARLINGSERVER_REGISTRY_HPP_
diff --git a/src/darlingserver/internal-include/darlingserver/server.hpp b/src/darlingserver/internal-include/darlingserver/server.hpp
new file mode 100644
index 000000000..8ccb9306e
--- /dev/null
+++ b/src/darlingserver/internal-include/darlingserver/server.hpp
@@ -0,0 +1,65 @@
+/**
+ * This file is part of Darling.
+ *
+ * Copyright (C) 2021 Darling developers
+ *
+ * Darling is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Darling is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Darling. If not, see .
+ */
+
+#ifndef _DARLINGSERVER_SERVER_HPP_
+#define _DARLINGSERVER_SERVER_HPP_
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+namespace DarlingServer {
+ // NOTE: server instances MUST be created with `new` rather than as a normal local/stack variable
+ class Server {
+ private:
+ int _listenerSocket;
+ std::string _prefix;
+ std::string _socketPath;
+ int _epollFD;
+ MessageQueue _inbox;
+ MessageQueue _outbox;
+ WorkQueue _workQueue;
+
+ void _worker(DarlingServer::Message message);
+
+ public:
+ Server(std::string prefix);
+ ~Server();
+
+ Server(const Server&) = delete;
+ Server& operator=(const Server&) = delete;
+ Server(Server&&) = delete;
+ Server& operator=(Server&&) = delete;
+
+ void start();
+
+ void monitorProcess(std::shared_ptr process);
+
+ std::string prefix() const;
+
+ static Server& sharedInstance();
+ };
+};
+
+#endif // _DARLINGSERVER_SERVER_HPP_
diff --git a/src/darlingserver/internal-include/darlingserver/thread.hpp b/src/darlingserver/internal-include/darlingserver/thread.hpp
new file mode 100644
index 000000000..b6dbe6dfb
--- /dev/null
+++ b/src/darlingserver/internal-include/darlingserver/thread.hpp
@@ -0,0 +1,79 @@
+/**
+ * This file is part of Darling.
+ *
+ * Copyright (C) 2021 Darling developers
+ *
+ * Darling is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Darling is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Darling. If not, see .
+ */
+
+#ifndef _DARLINGSERVER_THREAD_HPP_
+#define _DARLINGSERVER_THREAD_HPP_
+
+#include
+#include
+#include
+#include
+
+#include
+
+namespace DarlingServer {
+ class Process;
+ class Call;
+
+ class Thread: public std::enable_shared_from_this {
+ friend class Process;
+
+ private:
+ pid_t _tid;
+ pid_t _nstid;
+ std::weak_ptr _process;
+ std::shared_ptr _pendingCall;
+ Address _address;
+ mutable std::shared_mutex _rwlock;
+
+ public:
+ using ID = pid_t;
+ using NSID = ID;
+
+ Thread(std::shared_ptr process, NSID nsid);
+ ~Thread() noexcept(false);
+
+ void registerWithProcess();
+
+ Thread(const Thread&) = delete;
+ Thread& operator=(const Thread&) = delete;
+ Thread(Thread&&) = delete;
+ Thread& operator=(Thread&&) = delete;
+
+ std::shared_ptr process() const;
+
+ std::shared_ptr pendingCall() const;
+ void setPendingCall(std::shared_ptr newPendingCall);
+
+ /**
+ * The TID of this Thread as seen from darlingserver's namespace.
+ */
+ ID id() const;
+
+ /**
+ * The TID of this Thread as seen from within the container (i.e. launchd's namespace).
+ */
+ NSID nsid() const;
+
+ Address address() const;
+ void setAddress(Address address);
+ };
+};
+
+#endif // _DARLINGSERVER_THREAD_HPP_
diff --git a/src/darlingserver/internal-include/darlingserver/workers.hpp b/src/darlingserver/internal-include/darlingserver/workers.hpp
new file mode 100644
index 000000000..085b958e6
--- /dev/null
+++ b/src/darlingserver/internal-include/darlingserver/workers.hpp
@@ -0,0 +1,176 @@
+/**
+ * This file is part of Darling.
+ *
+ * Copyright (C) 2021 Darling developers
+ *
+ * Darling is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Darling is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Darling. If not, see .
+ */
+
+#ifndef _DARLINGSERVER_WORKERS_HPP_
+#define _DARLINGSERVER_WORKERS_HPP_
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace DarlingServer {
+ template
+ class WorkQueue {
+ private:
+ std::mutex _queueMutex;
+ std::queue _workItems;
+ std::function _workerFunction;
+ std::unordered_map _workerThreads;
+ size_t _threadsAvailable = 0;
+ std::condition_variable _cv;
+ bool _dying = false;
+
+ static constexpr unsigned int minimumThreads() {
+ return 2;
+ };
+
+ static unsigned int maximumThreads() {
+ unsigned int hw = std::thread::hardware_concurrency();
+ if (hw > 0 && hw - 1 > minimumThreads()) {
+ // minus one for the main thread
+ return hw - 1;
+ } else {
+ return minimumThreads();
+ }
+ };
+
+ static constexpr std::chrono::seconds temporaryWorkerWaitPeriod() {
+ return std::chrono::seconds(15);
+ };
+
+ void worker(bool permanent) {
+ std::optional workItem = std::nullopt;
+ std::unique_lock lock(_queueMutex, std::defer_lock);
+
+ while (true) {
+ // acquire the lock for the condition variable
+ lock.lock();
+
+ // we're going to wait for work, so we're available
+ ++_threadsAvailable;
+
+ bool waitResult;
+ auto predicate = [&, this]() {
+ // if we're dying, stop waiting
+ if (_dying) {
+ --_threadsAvailable;
+ return true;
+ }
+
+ // if we have a work item ready, take it and stop waiting
+ if (_workItems.size() > 0) {
+ workItem = std::move(_workItems.front());
+ _workItems.pop();
+ --_threadsAvailable;
+ return true;
+ }
+
+ // otherwise, let's keep waiting
+ return false;
+ };
+
+ if (permanent) {
+ // permanent workers just wait until there's work available (or the queue is being destroyed)
+ _cv.wait(lock, predicate);
+ waitResult = true;
+ } else {
+ // temporary workers wait for a certain period of time for work; if there's no work available after that period, they die
+ waitResult = _cv.wait_for(lock, temporaryWorkerWaitPeriod(), predicate);
+ }
+
+ // if the queue is being destroyed, let the thread die
+ if (_dying) {
+ return;
+ }
+
+ // if we failed to find any work items, let's die
+ if (!waitResult) {
+ auto thisThread = std::move(_workerThreads[std::this_thread::get_id()]);
+ _workerThreads.erase(thisThread.get_id());
+ thisThread.detach();
+ return;
+ }
+
+ // drop the lock to perform the work
+ lock.unlock();
+
+ // perform the work
+ _workerFunction(std::move(workItem.value()));
+ workItem = std::nullopt;
+ }
+ };
+
+ public:
+ WorkQueue(std::function workerFunction):
+ _workerFunction(workerFunction)
+ {
+ std::scoped_lock lock(_queueMutex);
+
+ // start the permanent worker threads
+ for (size_t i = 0; i < minimumThreads(); ++i) {
+ std::thread worker = std::thread(&WorkQueue::worker, this, true);
+ _workerThreads[worker.get_id()] = std::move(worker);
+ }
+ };
+ ~WorkQueue() {
+ // tell all the threads that we're being destroyed
+ std::unique_lock lock(_queueMutex);
+ _dying = true;
+ lock.unlock();
+ _cv.notify_all();
+
+ // now wait for all of the threads to die
+ for (auto& [id, thread]: _workerThreads) {
+ thread.join();
+ }
+ };
+
+ WorkQueue(const WorkQueue&) = delete;
+ WorkQueue& operator=(const WorkQueue&) = delete;
+ WorkQueue(WorkQueue&&) = delete;
+ WorkQueue& operator=(WorkQueue&&) = delete;
+
+ void push(WorkItem workItem) {
+ std::unique_lock lock(_queueMutex);
+ _workItems.push(std::move(workItem));
+
+ // if there are no threads available to perform the work AND we have less than the
+ // maximum number of worker threads currently active, spawn a temporary worker thread.
+ if (_threadsAvailable == 0 && _workerThreads.size() < maximumThreads()) {
+ std::thread worker = std::thread(&WorkQueue::worker, this, false);
+ _workerThreads[worker.get_id()] = std::move(worker);
+
+ // note that we don't need to notify anyone in this case, since we want the new worker thread
+ // to take care of the new work item (it's not a problem if another thread gets to it first, though).
+ } else {
+ // otherwise, notify one of the available threads that there's work available
+ lock.unlock();
+ _cv.notify_one();
+ }
+ };
+ };
+};
+
+#endif // _DARLINGSERVER_WORKERS_HPP_
diff --git a/src/darlingserver/scripts/generate-rpc-wrappers.py b/src/darlingserver/scripts/generate-rpc-wrappers.py
new file mode 100755
index 000000000..ee993d2f5
--- /dev/null
+++ b/src/darlingserver/scripts/generate-rpc-wrappers.py
@@ -0,0 +1,563 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+from collections import OrderedDict
+import textwrap
+from datetime import datetime
+
+# NOTE: in Python 3.7+, we can rely on dictionaries having their items in insertion order.
+# unfortunately, we can't expect everyone building Darling to have Python 3.7+ installed.
+calls = [
+ ('checkin', [], []),
+
+ ('checkout', [], []),
+
+ ('vchroot_path', [
+ ('buffer', 'char*'),
+ ('buffer_size', 'size_t'),
+ ], [
+ ('length', 'size_t'),
+ ]),
+]
+
+if len(sys.argv) < 5:
+ sys.exit("Usage: " + sys.argv[0] + " ")
+
+os.makedirs(os.path.dirname(sys.argv[1]), exist_ok=True)
+os.makedirs(os.path.dirname(sys.argv[2]), exist_ok=True)
+os.makedirs(os.path.dirname(sys.argv[3]), exist_ok=True)
+
+def to_camel_case(snake_str):
+ components = snake_str.split('_')
+ return ''.join(x.title() for x in components)
+
+public_header = open(sys.argv[1], "w")
+internal_header = open(sys.argv[2], "w")
+library_source = open(sys.argv[3], "w")
+library_import = sys.argv[4]
+
+license_header = """\
+// This file has been auto-generated by generate-rpc-wrappers.py for use with darlingserver
+
+/**
+ * This file is part of Darling.
+ *
+ * Copyright (C) {} Darling developers
+ *
+ * Darling is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Darling is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Darling. If not, see .
+ */
+
+""".format(datetime.now().year)
+
+public_header.write(license_header)
+library_source.write(license_header)
+internal_header.write(license_header)
+
+public_header.write("""\
+#ifndef _DARLINGSERVER_API_H_
+#define _DARLINGSERVER_API_H_
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+""")
+
+public_header.write("enum dserver_callnum {\n")
+public_header.write("\tdserver_callnum_invalid,\n")
+for call in calls:
+ call_name = call[0]
+ call_parameters = call[1]
+ reply_parameters = call[2]
+
+ public_header.write("\tdserver_callnum_" + call_name + ",\n")
+public_header.write("};\n")
+
+public_header.write("""\
+
+typedef enum dserver_callnum dserver_callnum_t;
+
+typedef struct dserver_rpc_callhdr {
+ pid_t pid;
+ pid_t tid;
+ dserver_callnum_t number;
+} dserver_rpc_callhdr_t;
+
+typedef struct dserver_rpc_replyhdr {
+ dserver_callnum_t number;
+ int code;
+} dserver_rpc_replyhdr_t;
+
+""")
+
+library_source.write("""\
+#include {}
+
+#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)
+ #error Missing definitions
+#endif
+
+#ifndef dserver_rpc_hooks_get_pid
+DSERVER_RPC_HOOKS_ATTRIBUTE pid_t dserver_rpc_hooks_get_pid(void);
+#endif
+
+#ifndef dserver_rpc_hooks_get_tid
+DSERVER_RPC_HOOKS_ATTRIBUTE pid_t dserver_rpc_hooks_get_tid(void);
+#endif
+
+#ifndef dserver_rpc_hooks_get_server_address
+DSERVER_RPC_HOOKS_ATTRIBUTE void* dserver_rpc_hooks_get_server_address(void);
+#endif
+
+#ifndef dserver_rpc_hooks_get_server_address_length
+DSERVER_RPC_HOOKS_ATTRIBUTE size_t dserver_rpc_hooks_get_server_address_length(void);
+#endif
+
+#ifndef dserver_rpc_hooks_memcpy
+DSERVER_RPC_HOOKS_ATTRIBUTE void* dserver_rpc_hooks_memcpy(void* destination, const void* source, size_t length);
+#endif
+
+#ifndef dserver_rpc_hooks_send_message
+DSERVER_RPC_HOOKS_ATTRIBUTE long int dserver_rpc_hooks_send_message(int socket, const dserver_rpc_hooks_msghdr_t* message);
+#endif
+
+#ifndef dserver_rpc_hooks_receive_message
+DSERVER_RPC_HOOKS_ATTRIBUTE long int dserver_rpc_hooks_receive_message(int socket, dserver_rpc_hooks_msghdr_t* out_message);
+#endif
+
+#ifndef dserver_rpc_hooks_get_bad_message_status
+DSERVER_RPC_HOOKS_ATTRIBUTE int dserver_rpc_hooks_get_bad_message_status(void);
+#endif
+
+#ifndef dserver_rpc_hooks_get_communication_error_status
+DSERVER_RPC_HOOKS_ATTRIBUTE int dserver_rpc_hooks_get_communication_error_status(void);
+#endif
+
+#ifndef dserver_rpc_hooks_get_broken_pipe_status
+DSERVER_RPC_HOOKS_ATTRIBUTE int dserver_rpc_hooks_get_broken_pipe_status(void);
+#endif
+
+#ifndef dserver_rpc_hooks_close_fd
+DSERVER_RPC_HOOKS_ATTRIBUTE void dserver_rpc_hooks_close_fd(int fd);
+#endif
+
+#ifndef dserver_rpc_hooks_get_socket
+DSERVER_RPC_HOOKS_ATTRIBUTE int dserver_rpc_hooks_get_socket(void);
+#endif
+
+""".format(library_import))
+
+internal_header.write("#define DSERVER_VALID_CALLNUM_CASES \\\n")
+for call in calls:
+ call_name = call[0]
+ call_parameters = call[1]
+ reply_parameters = call[2]
+
+ internal_header.write("\tcase dserver_callnum_" + call_name + ": \\\n")
+internal_header.write("\n")
+
+internal_header.write("#define DSERVER_CONSTRUCT_CASES \\\n")
+for call in calls:
+ call_name = call[0]
+ call_parameters = call[1]
+ reply_parameters = call[2]
+ camel_name = to_camel_case(call_name)
+
+ internal_header.write("\tCALL_CASE(" + call_name + ", " + camel_name + "); \\\n")
+internal_header.write("\n")
+
+internal_header.write("#define DSERVER_ENUM_VALUES \\\n")
+for call in calls:
+ call_name = call[0]
+ call_parameters = call[1]
+ reply_parameters = call[2]
+ camel_name = to_camel_case(call_name)
+
+ internal_header.write("\t" + camel_name + " = dserver_callnum_" + call_name + ", \\\n")
+internal_header.write("\n")
+
+internal_header.write("#define DSERVER_CLASS_DECLS \\\n")
+for call in calls:
+ call_name = call[0]
+ call_parameters = call[1]
+ reply_parameters = call[2]
+ camel_name = to_camel_case(call_name)
+
+ internal_header.write("\tclass " + camel_name + "; \\\n")
+internal_header.write("\n")
+
+internal_header.write("#define DSERVER_CLASS_DEFS \\\n")
+for call in calls:
+ call_name = call[0]
+ call_parameters = call[1]
+ reply_parameters = call[2]
+ camel_name = to_camel_case(call_name)
+ fd_count_in_reply = 0
+
+ internal_header.write(textwrap.indent(textwrap.dedent("""\
+ class Call::{1}: public Call {{ \\
+ friend class Call; \\
+ private: \\
+ {2}
+ public: \\
+ {1}(MessageQueue& replyQueue, std::shared_ptr thread, dserver_rpc_call_{0}_t* data, Message&& requestMessage): \\
+ Call(replyQueue, thread, requestMessage.address()){3} \\
+ {4}
+ {{ \\
+ """), '\t').format(
+ call_name,
+ camel_name,
+ ("dserver_call_" + call_name + "_t _body; \\") if len(call_parameters) > 0 else "\\",
+ "," if len(call_parameters) > 0 else "",
+ "_body(data->body) \\" if len(call_parameters) > 0 else "\\"
+ )
+ )
+
+ for param in call_parameters:
+ param_name = param[0]
+ param_type = param[1]
+
+ if param_type != "@fd":
+ continue
+
+ param_type = "int"
+
+ internal_header.write("\t\t\t_body." + param_name + " = requestMessage.extractDescriptorAtIndex(_body." + param_name + "); \\\n")
+ internal_header.write("\t\t}; \\\n")
+
+ internal_header.write("\t\t~" + camel_name + "() { \\\n")
+ for param in call_parameters:
+ param_name = param[0]
+ param_type = param[1]
+
+ if param_type != "@fd":
+ continue
+
+ param_type = "int"
+
+ internal_header.write("\t\t\tif (_body." + param_name + " != -1) { \\\n")
+ internal_header.write("\t\t\t\tclose(_body." + param_name + "); \\\n")
+ internal_header.write("\t\t} \\\n")
+
+ internal_header.write(textwrap.indent(textwrap.dedent("""\
+ }}; \\
+ virtual Call::Number number() const {{ \\
+ return Call::Number::{0}; \\
+ }}; \\
+ virtual void processCall(); \\
+ private: \\
+ """), '\t').format(camel_name))
+
+ internal_header.write("\t\tvoid _sendReply(int resultCode")
+ for param in reply_parameters:
+ param_name = param[0]
+ param_type = param[1]
+
+ if param_type == "@fd":
+ param_type = "int"
+ fd_count_in_reply += 1
+
+ internal_header.write(", " + param_type + " " + param_name)
+ internal_header.write(") { \\\n")
+
+ internal_header.write(textwrap.indent(textwrap.dedent("""\
+ Message reply(sizeof(dserver_rpc_reply_{0}_t), {1}); \\
+ reply.setAddress(_replyAddress); \\
+ auto replyStruct = reinterpret_cast(reply.data().data()); \\
+ replyStruct->header.number = dserver_callnum_{0}; \\
+ replyStruct->header.code = resultCode; \\
+ """), '\t\t\t').format(call_name, fd_count_in_reply))
+
+ fd_index = 0
+ for param in reply_parameters:
+ param_name = param[0]
+ param_type = param[1]
+ val = param_name
+
+ if param_type == "@fd":
+ param_type = "int"
+ val = str(fd_index)
+ internal_header.write("\t\t\treply.pushDescriptor(" + param_name + "); \\\n")
+
+ internal_header.write("\t\t\treplyStruct->body." + param_name + " = " + val + "; \\\n")
+ internal_header.write("\t\t\t_replyQueue.push(std::move(reply)); \\\n")
+ internal_header.write("\t\t}; \\\n")
+
+ internal_header.write("\t}; \\\n")
+internal_header.write("\n")
+
+for call in calls:
+ call_name = call[0]
+ call_parameters = call[1]
+ reply_parameters = call[2]
+ fd_count_in_call = 0
+ fd_count_in_reply = 0
+
+ # define the RPC call body structure
+ if len(call_parameters) > 0:
+ public_header.write("typedef struct dserver_call_" + call_name + " dserver_call_" + call_name + "_t;\n")
+ public_header.write("struct dserver_call_" + call_name + " {\n")
+ for param in call_parameters:
+ param_name = param[0]
+ param_type = param[1]
+
+ if param_type == "@fd":
+ param_type = "int"
+ fd_count_in_call += 1
+
+ public_header.write("\t" + param_type + " " + param_name + ";\n")
+ public_header.write("};\n")
+
+ # define the RPC call structure
+ public_header.write(textwrap.dedent("""\
+ typedef struct dserver_rpc_call_{0} dserver_rpc_call_{0}_t;
+ struct dserver_rpc_call_{0} {{
+ dserver_rpc_callhdr_t header;
+ """).format(call_name))
+ if len(call_parameters) > 0:
+ public_header.write("\tdserver_call_" + call_name + "_t body;\n")
+ public_header.write("};\n")
+
+ # define the RPC reply body structure
+ if len(reply_parameters) > 0:
+ public_header.write("typedef struct dserver_reply_" + call_name + " dserver_reply_" + call_name + "_t;\n")
+ public_header.write("struct dserver_reply_" + call_name + " {\n")
+ for param in reply_parameters:
+ param_name = param[0]
+ param_type = param[1]
+
+ if param_type == "@fd":
+ param_type = "int"
+ fd_count_in_reply += 1
+
+ public_header.write("\t" + param_type + " " + param_name + ";\n")
+ public_header.write("};\n")
+
+ # define the RPC reply structure
+ public_header.write(textwrap.dedent("""\
+ typedef struct dserver_rpc_reply_{0} dserver_rpc_reply_{0}_t;
+ struct dserver_rpc_reply_{0} {{
+ dserver_rpc_replyhdr_t header;
+ """).format(call_name))
+ if len(reply_parameters) > 0:
+ public_header.write("\tdserver_reply_" + call_name + "_t body;\n")
+ public_header.write("};\n")
+
+ # declare the RPC call wrapper function
+ # (and output the prototype part of the function definition)
+ tmp = "int dserver_rpc_" + call_name + "("
+ public_header.write(tmp)
+ library_source.write(tmp)
+ is_first = True
+ for param in call_parameters:
+ param_name = param[0]
+ param_type = param[1]
+
+ if param_type == "@fd":
+ param_type = "int"
+
+ if is_first:
+ is_first = False
+ tmp = ""
+ else:
+ tmp = ", "
+ tmp += param_type + " " + param_name
+ public_header.write(tmp)
+ library_source.write(tmp)
+
+ for param in reply_parameters:
+ param_name = param[0]
+ param_type = param[1]
+
+ if param_type == "@fd":
+ param_type = "int"
+
+ if is_first:
+ is_first = False
+ tmp = ""
+ else:
+ tmp = ", "
+ tmp += param_type + "* out_" + param_name
+ public_header.write(tmp)
+ library_source.write(tmp)
+ public_header.write(");\n\n")
+ library_source.write(") {\n")
+
+ # define the RPC call wrapper function
+ library_source.write(textwrap.indent(textwrap.dedent("""\
+ int status = 0;
+ dserver_rpc_call_{0}_t call = {{
+ .header = {{
+ .pid = dserver_rpc_hooks_get_pid(),
+ .tid = dserver_rpc_hooks_get_tid(),
+ .number = dserver_callnum_{0},
+ }},
+ """), '\t').format(call_name))
+
+ if len(call_parameters) > 0:
+ library_source.write("\t\t.body = {\n")
+ fd_index = 0
+ for param in call_parameters:
+ param_name = param[0]
+ param_type = param[1]
+ val = param_name
+
+ if param_type == "@fd":
+ param_type = "int"
+ val = "(" + param_name + " < 0) ? -1 : " + str(fd_index)
+ fd_index += 1
+
+ library_source.write("\t\t\t." + param_name + " = " + val + ",\n")
+ library_source.write("\t\t},\n")
+
+ library_source.write("\t};\n")
+ library_source.write("\tdserver_rpc_reply_" + call_name + "_t reply;\n")
+
+ if fd_count_in_call > 0 or fd_count_in_reply > 0:
+ library_source.write("\tint fds[" + max(fd_count_in_call, fd_count_in_reply) + "]")
+ if fd_count_in_call > 0:
+ library_source.write(" = { ")
+ is_first = True
+ for param in call_parameters:
+ param_name = param[0]
+ param_type = param[1]
+
+ if param_type != "@fd":
+ continue
+
+ if is_first:
+ is_first = False
+ else:
+ library_source.write(", ")
+ library_source.write(param_name)
+ library_source.write(" }")
+ library_source.write(";\n")
+ library_source.write("\tchar controlbuf[DSERVER_RPC_HOOKS_CMSG_SPACE(sizeof(fds))];\n")
+
+ library_source.write(textwrap.indent(textwrap.dedent("""\
+ dserver_rpc_hooks_iovec_t call_data = {
+ .iov_base = &call,
+ .iov_len = sizeof(call),
+ };
+ dserver_rpc_hooks_msghdr_t callmsg = {
+ .msg_name = dserver_rpc_hooks_get_server_address(),
+ .msg_namelen = dserver_rpc_hooks_get_server_address_length(),
+ .msg_iov = &call_data,
+ .msg_iovlen = 1,
+ """), '\t'))
+
+ if fd_count_in_call == 0:
+ library_source.write("\t\t.msg_control = NULL,\n")
+ library_source.write("\t\t.msg_controllen = 0,\n")
+ else:
+ library_source.write("\t\t.msg_control = controlbuf,\n")
+ library_source.write("\t\t.msg_controllen = sizeof(controlbuf),\n")
+
+ library_source.write("\t};\n")
+
+ if fd_count_in_call > 0:
+ library_source.write(textwrap.indent(textwrap.dedent("""\
+ dserver_rpc_hooks_cmsghdr_t* call_cmsg = DSERVER_RPC_HOOKS_CMSG_FIRSTHDR(&callmsg);
+ call_cmsg->cmsg_level = DSERVER_RPC_HOOKS_SOL_SOCKET;
+ call_cmsg->cmsg_type = DSERVER_RPC_HOOKS_SCM_RIGHTS;
+ call_cmsg->cmsg_len = DSERVER_RPC_HOOKS_CMSG_LEN(sizeof(int) * {0});
+ dserver_rpc_hooks_memcpy(DSERVER_RPC_HOOKS_CMSG_DATA(call_cmsg), fds, sizeof(int) * {0});
+ """), '\t').format(fd_count_in_call))
+
+ library_source.write(textwrap.indent(textwrap.dedent("""\
+ dserver_rpc_hooks_iovec_t reply_data = {
+ .iov_base = &reply,
+ .iov_len = sizeof(reply),
+ };
+ dserver_rpc_hooks_msghdr_t replymsg = {
+ .msg_name = NULL,
+ .msg_namelen = 0,
+ .msg_iov = &reply_data,
+ .msg_iovlen = 1,
+ """), '\t'))
+
+ if fd_count_in_reply == 0:
+ library_source.write("\t\t.msg_control = NULL,\n")
+ library_source.write("\t\t.msg_controllen = 0,\n")
+ else:
+ library_source.write("\t\t.msg_control = controlbuf,\n")
+ library_source.write("\t\t.msg_controllen = sizeof(controlbuf),\n")
+
+ library_source.write("\t};\n\n")
+
+ library_source.write("\tint socket = dserver_rpc_hooks_get_socket();\n")
+ library_source.write("\tif (socket < 0) {")
+ library_source.write("\t\treturn dserver_rpc_hooks_get_broken_pipe_status();\n")
+ library_source.write("\t}\n\n")
+
+ library_source.write("\tlong int long_status = dserver_rpc_hooks_send_message(socket, &callmsg);\n")
+ library_source.write("\tif (long_status < 0) {\n")
+ library_source.write("\t\treturn (int)long_status;\n")
+ library_source.write("\t}\n\n")
+ library_source.write("\tif (long_status != sizeof(call)) {\n")
+ library_source.write("\t\treturn dserver_rpc_hooks_get_communication_error_status();\n")
+ library_source.write("\t}\n\n")
+
+ library_source.write("\tlong_status = dserver_rpc_hooks_receive_message(socket, &replymsg);\n")
+ library_source.write("\tif (long_status < 0) {\n")
+ library_source.write("\t\treturn (int)long_status;\n")
+ library_source.write("\t}\n\n")
+ library_source.write("\tif (long_status != sizeof(reply)) {\n")
+ library_source.write("\t\treturn dserver_rpc_hooks_get_communication_error_status();\n")
+ library_source.write("\t}\n\n")
+
+ if fd_count_in_reply != 0:
+ library_source.write(textwrap.indent(textwrap.dedent("""\
+ dserver_rpc_hooks_cmsghdr_t* reply_cmsg = DSERVER_RPC_HOOKS_CMSG_FIRSTHDR(&replymsg);
+ 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})) {{
+ status = dserver_rpc_hooks_get_bad_message_status();
+ return status;
+ }}
+ dserver_rpc_hooks_memcpy(fds, DSERVER_RPC_HOOKS_CMSG_DATA(reply_cmsg), sizeof(int) * {0});
+ """), '\t').format(fd_count_in_reply))
+
+ for param in reply_parameters:
+ param_name = param[0]
+ param_type = param[1]
+
+ if param_type == "@fd":
+ param_type = "int"
+ library_source.write("\tif (out_" + param_name + ") {\n")
+ library_source.write("\t\t*out_" + param_name + " = fds[reply.body." + param_name + "];\n")
+ library_source.write("\t} else {\n")
+ library_source.write("\t\tdserver_rpc_hooks_close_fd(fds[reply.body." + param_name + "]);\n")
+ library_source.write("\t}\n")
+ else:
+ library_source.write("\tif (out_" + param_name + ") {\n")
+ library_source.write("\t\t*out_" + param_name + " = reply.body." + param_name + ";\n")
+ library_source.write("\t}\n")
+
+ library_source.write("\treturn status;\n")
+
+ library_source.write("};\n\n")
+
+public_header.write("""\
+#ifdef __cplusplus
+};
+#endif
+
+#endif // _DARLINGSERVER_API_H_
+""")
+
+public_header.close()
+internal_header.close()
+library_source.close()
diff --git a/src/darlingserver/src/call.cpp b/src/darlingserver/src/call.cpp
new file mode 100644
index 000000000..3ff2b06bb
--- /dev/null
+++ b/src/darlingserver/src/call.cpp
@@ -0,0 +1,162 @@
+/**
+ * This file is part of Darling.
+ *
+ * Copyright (C) 2021 Darling developers
+ *
+ * Darling is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Darling is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Darling. If not, see .
+ */
+
+#define _GNU_SOURCE 1
+#include
+#include
+#include
+
+std::shared_ptr DarlingServer::Call::callFromMessage(Message&& requestMessage, MessageQueue& replyQueue) {
+ if (requestMessage.data().size() < sizeof(dserver_rpc_callhdr_t)) {
+ throw std::invalid_argument("Message buffer was too small for call header");
+ }
+
+ dserver_rpc_callhdr_t* header = reinterpret_cast(requestMessage.data().data());
+ std::shared_ptr result = nullptr;
+ std::shared_ptr process = nullptr;
+ std::shared_ptr thread = nullptr;
+
+ // first, make sure we know this call number
+ switch (header->number) {
+ DSERVER_VALID_CALLNUM_CASES
+ break;
+
+ default:
+ throw std::invalid_argument("Invalid call number");
+ }
+
+ // now let's lookup (and possibly create) the process and thread making this call
+ process = processRegistry().registerIfAbsent(header->pid, [&]() {
+ auto tmp = std::make_shared(requestMessage.pid(), header->pid);
+ Server::sharedInstance().monitorProcess(tmp);
+ return tmp;
+ });
+ thread = threadRegistry().registerIfAbsent(header->tid, [&]() {
+ auto tmp = std::make_shared(process, header->tid);
+ tmp->setAddress(requestMessage.address());
+ tmp->registerWithProcess();
+ return tmp;
+ });
+
+ // finally, let's construct the call class
+
+ #define CALL_CASE(_callName, _className) \
+ case dserver_callnum_ ## _callName: { \
+ if (requestMessage.data().size() < sizeof(dserver_rpc_call_ ## _callName ## _t)) { \
+ throw std::invalid_argument("Message buffer was too small for dserver_call_" #_callName "_t"); \
+ } \
+ result = std::make_shared<_className>(replyQueue, thread, reinterpret_cast(header), std::move(requestMessage)); \
+ } break;
+
+ switch (header->number) {
+ DSERVER_CONSTRUCT_CASES
+
+ default:
+ throw std::invalid_argument("Invalid call number");
+ }
+
+ #undef CALL_CASE
+
+ thread->setPendingCall(result);
+
+ return result;
+};
+
+DarlingServer::Call::Call(MessageQueue& replyQueue, std::shared_ptr thread, Address replyAddress):
+ _replyQueue(replyQueue),
+ _thread(thread),
+ _replyAddress(replyAddress)
+ {};
+
+DarlingServer::Call::~Call() {};
+
+/**
+ * Note that you MUST NOT have any local variables referencing `this` when this method is called.
+ *
+ * This Call object MAY be destroyed upon the return of this method.
+ */
+void DarlingServer::Call::_stopPending() {
+ // we're done processing this call; dump it
+ if (auto thread = _thread.lock()) {
+ thread->setPendingCall(nullptr);
+ }
+};
+
+void DarlingServer::Call::Checkin::processCall() {
+ // the Call creation already took care of registering the process and thread
+ _sendReply(0);
+ _stopPending();
+};
+
+void DarlingServer::Call::Checkout::processCall() {
+ int code = 0;
+
+ if (auto thread = _thread.lock()) {
+ if (auto process = thread->process()) {
+ threadRegistry().unregisterEntry(thread);
+
+ if (thread->id() == process->id()) {
+ processRegistry().unregisterEntry(process);
+ }
+ } else {
+ code = -ESRCH;
+ }
+ } else {
+ code = -ESRCH;
+ }
+
+ _sendReply(code);
+ _stopPending();
+};
+
+void DarlingServer::Call::VchrootPath::processCall() {
+ int code = 0;
+ size_t fullLength = 0;
+
+ if (auto thread = _thread.lock()) {
+ if (auto process = thread->process()) {
+ if (_body.buffer_size > 0) {
+ struct iovec local;
+ struct iovec remote;
+ auto tmpstr = process->vchrootPath().substr(0, _body.buffer_size - 1);
+ auto len = std::min(tmpstr.length() + 1, _body.buffer_size);
+
+ fullLength = process->vchrootPath().length();
+
+ // note that, despite the const cast, this is safe because the local iovec data is not modified by the call
+ local.iov_base = const_cast(tmpstr.c_str());
+ local.iov_len = len;
+
+ remote.iov_base = _body.buffer;
+ remote.iov_len = len;
+
+ if (process_vm_writev(process->id(), &local, 1, &remote, 1, 0) < 0) {
+ code = -errno;
+ }
+ }
+ } else {
+ code = -ESRCH;
+ }
+ } else {
+ code = -ESRCH;
+ }
+
+ _sendReply(code, fullLength);
+ _stopPending();
+};
diff --git a/src/darlingserver/src/darlingserver.cpp b/src/darlingserver/src/darlingserver.cpp
new file mode 100644
index 000000000..bc162c9fb
--- /dev/null
+++ b/src/darlingserver/src/darlingserver.cpp
@@ -0,0 +1,532 @@
+/**
+ * This file is part of Darling.
+ *
+ * Copyright (C) 2021 Darling developers
+ *
+ * Darling is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Darling is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Darling. If not, see .
+ */
+
+#define _GNU_SOURCE 1
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+
+// TODO: most of the code here was ported over from startup/darling.c; we should C++-ify it.
+
+#define MLDR_PATH LIBEXEC_PATH "/usr/libexec/darling/mldr"
+
+void fixPermissionsRecursive(const char* path, uid_t originalUID, gid_t originalGID)
+{
+ DIR* dir;
+ struct dirent* ent;
+
+ if (chown(path, originalUID, originalGID) == -1)
+ fprintf(stderr, "Cannot chown %s: %s\n", path, strerror(errno));
+
+ dir = opendir(path);
+ if (!dir)
+ return;
+
+ while ((ent = readdir(dir)) != NULL)
+ {
+ if (ent->d_type == DT_DIR)
+ {
+ char* subdir;
+
+ if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
+ continue;
+
+ subdir = (char*) malloc(strlen(path) + 2 + strlen(ent->d_name));
+ sprintf(subdir, "%s/%s", path, ent->d_name);
+
+ fixPermissionsRecursive(subdir, originalUID, originalGID);
+
+ free(subdir);
+ }
+ }
+
+ closedir(dir);
+}
+
+const char* xdgDirectory(const char* name)
+{
+ static char dir[4096];
+ char* cmd = (char*) malloc(16 + strlen(name));
+
+ sprintf(cmd, "xdg-user-dir %s", name);
+
+ FILE* proc = popen(cmd, "r");
+
+ free(cmd);
+
+ if (!proc)
+ return NULL;
+
+ fgets(dir, sizeof(dir)-1, proc);
+
+ pclose(proc);
+
+ size_t len = strlen(dir);
+ if (len <= 1)
+ return NULL;
+
+ if (dir[len-1] == '\n')
+ dir[len-1] = '\0';
+ return dir;
+}
+
+void setupUserHome(const char* prefix, uid_t originalUID)
+{
+ char buf[4096], buf2[4096];
+
+ snprintf(buf, sizeof(buf), "%s/Users", prefix);
+
+ // Remove the old /Users symlink that may exist
+ unlink(buf);
+
+ // mkdir /Users
+ mkdir(buf, 0777);
+
+ // mkdir /Users/Shared
+ strcat(buf, "/Shared");
+ mkdir(buf, 0777);
+
+ const char* home = getenv("HOME");
+
+ const char* login = NULL;
+ struct passwd* pw = getpwuid(originalUID);
+
+ if (pw != NULL)
+ login = pw->pw_name;
+
+ if (!login)
+ login = getlogin();
+
+ if (!login)
+ {
+ fprintf(stderr, "Cannot determine your user name\n");
+ exit(1);
+ }
+ if (!home)
+ {
+ fprintf(stderr, "Cannot determine your home directory\n");
+ exit(1);
+ }
+
+ snprintf(buf, sizeof(buf), "%s/Users/%s", prefix, login);
+
+ // mkdir /Users/$LOGIN
+ mkdir(buf, 0755);
+
+ snprintf(buf2, sizeof(buf2), "/Volumes/SystemRoot%s", home);
+
+ strcat(buf, "/LinuxHome");
+ unlink(buf);
+
+ // symlink /Users/$LOGIN/LinuxHome -> $HOME
+ symlink(buf2, buf);
+
+ static const char* xdgmap[][2] = {
+ { "DESKTOP", "Desktop" },
+ { "DOWNLOAD", "Downloads" },
+ { "PUBLICSHARE", "Public" },
+ { "DOCUMENTS", "Documents" },
+ { "MUSIC", "Music" },
+ { "PICTURES", "Pictures" },
+ { "VIDEOS", "Movies" },
+ };
+
+ for (int i = 0; i < sizeof(xdgmap) / sizeof(xdgmap[0]); i++)
+ {
+ const char* dir = xdgDirectory(xdgmap[i][0]);
+ if (!dir)
+ continue;
+
+ snprintf(buf2, sizeof(buf2), "/Volumes/SystemRoot%s", dir);
+ snprintf(buf, sizeof(buf), "%s/Users/%s/%s", prefix, login, xdgmap[i][1]);
+
+ unlink(buf);
+ symlink(buf2, buf);
+ }
+}
+
+void setupCoredumpPattern(void)
+{
+ FILE* f = fopen("/proc/sys/kernel/core_pattern", "w");
+ if (f != NULL)
+ {
+ // This is how macOS saves core dumps
+ fputs("/cores/core.%p\n", f);
+ fclose(f);
+ }
+}
+
+static void wipeDir(const char* dirpath)
+{
+ char path[4096];
+ struct dirent* ent;
+ DIR* dir = opendir(dirpath);
+
+ if (!dir)
+ return;
+
+ while ((ent = readdir(dir)) != NULL)
+ {
+ if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
+ continue;
+
+ snprintf(path, sizeof(path), "%s/%s", dirpath, ent->d_name);
+
+ if (ent->d_type == DT_DIR)
+ {
+ wipeDir(path);
+ rmdir(path);
+ }
+ else
+ unlink(path);
+ }
+
+ closedir(dir);
+}
+
+void darlingPreInit(const char* prefix)
+{
+ // TODO: Run /usr/libexec/makewhatis
+ const char* dirs[] = {
+ "/var/tmp",
+ "/var/run"
+ };
+
+ char fullpath[4096];
+ strcpy(fullpath, prefix);
+ const size_t prefixLen = strlen(fullpath);
+
+ for (size_t i = 0; i < sizeof(dirs)/sizeof(dirs[0]); i++)
+ {
+ fullpath[prefixLen] = 0;
+ strcat(fullpath, dirs[i]);
+ wipeDir(fullpath);
+ }
+}
+
+void spawnLaunchd(const char* prefix)
+{
+ puts("Bootstrapping the container with launchd...");
+
+ // putenv("KQUEUE_DEBUG=1");
+
+ auto tmp = (std::string(prefix) + "/var/run/darlingserver.sock");
+
+ setenv("DYLD_ROOT_PATH", LIBEXEC_PATH, 1);
+ setenv("__mldr_sockpath", tmp.c_str(), 1);
+ execl(MLDR_PATH, "mldr!" LIBEXEC_PATH "/usr/libexec/darling/vchroot", "vchroot", prefix, "/sbin/launchd", NULL);
+
+ fprintf(stderr, "Failed to exec launchd: %s\n", strerror(errno));
+ abort();
+}
+
+static bool testEnvVar(const char* var_name) {
+ const char* var = getenv(var_name);
+
+ if (!var) {
+ return false;
+ }
+
+ auto len = strlen(var);
+
+ if (len > 0 && (var[0] == '1' || var[0] == 't' || var[0] == 'T')) {
+ return true;
+ }
+
+ return false;
+};
+
+static void temp_drop_privileges(uid_t uid, gid_t gid) {
+ // it's important to drop GID first, because non-root users can't change their GID
+ if (setresgid(gid, gid, 0) < 0) {
+ fprintf(stderr, "Failed to temporarily drop group privileges\n");
+ exit(1);
+ }
+ if (setresuid(uid, uid, 0) < 0) {
+ fprintf(stderr, "Failed to temporarily drop user privileges\n");
+ exit(1);
+ }
+};
+
+static void perma_drop_privileges(uid_t uid, gid_t gid) {
+ if (setresgid(gid, gid, gid) < 0) {
+ fprintf(stderr, "Failed to drop group privileges\n");
+ exit(1);
+ }
+ if (setresuid(uid, uid, uid) < 0) {
+ fprintf(stderr, "Failed to drop user privileges\n");
+ exit(1);
+ }
+};
+
+static void regain_privileges() {
+ if (seteuid(0) < 0) {
+ fprintf(stderr, "Failed to regain root EUID\n");
+ exit(1);
+ }
+ if (setegid(0) < 0) {
+ fprintf(stderr, "Failed to regain root EGID\n");
+ exit(1);
+ }
+
+ if (setresuid(0, 0, 0) < 0) {
+ fprintf(stderr, "Failed to regain root privileges\n");
+ exit(1);
+ }
+ if (setresgid(0, 0, 0) < 0) {
+ fprintf(stderr, "Failed to regain root privileges\n");
+ exit(1);
+ }
+};
+
+int main(int argc, char** argv) {
+ const char* prefix = NULL;
+ uid_t originalUID = -1;
+ gid_t originalGID = -1;
+ int pipefd = -1;
+ bool fix_permissions = false;
+ pid_t launchdGlobalPID = -1;
+ size_t prefix_length = 0;
+ struct rlimit default_limit;
+ struct rlimit increased_limit;
+ FILE* nr_open_file = NULL;
+ int childWaitFDs[2];
+
+ char *opts;
+ char putOld[4096];
+ char *p;
+
+ if (argc < 6) {
+ fprintf(stderr, "darlingserver is not meant to be started manually\n");
+ exit(1);
+ }
+
+ prefix = argv[1];
+ sscanf(argv[2], "%d", &originalUID);
+ sscanf(argv[3], "%d", &originalGID);
+ sscanf(argv[4], "%d", &pipefd);
+
+ if (argv[5][0] == '1') {
+ fix_permissions = true;
+ }
+
+ prefix_length = strlen(prefix);
+
+ if (getuid() != 0 || getgid() != 0) {
+ fprintf(stderr, "darlingserver needs to start as root\n");
+ exit(1);
+ }
+
+ // temporarily drop privileges to perform some prefix work
+ temp_drop_privileges(originalUID, originalGID);
+ setupUserHome(prefix, originalUID);
+ //setupCoredumpPattern();
+ regain_privileges();
+
+ // read the default rlimit so we can restore it for our children
+ if (getrlimit(RLIMIT_NOFILE, &default_limit) != 0) {
+ fprintf(stderr, "Failed to read default FD rlimit: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ // read the system maximum
+ nr_open_file = fopen("/proc/sys/fs/nr_open", "r");
+ if (nr_open_file == NULL) {
+ fprintf(stderr, "Failed to open /proc/sys/fs/nr_open: %s\n", strerror(errno));
+ exit(1);
+ }
+ if (fscanf(nr_open_file, "%lu", &increased_limit.rlim_max) != 1) {
+ fprintf(stderr, "Failed to read /proc/sys/fs/nr_open: %s\n", strerror(errno));
+ exit(1);
+ }
+ increased_limit.rlim_cur = increased_limit.rlim_max;
+ if (fclose(nr_open_file) != 0) {
+ fprintf(stderr, "Failed to close /proc/sys/fs/nr_open: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ // now set our increased rlimit
+ if (setrlimit(RLIMIT_NOFILE, &increased_limit) != 0) {
+ fprintf(stderr, "Failed to increase FD rlimit: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ // Since overlay cannot be mounted inside user namespaces, we have to setup a new mount namespace
+ // and do the mount while we can be root
+ if (unshare(CLONE_NEWNS) != 0)
+ {
+ fprintf(stderr, "Cannot unshare PID and mount namespaces: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ // Because systemd marks / as MS_SHARED and we would inherit this into the overlay mount,
+ // causing it not to be unmounted once the init process dies.
+ if (mount(NULL, "/", NULL, MS_REC | MS_SLAVE, NULL) != 0)
+ {
+ fprintf(stderr, "Cannot remount / as slave: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ umount("/dev/shm");
+ if (mount("tmpfs", "/dev/shm", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV, NULL) != 0)
+ {
+ fprintf(stderr, "Cannot mount new /dev/shm: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ opts = (char*) malloc(strlen(prefix)*2 + sizeof(LIBEXEC_PATH) + 100);
+
+ const char* opts_fmt = "lowerdir=%s,upperdir=%s,workdir=%s.workdir,index=off";
+
+ sprintf(opts, opts_fmt, LIBEXEC_PATH, prefix, prefix);
+
+ // Mount overlay onto our prefix
+ if (mount("overlay", prefix, "overlay", 0, opts) != 0)
+ {
+ fprintf(stderr, "Cannot mount overlay: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ free(opts);
+
+ // This is executed once at prefix creation
+ if (fix_permissions) {
+ const char* extra_paths[] = {
+ "/private/etc/passwd",
+ "/private/etc/master.passwd",
+ "/private/etc/group",
+ };
+ char path[4096];
+
+ fixPermissionsRecursive(prefix, originalUID, originalGID);
+
+ path[sizeof(path) - 1] = '\0';
+ strncpy(path, prefix, sizeof(path) - 1);
+ for (size_t i = 0; i < sizeof(extra_paths) / sizeof(*extra_paths); ++i) {
+ path[prefix_length] = '\0';
+ strncat(path, extra_paths[i], sizeof(path) - 1);
+ fixPermissionsRecursive(path, originalUID, originalGID);
+ }
+ }
+
+ snprintf(putOld, sizeof(putOld), "%s/proc", prefix);
+
+ // mount procfs for our new PID namespace
+ if (mount("proc", putOld, "proc", 0, "") != 0)
+ {
+ fprintf(stderr, "Cannot mount procfs: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ // temporarily drop privileges and do some prefix work
+ temp_drop_privileges(originalUID, originalGID);
+ darlingPreInit(prefix);
+ regain_privileges();
+
+ // Tell the parent we're ready
+ write(pipefd, ".", 1);
+ close(pipefd);
+
+ if (pipe(childWaitFDs) != 0) {
+ std::cerr << "Failed to create child waiting pipe: " << strerror(errno) << std::endl;
+ exit(1);
+ }
+
+ // we have to use `clone` rather than `fork` to create the process in its own PID namespace
+ // and still be able to spawn new processes and threads of our own
+ struct clone_args launchdCloneArgs;
+ launchdCloneArgs.flags = CLONE_NEWPID;
+ launchdCloneArgs.pidfd = 0;
+ launchdCloneArgs.child_tid = 0;
+ launchdCloneArgs.parent_tid = 0;
+ launchdCloneArgs.exit_signal = SIGCHLD;
+ launchdCloneArgs.stack = 0;
+ launchdCloneArgs.stack_size = 0;
+ launchdCloneArgs.tls = 0;
+ launchdCloneArgs.set_tid = 0;
+ launchdCloneArgs.set_tid_size = 0;
+ launchdCloneArgs.cgroup = 0;
+ launchdGlobalPID = syscall(SYS_clone3, &launchdCloneArgs, sizeof(launchdCloneArgs));
+
+ // drop privileges in both parent and child
+ perma_drop_privileges(originalUID, originalGID);
+ prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
+
+ if (launchdGlobalPID < 0) {
+ fprintf(stderr, "Failed to fork to start launchd: %s\n", strerror(errno));
+ exit(1);
+ } else if (launchdGlobalPID == 0) {
+ // this is the child
+ char buf[1];
+
+ close(childWaitFDs[1]);
+
+ // decrease the FD limit back to the default
+ if (setrlimit(RLIMIT_NOFILE, &default_limit) != 0) {
+ fprintf(stderr, "Failed to decrease FD limit back down for launchd: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ // wait for the parent to give us the green light
+ read(childWaitFDs[0], buf, 1);
+ close(childWaitFDs[0]);
+
+ spawnLaunchd(prefix);
+ __builtin_unreachable();
+ }
+
+ // this is the parent
+ close(childWaitFDs[0]);
+
+ // create the server
+ auto server = new DarlingServer::Server(prefix);
+
+ // tell the child to go ahead; the socket has been created
+ write(childWaitFDs[1], ".", 1);
+ close(childWaitFDs[1]);
+
+ // start the main loop
+ server->start();
+
+ // this should never happen
+ std::cerr << "Server exited main loop!" << std::endl;
+ delete server;
+
+ return 1;
+};
diff --git a/src/darlingserver/src/logging.cpp b/src/darlingserver/src/logging.cpp
new file mode 100644
index 000000000..f30d03ccf
--- /dev/null
+++ b/src/darlingserver/src/logging.cpp
@@ -0,0 +1,87 @@
+/**
+ * This file is part of Darling.
+ *
+ * Copyright (C) 2021 Darling developers
+ *
+ * Darling is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Darling is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Darling. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+DarlingServer::Log::Log(std::string category):
+ _category(category)
+ {};
+
+DarlingServer::Log::Stream::Stream(Type type, Log& log):
+ _type(type),
+ _log(log)
+ {};
+template<>
+DarlingServer::Log::Stream& DarlingServer::Log::Stream::operator<<(EndLog value) {
+ _log._log(_type, _buffer.str());
+ return *this;
+};
+
+DarlingServer::Log::Stream DarlingServer::Log::debug() {
+ return Stream(Type::Debug, *this);
+};
+
+DarlingServer::Log::Stream DarlingServer::Log::info() {
+ return Stream(Type::Info, *this);
+};
+
+DarlingServer::Log::Stream DarlingServer::Log::warning() {
+ return Stream(Type::Warning, *this);
+};
+
+DarlingServer::Log::Stream DarlingServer::Log::error() {
+ return Stream(Type::Error, *this);
+};
+
+std::string DarlingServer::Log::_typeToString(Type type) {
+ switch (type) {
+ case Type::Debug:
+ return "Debug";
+ case Type::Info:
+ return "Info";
+ case Type::Warning:
+ return "Warning";
+ case Type::Error:
+ return "Error";
+ default:
+ return "Unknown";
+ }
+};
+
+
+void DarlingServer::Log::_log(Type type, std::string message) {
+ // NOTE: we use POSIX file APIs because we want to append each message to the log file atomically,
+ // and as far as i can tell, C++ fstreams provide no such guarantee (that they won't write in chunks).
+ static int logFile = []() {
+ std::filesystem::path path(Server::sharedInstance().prefix() + "/private/var/log/");
+ std::filesystem::create_directories(path.parent_path());
+ return open(path.c_str(), O_WRONLY | O_APPEND);
+ }();
+
+ struct timespec time;
+ clock_gettime(CLOCK_REALTIME, &time);
+ double secs = (double)time.tv_sec + ((double)time.tv_nsec / 1.0e9);
+ std::string messageToLog = "[" + std::to_string(secs) + "](" + _category + ", " + _typeToString(type) + ") " + message + "\n";
+
+ write(logFile, messageToLog.c_str(), messageToLog.size());
+};
diff --git a/src/darlingserver/src/message.cpp b/src/darlingserver/src/message.cpp
new file mode 100644
index 000000000..e6a154540
--- /dev/null
+++ b/src/darlingserver/src/message.cpp
@@ -0,0 +1,539 @@
+/**
+ * This file is part of Darling.
+ *
+ * Copyright (C) 2021 Darling developers
+ *
+ * Darling is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Darling is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Darling. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+DarlingServer::Address::Address() {
+ _address.sun_family = AF_UNIX;
+ memset(_address.sun_path, 0, sizeof(_address.sun_path));
+ _size = sizeof(_address);
+};
+
+DarlingServer::Address::Address(const struct sockaddr_un& rawAddress, size_t addressLength) {
+ _address = rawAddress;
+ _size = addressLength;
+};
+
+struct sockaddr_un& DarlingServer::Address::raw() {
+ return _address;
+};
+
+const struct sockaddr_un& DarlingServer::Address::raw() const {
+ return _address;
+};
+
+size_t DarlingServer::Address::rawSize() const {
+ return _size;
+};
+
+void DarlingServer::Address::setRawSize(size_t newRawSize) {
+ _size = newRawSize;
+};
+
+DarlingServer::Message::Message(size_t bufferSpace, size_t descriptorSpace) {
+ size_t controlLen = CMSG_SPACE(sizeof(struct ucred)) + (descriptorSpace > 0 ? CMSG_SPACE(sizeof(int) * descriptorSpace) : 0);
+ _controlHeader = static_cast(malloc(controlLen));
+
+ if (!_controlHeader) {
+ throw std::bad_alloc();
+ }
+
+ _controlHeader->cmsg_len = CMSG_LEN(sizeof(struct ucred));
+ _controlHeader->cmsg_level = SOL_SOCKET;
+ _controlHeader->cmsg_type = SCM_CREDENTIALS;
+ struct ucred ourCreds;
+ ourCreds.pid = getpid();
+ ourCreds.uid = getuid();
+ ourCreds.gid = getgid();
+ // the cmsg man page says memcpy should be used with CMSG_DATA instead of direct pointer access
+ memcpy(CMSG_DATA(_controlHeader), &ourCreds, sizeof(ourCreds));
+
+ if (descriptorSpace > 0) {
+ auto fdHeader = reinterpret_cast(reinterpret_cast(_controlHeader) + CMSG_SPACE(sizeof(struct ucred)));
+ fdHeader->cmsg_len = CMSG_LEN(sizeof(int) * descriptorSpace);
+ fdHeader->cmsg_level = SOL_SOCKET;
+ fdHeader->cmsg_type = SCM_RIGHTS;
+ for (size_t i = 0; i < descriptorSpace; ++i) {
+ const int fd = -1;
+ memcpy(CMSG_DATA(fdHeader) + (sizeof(int) * i), &fd, sizeof(int));
+ }
+ }
+
+ _buffer.resize(bufferSpace);
+
+ _header.msg_name = &_socketAddress.raw();
+ _header.msg_namelen = _socketAddress.rawSize();
+ _header.msg_iov = &_dataDescriptor;
+ _header.msg_iovlen = 1;
+ _header.msg_control = _controlHeader;
+ _header.msg_controllen = controlLen;
+ _header.msg_flags = 0;
+
+ _dataDescriptor.iov_base = _buffer.data();
+ _dataDescriptor.iov_len = _buffer.capacity();
+};
+
+void DarlingServer::Message::_initWithOther(Message&& other) {
+ _buffer = std::move(other._buffer);
+ _socketAddress = std::move(other._socketAddress);
+ _controlHeader = std::move(other._controlHeader);
+
+ other._controlHeader = nullptr;
+
+ _header = std::move(other._header);
+ _header.msg_name = &_socketAddress.raw();
+ _header.msg_iov = &_dataDescriptor;
+
+ _dataDescriptor.iov_base = _buffer.data();
+ _dataDescriptor.iov_len = _buffer.capacity();
+};
+
+void DarlingServer::Message::_cleanupSelf() {
+ if (_controlHeader) {
+ auto fdHeader = _descriptorHeader();
+ if (fdHeader) {
+ for (size_t i = 0; i < _descriptorSpace(); ++i) {
+ int fd = -1;
+ memcpy(&fd, CMSG_DATA(fdHeader) + (sizeof(int) * i), sizeof(int));
+ if (fd != -1) {
+ close(fd);
+ }
+ }
+ }
+ free(_controlHeader);
+ _controlHeader = nullptr;
+ }
+};
+
+struct cmsghdr* DarlingServer::Message::_credentialsHeader() {
+ return const_cast(const_cast(this)->_credentialsHeader());
+};
+
+const struct cmsghdr* DarlingServer::Message::_credentialsHeader() const {
+ const struct cmsghdr* hdr = CMSG_FIRSTHDR(&_header);
+
+ while (hdr) {
+ if (hdr->cmsg_level == SOL_SOCKET && hdr->cmsg_type == SCM_CREDENTIALS) {
+ return hdr;
+ }
+
+ hdr = CMSG_NXTHDR(const_cast(&_header), const_cast(hdr));
+ }
+
+ return nullptr;
+};
+
+struct cmsghdr* DarlingServer::Message::_descriptorHeader() {
+ return const_cast(const_cast(this)->_descriptorHeader());
+};
+
+const struct cmsghdr* DarlingServer::Message::_descriptorHeader() const {
+ const struct cmsghdr* hdr = CMSG_FIRSTHDR(&_header);
+
+ while (hdr) {
+ if (hdr->cmsg_level == SOL_SOCKET && hdr->cmsg_type == SCM_RIGHTS) {
+ return hdr;
+ }
+
+ hdr = CMSG_NXTHDR(const_cast(&_header), const_cast(hdr));
+ }
+
+ return nullptr;
+};
+
+size_t DarlingServer::Message::_descriptorSpace() const {
+ auto hdr = _descriptorHeader();
+
+ if (!hdr) {
+ return 0;
+ }
+
+ return (hdr->cmsg_len - (reinterpret_cast(CMSG_DATA(hdr)) - reinterpret_cast(hdr))) / sizeof(int);
+};
+
+DarlingServer::Message::~Message() {
+ _cleanupSelf();
+};
+
+DarlingServer::Message::Message(Message&& other) {
+ _initWithOther(std::move(other));
+};
+
+DarlingServer::Message& DarlingServer::Message::operator=(Message&& other) {
+ _cleanupSelf();
+ _initWithOther(std::move(other));
+ return *this;
+};
+
+struct msghdr& DarlingServer::Message::rawHeader() {
+ return _header;
+};
+
+const struct msghdr& DarlingServer::Message::rawHeader() const {
+ return _header;
+};
+
+std::vector& DarlingServer::Message::data() {
+ return _buffer;
+};
+
+const std::vector& DarlingServer::Message::data() const {
+ return _buffer;
+};
+
+DarlingServer::Address DarlingServer::Message::address() const {
+ return _socketAddress;
+};
+
+void DarlingServer::Message::setAddress(Address address) {
+ _socketAddress = address;
+ _header.msg_namelen = _socketAddress.rawSize();
+};
+
+std::vector DarlingServer::Message::descriptors() const {
+ std::vector descriptors;
+ auto fdHeader = _descriptorHeader();
+
+ if (fdHeader) {
+ for (size_t i = 0; i < _descriptorSpace(); ++i) {
+ int fd = -1;
+ memcpy(&fd, CMSG_DATA(fdHeader) + (sizeof(int) * i), sizeof(int));
+ if (fd != -1) {
+ descriptors.push_back(fd);
+ }
+ }
+ }
+
+ return descriptors;
+};
+
+void DarlingServer::Message::pushDescriptor(int descriptor) {
+ if (auto fdHeader = _descriptorHeader()) {
+ for (size_t i = 0; i < _descriptorSpace(); ++i) {
+ int fd = -1;
+ memcpy(&fd, CMSG_DATA(fdHeader) + (sizeof(int) * i), sizeof(int));
+ if (fd != -1) {
+ continue;
+ }
+
+ memcpy(CMSG_DATA(fdHeader) + (sizeof(int) * i), &descriptor, sizeof(int));
+ return;
+ }
+ }
+
+ auto oldSpace = _descriptorSpace();
+ _ensureDescriptorHeader(oldSpace + 1);
+ memcpy(CMSG_DATA(_descriptorHeader()) + (sizeof(int) * oldSpace), &descriptor, sizeof(int));
+};
+
+int DarlingServer::Message::extractDescriptor(int descriptor) {
+ if (auto fdHeader = _descriptorHeader()) {
+ for (size_t i = 0; i < _descriptorSpace(); ++i) {
+ int fd = -1;
+ memcpy(&fd, CMSG_DATA(fdHeader) + (sizeof(int) * i), sizeof(int));
+ if (fd != descriptor) {
+ continue;
+ }
+
+ fd = -1;
+ memcpy(CMSG_DATA(fdHeader) + (sizeof(int) * i), &fd, sizeof(int));
+ return descriptor;
+ }
+ }
+
+ return -1;
+};
+
+int DarlingServer::Message::extractDescriptorAtIndex(size_t index) {
+ if (auto fdHeader = _descriptorHeader()) {
+ for (size_t i = 0, presentIndex = 0; i < _descriptorSpace(); ++i) {
+ int fd = -1;
+ memcpy(&fd, CMSG_DATA(fdHeader) + (sizeof(int) * i), sizeof(int));
+ if (fd == -1) {
+ continue;
+ }
+
+ if (presentIndex++ != index) {
+ continue;
+ }
+
+ int tmp = -1;
+ memcpy(CMSG_DATA(fdHeader) + (sizeof(int) * i), &tmp, sizeof(int));
+ return fd;
+ }
+ }
+
+ return -1;
+};
+
+void DarlingServer::Message::replaceDescriptors(const std::vector& newDescriptors) {
+ _ensureDescriptorHeader(newDescriptors.size());
+ memcpy(CMSG_DATA(_descriptorHeader()), newDescriptors.data(), sizeof(int) * newDescriptors.size());
+};
+
+bool DarlingServer::Message::copyCredentialsOut(struct ucred& outputCredentials) const {
+ auto credHeader = _credentialsHeader();
+
+ if (credHeader) {
+ memcpy(&outputCredentials, CMSG_DATA(credHeader), sizeof(outputCredentials));
+ return true;
+ } else {
+ return false;
+ }
+};
+
+void DarlingServer::Message::copyCredentialsIn(const struct ucred& inputCredentials) {
+ _ensureCredentialsHeader();
+ memcpy(CMSG_DATA(_credentialsHeader()), &inputCredentials, sizeof(inputCredentials));
+};
+
+void DarlingServer::Message::_ensureCredentialsHeader() {
+ if (_credentialsHeader()) {
+ return;
+ }
+
+ auto fdHeader = _descriptorHeader();
+ auto descSpace = _descriptorSpace();
+ size_t controlLen = CMSG_SPACE(sizeof(struct ucred)) + (descSpace > 0 ? CMSG_SPACE(sizeof(int) * descSpace) : 0);
+ auto tmp = static_cast(malloc(controlLen));
+ auto old = _controlHeader;
+ struct cmsghdr* credHeader = nullptr;
+
+ if (!tmp) {
+ throw std::bad_alloc();
+ }
+
+ _controlHeader = tmp;
+ _header.msg_control = _controlHeader;
+
+ credHeader = _controlHeader;
+
+ if (descSpace > 0) {
+ auto newFdHeader = reinterpret_cast(reinterpret_cast(_controlHeader) + CMSG_SPACE(sizeof(struct ucred)));
+ memcpy(newFdHeader, fdHeader, CMSG_SPACE(sizeof(int) * descSpace));
+ }
+
+ free(old);
+
+ _header.msg_controllen = controlLen;
+
+ credHeader->cmsg_len = CMSG_LEN(sizeof(struct ucred));
+ credHeader->cmsg_level = SOL_SOCKET;
+ credHeader->cmsg_type = SCM_CREDENTIALS;
+
+ struct ucred creds;
+ creds.pid = getpid();
+ creds.uid = getuid();
+ creds.gid = getgid();
+ memcpy(CMSG_DATA(credHeader), &creds, sizeof(struct ucred));
+};
+
+void DarlingServer::Message::_ensureDescriptorHeader(size_t newSpace) {
+ if (_descriptorSpace() == newSpace) {
+ return;
+ }
+
+ auto credHeader = _credentialsHeader();
+ size_t oldSpace = _descriptorSpace();
+ size_t controlLen = (credHeader ? CMSG_SPACE(sizeof(struct ucred)) : 0) + (newSpace == 0 ? 0 : CMSG_SPACE(sizeof(int) * newSpace));
+
+ if (controlLen == 0) {
+ free(_controlHeader);
+ _controlHeader = nullptr;
+ _header.msg_control = nullptr;
+ _header.msg_controllen = 0;
+ return;
+ }
+
+ auto tmp = static_cast(malloc(controlLen));
+ auto old = _controlHeader;
+ struct cmsghdr* fdHeader = nullptr;
+
+ if (!tmp) {
+ throw std::bad_alloc();
+ }
+
+ _controlHeader = tmp;
+ _header.msg_control = _controlHeader;
+
+ if (credHeader) {
+ memcpy(_controlHeader, credHeader, CMSG_SPACE(sizeof(struct ucred)));
+ fdHeader = reinterpret_cast(reinterpret_cast(_controlHeader) + CMSG_SPACE(sizeof(struct ucred)));
+ } else {
+ fdHeader = _controlHeader;
+ }
+
+ free(old);
+
+ _header.msg_controllen = controlLen;
+
+ fdHeader->cmsg_len = CMSG_LEN(sizeof(int) * newSpace);
+ fdHeader->cmsg_level = SOL_SOCKET;
+ fdHeader->cmsg_type = SCM_RIGHTS;
+
+ for (size_t i = oldSpace; i < newSpace; ++i) {
+ int fd = -1;
+ memcpy(CMSG_DATA(fdHeader) + (sizeof(int) * i), &fd, sizeof(int));
+ }
+};
+
+pid_t DarlingServer::Message::pid() const {
+ struct ucred creds;
+
+ if (copyCredentialsOut(creds)) {
+ return creds.pid;
+ } else {
+ return -1;
+ }
+};
+
+void DarlingServer::Message::setPID(pid_t pid) {
+ _ensureCredentialsHeader();
+ struct ucred creds;
+ copyCredentialsOut(creds);
+ creds.pid = pid;
+ copyCredentialsIn(creds);
+};
+
+uid_t DarlingServer::Message::uid() const {
+ struct ucred creds;
+
+ if (copyCredentialsOut(creds)) {
+ return creds.uid;
+ } else {
+ return -1;
+ }
+};
+
+void DarlingServer::Message::setUID(uid_t uid) {
+ _ensureCredentialsHeader();
+ struct ucred creds;
+ copyCredentialsOut(creds);
+ creds.uid = uid;
+ copyCredentialsIn(creds);
+};
+
+gid_t DarlingServer::Message::gid() const {
+ struct ucred creds;
+
+ if (copyCredentialsOut(creds)) {
+ return creds.gid;
+ } else {
+ return -1;
+ }
+};
+
+void DarlingServer::Message::setGID(gid_t gid) {
+ _ensureCredentialsHeader();
+ struct ucred creds;
+ copyCredentialsOut(creds);
+ creds.gid = gid;
+ copyCredentialsIn(creds);
+};
+
+void DarlingServer::MessageQueue::push(Message&& message) {
+ std::scoped_lock lock(_lock);
+ _messages.push_back(std::move(message));
+};
+
+std::optional DarlingServer::MessageQueue::pop() {
+ std::scoped_lock lock(_lock);
+ if (_messages.size() > 0) {
+ Message message = std::move(_messages.front());
+ _messages.pop_front();
+ return message;
+ } else {
+ return std::nullopt;
+ }
+};
+
+void DarlingServer::MessageQueue::sendMany(int socket) {
+ std::scoped_lock lock(_lock);
+ struct mmsghdr mmsgs[16];
+ size_t len = 0;
+ int ret = 0;
+
+ while (ret >= 0) {
+ len = 0;
+ for (size_t i = 0; i < _messages.size() && i < sizeof(mmsgs) / sizeof(*mmsgs); ++i) {
+ mmsgs[i].msg_hdr = _messages[i].rawHeader();
+ mmsgs[i].msg_len = 0;
+ ++len;
+ }
+
+ if (len == 0) {
+ break;
+ }
+
+ ret = sendmmsg(socket, mmsgs, len, MSG_DONTWAIT);
+
+ if (ret < 0) {
+ if (errno == EAGAIN) {
+ break;
+ } else if (errno == EINTR) {
+ ret = 0;
+ } else {
+ throw std::system_error(errno, std::generic_category(), "Failed to send messages through socket");
+ }
+ }
+
+ for (size_t i = 0; i < ret; ++i) {
+ _messages.pop_front();
+ }
+ }
+};
+
+void DarlingServer::MessageQueue::receiveMany(int socket) {
+ std::scoped_lock lock(_lock);
+ struct mmsghdr mmsgs[16];
+ int ret = 0;
+
+ while (ret >= 0) {
+ std::array messages;
+
+ for (size_t i = 0; i < messages.size(); ++i) {
+ mmsgs[i].msg_hdr = messages[i].rawHeader();
+ mmsgs[i].msg_len = 0;
+ }
+
+ ret = recvmmsg(socket, mmsgs, sizeof(mmsgs) / sizeof(*mmsgs), MSG_CMSG_CLOEXEC | MSG_DONTWAIT, nullptr);
+
+ if (ret < 0) {
+ if (errno == EAGAIN) {
+ break;
+ } else if (errno == EINTR) {
+ ret = 0;
+ } else {
+ throw std::system_error(errno, std::generic_category(), "Failed to receive messages through socket");
+ }
+ }
+
+ for (size_t i = 0; i < ret; ++i) {
+ messages[i].rawHeader() = mmsgs[i].msg_hdr;
+ messages[i].data().resize(mmsgs[i].msg_len);
+ messages[i].setAddress(Address(*(const struct sockaddr_un*)mmsgs[i].msg_hdr.msg_name, mmsgs[i].msg_hdr.msg_namelen));
+ _messages.push_back(std::move(messages[i]));
+ }
+ }
+};
diff --git a/src/darlingserver/src/process.cpp b/src/darlingserver/src/process.cpp
new file mode 100644
index 000000000..95ca21b76
--- /dev/null
+++ b/src/darlingserver/src/process.cpp
@@ -0,0 +1,83 @@
+/**
+ * This file is part of Darling.
+ *
+ * Copyright (C) 2021 Darling developers
+ *
+ * Darling is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Darling is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Darling. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+
+DarlingServer::Process::Process(ID id, NSID nsid):
+ _pid(id),
+ _nspid(nsid)
+{
+ _pidfd = syscall(SYS_pidfd_open, _pid, 0);
+ if (_pidfd < 0) {
+ throw std::system_error(errno, std::generic_category(), "Failed to open pidfd for process");
+ }
+};
+
+DarlingServer::Process::~Process() {
+ close(_pidfd);
+
+ _unregisterThreads();
+};
+
+void DarlingServer::Process::_unregisterThreads() {
+ std::unique_lock lock(_rwlock);
+ while (!_threads.empty()) {
+ auto thread = _threads.front().lock();
+ lock.unlock();
+ if (thread) {
+ thread->_process = std::weak_ptr();
+ threadRegistry().unregisterEntry(thread);
+ }
+ lock.lock();
+ }
+};
+
+DarlingServer::Process::ID DarlingServer::Process::id() const {
+ return _pid;
+};
+
+DarlingServer::Process::NSID DarlingServer::Process::nsid() const {
+ return _nspid;
+};
+
+std::vector> DarlingServer::Process::threads() const {
+ std::vector> result;
+ std::shared_lock lock(_rwlock);
+
+ for (auto& maybeThread: _threads) {
+ if (auto thread = maybeThread.lock()) {
+ result.push_back(thread);
+ }
+ }
+
+ return result;
+};
+
+std::string DarlingServer::Process::vchrootPath() const {
+ std::shared_lock lock(_rwlock);
+ return _vchrootPath;
+};
+
+void DarlingServer::Process::setVchrootPath(std::string path) {
+ std::unique_lock lock(_rwlock);
+ _vchrootPath = path;
+};
diff --git a/src/darlingserver/src/registry.cpp b/src/darlingserver/src/registry.cpp
new file mode 100644
index 000000000..1fecf8795
--- /dev/null
+++ b/src/darlingserver/src/registry.cpp
@@ -0,0 +1,30 @@
+/**
+ * This file is part of Darling.
+ *
+ * Copyright (C) 2021 Darling developers
+ *
+ * Darling is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Darling is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Darling. If not, see .
+ */
+
+#include
+
+DarlingServer::Registry& DarlingServer::processRegistry() {
+ static Registry registry;
+ return registry;
+};
+
+DarlingServer::Registry& DarlingServer::threadRegistry() {
+ static Registry registry;
+ return registry;
+};
diff --git a/src/darlingserver/src/server.cpp b/src/darlingserver/src/server.cpp
new file mode 100644
index 000000000..139e363ef
--- /dev/null
+++ b/src/darlingserver/src/server.cpp
@@ -0,0 +1,152 @@
+/**
+ * This file is part of Darling.
+ *
+ * Copyright (C) 2021 Darling developers
+ *
+ * Darling is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Darling is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Darling. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+static DarlingServer::Server* sharedInstancePointer = nullptr;
+
+DarlingServer::Server::Server(std::string prefix):
+ _prefix(prefix),
+ _socketPath(_prefix + "/var/run/darlingserver.sock"),
+ _workQueue(std::bind(&Server::_worker, this, std::placeholders::_1))
+{
+ sharedInstancePointer = this;
+
+ // remove the old socket (if it exists)
+ unlink(_socketPath.c_str());
+
+ // create the socket
+ _listenerSocket = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
+ if (_listenerSocket < 0) {
+ throw std::system_error(errno, std::generic_category(), "Failed to create socket");
+ }
+
+ int passCred = 1;
+ if (setsockopt(_listenerSocket, SOL_SOCKET, SO_PASSCRED, &passCred, sizeof(passCred)) < 0) {
+ throw std::system_error(errno, std::generic_category(), "Failed to set SO_PASSCRED on socket");
+ }
+
+ struct sockaddr_un addr;
+ addr.sun_family = AF_UNIX;
+ addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
+ strncpy(addr.sun_path, _socketPath.c_str(), sizeof(addr.sun_path) - 1);
+
+ if (bind(_listenerSocket, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
+ throw std::system_error(errno, std::generic_category(), "Failed to bind socket");
+ }
+
+ _epollFD = epoll_create1(EPOLL_CLOEXEC);
+ if (_epollFD < 0) {
+ throw std::system_error(errno, std::generic_category(), "Failed to create epoll context");
+ }
+
+ struct epoll_event settings;
+ settings.data.ptr = this;
+ settings.events = EPOLLIN | EPOLLOUT;
+
+ if (epoll_ctl(_epollFD, EPOLL_CTL_ADD, _listenerSocket, &settings) < 0) {
+ throw std::system_error(errno, std::generic_category(), "Failed to add listener socket to epoll context");
+ }
+};
+
+DarlingServer::Server::~Server() {
+ close(_epollFD);
+ close(_listenerSocket);
+ unlink(_socketPath.c_str());
+};
+
+void DarlingServer::Server::start() {
+ while (true) {
+ struct epoll_event events[16];
+ int ret = epoll_wait(_epollFD, events, 16, -1);
+
+ if (ret < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+
+ throw std::system_error(errno, std::generic_category(), "Failed to wait on epoll context");
+ }
+
+ for (size_t i = 0; i < ret; ++i) {
+ struct epoll_event* event = &events[i];
+
+ if (event->data.ptr == this) {
+ if (event->events & EPOLLIN) {
+ _inbox.receiveMany(_listenerSocket);
+
+ // TODO: receive messages directly onto the work queue
+ while (auto msg = _inbox.pop()) {
+ _workQueue.push(std::move(msg.value()));
+ }
+ }
+
+ if (event->events & EPOLLOUT) {
+ _outbox.sendMany(_listenerSocket);
+ }
+ } else if (event->events & EPOLLIN) {
+ std::shared_ptr& process = *reinterpret_cast*>(event->data.ptr);
+
+ if (epoll_ctl(_epollFD, EPOLL_CTL_DEL, process->_pidfd, NULL) < 0) {
+ throw std::system_error(errno, std::generic_category(), "Failed to remove process handle from epoll context");
+ }
+
+ process->_unregisterThreads();
+ processRegistry().unregisterEntry(process);
+
+ delete &process;
+ }
+ }
+ }
+};
+
+void DarlingServer::Server::monitorProcess(std::shared_ptr process) {
+ struct epoll_event settings;
+ settings.data.ptr = new std::shared_ptr(process);
+ settings.events = EPOLLIN;
+
+ if (epoll_ctl(_epollFD, EPOLL_CTL_ADD, process->_pidfd, &settings) < 0) {
+ throw std::system_error(errno, std::generic_category(), "Failed to add process descriptor to epoll context");
+ }
+};
+
+DarlingServer::Server& DarlingServer::Server::sharedInstance() {
+ return *sharedInstancePointer;
+};
+
+std::string DarlingServer::Server::prefix() const {
+ return _prefix;
+};
+
+void DarlingServer::Server::_worker(DarlingServer::Message message) {
+ auto call = DarlingServer::Call::callFromMessage(std::move(message), _outbox);
+ call->processCall();
+};
diff --git a/src/darlingserver/src/thread.cpp b/src/darlingserver/src/thread.cpp
new file mode 100644
index 000000000..2d83cdcf2
--- /dev/null
+++ b/src/darlingserver/src/thread.cpp
@@ -0,0 +1,122 @@
+/**
+ * This file is part of Darling.
+ *
+ * Copyright (C) 2021 Darling developers
+ *
+ * Darling is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Darling is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Darling. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+
+DarlingServer::Thread::Thread(std::shared_ptr process, NSID nsid):
+ _nstid(nsid),
+ _process(process)
+{
+ _tid = -1;
+
+ for (const auto& entry: std::filesystem::directory_iterator("/proc/" + std::to_string(process->id()) + "/task")) {
+ std::ifstream statusFile(entry.path() / "status");
+ std::string line;
+
+ while (std::getline(statusFile, line)) {
+ if (line.substr(0, sizeof("NSpid") - 1) == "NSpid") {
+ auto pos = line.find_last_of('\t');
+ std::string id;
+
+ if (pos != line.npos) {
+ id = line.substr(pos + 1);
+ }
+
+ if (id.empty()) {
+ throw std::runtime_error("Failed to parse thread ID");
+ }
+
+ if (std::stoi(id) != _nstid) {
+ continue;
+ }
+
+ _tid = std::stoi(entry.path().filename().string());
+
+ break;
+ }
+ }
+ }
+
+ if (_tid == -1) {
+ throw std::runtime_error("Failed to find thread ID within darlingserver's namespace");
+ }
+};
+
+void DarlingServer::Thread::registerWithProcess() {
+ auto process = _process.lock();
+ std::unique_lock lock(process->_rwlock);
+ process->_threads.push_back(shared_from_this());
+};
+
+DarlingServer::Thread::~Thread() noexcept(false) {
+ auto process = _process.lock();
+ if (!process) {
+ // the process is unregistering us
+ return;
+ }
+
+ std::unique_lock lock(process->_rwlock);
+ auto it = process->_threads.begin();
+ while (it != process->_threads.end()) {
+ if (auto thread = it->lock()) {
+ if (thread.get() == this) {
+ break;
+ }
+ }
+ }
+ if (it == process->_threads.end()) {
+ throw std::runtime_error("Thread was not registered with Process");
+ }
+ process->_threads.erase(it);
+};
+
+DarlingServer::Thread::ID DarlingServer::Thread::id() const {
+ return _tid;
+};
+
+DarlingServer::Thread::NSID DarlingServer::Thread::nsid() const {
+ return _nstid;
+};
+
+std::shared_ptr DarlingServer::Thread::process() const {
+ return _process.lock();
+};
+
+std::shared_ptr DarlingServer::Thread::pendingCall() const {
+ std::shared_lock lock(_rwlock);
+ return _pendingCall;
+};
+
+void DarlingServer::Thread::setPendingCall(std::shared_ptr newPendingCall) {
+ std::unique_lock lock(_rwlock);
+ _pendingCall = newPendingCall;
+};
+
+DarlingServer::Address DarlingServer::Thread::address() const {
+ std::shared_lock lock(_rwlock);
+ return _address;
+};
+
+void DarlingServer::Thread::setAddress(Address address) {
+ std::unique_lock lock(_rwlock);
+ _address = address;
+};
diff --git a/src/external/cocotron b/src/external/cocotron
index d7cc4e3ff..7edb4834c 160000
--- a/src/external/cocotron
+++ b/src/external/cocotron
@@ -1 +1 @@
-Subproject commit d7cc4e3ff153f8986b911fbb0a7f7b9d6d74022c
+Subproject commit 7edb4834c7af363e92183f7720c8991d2c518e2a
diff --git a/src/external/darling-dmg b/src/external/darling-dmg
index 34d3a3460..8a2831704 160000
--- a/src/external/darling-dmg
+++ b/src/external/darling-dmg
@@ -1 +1 @@
-Subproject commit 34d3a346039f25a407c64aca1c7ecbdd9af85933
+Subproject commit 8a28317048006b2379b724bc32adad9dd0523f02
diff --git a/src/kernel/emulation/linux/bsdthread/workq_kernreturn.c b/src/kernel/emulation/linux/bsdthread/workq_kernreturn.c
index 9cd0e5be6..c209f5445 100644
--- a/src/kernel/emulation/linux/bsdthread/workq_kernreturn.c
+++ b/src/kernel/emulation/linux/bsdthread/workq_kernreturn.c
@@ -19,7 +19,7 @@
#include
// much easier to include than libpthread's `internal.h`
-#include "../../../../libelfloader/native/dthreads.h"
+#include "../../../../startup/mldr/elfcalls/dthreads.h"
#define WQ_MAX_THREADS 64
diff --git a/src/kernel/emulation/linux/elfcalls_wrapper.c b/src/kernel/emulation/linux/elfcalls_wrapper.c
index 213667ca7..39f8d76d3 100644
--- a/src/kernel/emulation/linux/elfcalls_wrapper.c
+++ b/src/kernel/emulation/linux/elfcalls_wrapper.c
@@ -1,21 +1,24 @@
#include "elfcalls_wrapper.h"
#include
#include
+#include "simple.h"
-static struct elf_calls* _elfcalls;
+extern struct elf_calls* _elfcalls;
struct elf_calls* elfcalls(void)
{
if (!_elfcalls)
{
- void* module = dlopen("/usr/lib/darling/libelfloader.dylib", RTLD_NOW);
+ //void* module = dlopen("/usr/lib/darling/libelfloader.dylib", RTLD_NOW);
// if (!module)
// __simple_printf("Load error: %s\n", dlerror());
// struct elf_calls** ptr = (struct elf_calls**) dlsym(module, "_elfcalls");
// __simple_printf("_elfcalls is at %p\n", ptr);
// __simple_printf("*_elfcalls = %p\n", *ptr);
- _elfcalls = *(struct elf_calls**) dlsym(module, "_elfcalls");
+ //_elfcalls = *(struct elf_calls**) dlsym(module, "_elfcalls");
+ __simple_printf("elfcalls not found?\n");
+ __simple_abort();
}
return _elfcalls;
}
diff --git a/src/kernel/emulation/linux/mach/lkm.c b/src/kernel/emulation/linux/mach/lkm.c
index ab93b03a5..bbc368dc3 100644
--- a/src/kernel/emulation/linux/mach/lkm.c
+++ b/src/kernel/emulation/linux/mach/lkm.c
@@ -9,6 +9,7 @@
#include "../../../libsyscall/wrappers/_libkernel_init.h"
#include "../simple.h"
#include "../misc/ioctl.h"
+#include
extern int sys_open(const char*, int, int);
extern int close_internal(int);
@@ -22,8 +23,15 @@ extern _libkernel_functions_t _libkernel_functions;
static int driver_fd = -1;
+VISIBLE
+struct elf_calls* _elfcalls;
+
void mach_driver_init(const char** applep)
{
+ // DARLINGSERVER/MLDR TESTING
+ __simple_printf("We're being initialized...\n");
+ __builtin_unreachable();
+
#ifdef VARIANT_DYLD
if (applep != NULL)
{
@@ -33,7 +41,11 @@ void mach_driver_init(const char** applep)
if (strncmp(applep[i], "kernfd=", 7) == 0)
{
driver_fd = __simple_atoi(applep[i] + 7, NULL);
- break;
+ }
+ if (strncmp(applep[i], "elf_calls=", 10) == 0)
+ {
+ uintptr_t table = (uintptr_t) __simple_atoi16(applep[i] + 10, NULL);
+ _elfcalls = (struct elf_calls*) table;
}
}
}
diff --git a/src/kernel/emulation/linux/simple.c b/src/kernel/emulation/linux/simple.c
index 1fe64ac0b..3b4ca42d7 100644
--- a/src/kernel/emulation/linux/simple.c
+++ b/src/kernel/emulation/linux/simple.c
@@ -5,6 +5,8 @@
#include
#include
#include "mach/lkm.h"
+#include "signal/kill.h"
+#include
extern char* memchr(char* buf, int c, __SIZE_TYPE__ n);
@@ -569,3 +571,8 @@ have_nl:
return out;
}
+__attribute__ ((visibility ("default")))
+void __simple_abort(void) {
+ sys_kill(0, SIGABRT, 1);
+ __builtin_unreachable();
+};
diff --git a/src/kernel/emulation/linux/simple.h b/src/kernel/emulation/linux/simple.h
index 30450d72e..6185eff2e 100644
--- a/src/kernel/emulation/linux/simple.h
+++ b/src/kernel/emulation/linux/simple.h
@@ -34,6 +34,9 @@ struct simple_readline_buf
void __simple_readline_init(struct simple_readline_buf* buf);
char* __simple_readline(int fd, struct simple_readline_buf* buf, char* out, int max_out);
+__attribute__((noreturn))
+void __simple_abort(void);
+
#ifdef __cplusplus
};
#endif
diff --git a/src/kernel/libsyscall/wrappers/_libkernel_init.c b/src/kernel/libsyscall/wrappers/_libkernel_init.c
index af65a13ee..c873b9d9e 100644
--- a/src/kernel/libsyscall/wrappers/_libkernel_init.c
+++ b/src/kernel/libsyscall/wrappers/_libkernel_init.c
@@ -33,9 +33,8 @@
#include "_libkernel_init.h"
#ifdef DARLING
-#include
-
extern int mach_init(const char** applep);
+extern void sigexc_setup(void);
#else
extern int mach_init(void);
#endif
@@ -49,44 +48,21 @@ bool _os_xbs_chrooted;
/* dlsym() funcptr is for legacy support in exc_catcher */
void* (*_dlsym)(void*, const char*) __attribute__((visibility("hidden")));
-#ifdef DARLING
-extern int strncmp(const char *s1, const char *s2, __SIZE_TYPE__ n);
-extern unsigned long long __simple_atoi16(const char* str, const char** endp);
-#endif
-
__attribute__((visibility("hidden")))
_libkernel_functions_t _libkernel_functions;
-#ifdef DARLING
-__attribute__((visibility("hidden")))
-struct elf_calls* _elfcalls;
-#endif
-
void
__libkernel_init(_libkernel_functions_t fns,
const char *envp[] __attribute__((unused)),
const char *apple[],
const struct ProgramVars *vars __attribute__((unused)))
{
-#ifdef DARLING
- int i;
-#endif
-
_libkernel_functions = fns;
if (fns->dlsym) {
_dlsym = fns->dlsym;
}
#ifdef DARLING
- for (i = 0; apple[i] != NULL; i++)
- {
- if (strncmp(apple[i], "elf_calls=", 10) == 0)
- {
- uintptr_t table = (uintptr_t) __simple_atoi16(apple[i] + 10, NULL);
- _elfcalls = (struct elf_calls*) table;
- }
- }
-
mach_init(apple);
sigexc_setup();
#else
diff --git a/src/libelfloader/CMakeLists.txt b/src/libelfloader/CMakeLists.txt
deleted file mode 100644
index 1f30d7e30..000000000
--- a/src/libelfloader/CMakeLists.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-project(libelfloader)
-
-set(elfloader_sources
- loader.c
-)
-
-set(DYLIB_INSTALL_NAME "/usr/lib/darling/libelfloader.dylib")
-add_darling_library(elfloader SHARED ${elfloader_sources})
-target_link_libraries(elfloader system)
-make_fat(elfloader)
-
-install(TARGETS elfloader DESTINATION libexec/darling/usr/lib/darling)
-
diff --git a/src/libelfloader/auxvec.h b/src/libelfloader/auxvec.h
deleted file mode 100644
index c5e1a3229..000000000
--- a/src/libelfloader/auxvec.h
+++ /dev/null
@@ -1,35 +0,0 @@
-#ifndef _UAPI_LINUX_AUXVEC_H
-#define _UAPI_LINUX_AUXVEC_H
-
-/* Symbolic values for the entries in the auxiliary table
- put on the initial stack */
-#define AT_NULL 0 /* end of vector */
-#define AT_IGNORE 1 /* entry should be ignored */
-#define AT_EXECFD 2 /* file descriptor of program */
-#define AT_PHDR 3 /* program headers for program */
-#define AT_PHENT 4 /* size of program header entry */
-#define AT_PHNUM 5 /* number of program headers */
-#define AT_PAGESZ 6 /* system page size */
-#define AT_BASE 7 /* base address of interpreter */
-#define AT_FLAGS 8 /* flags */
-#define AT_ENTRY 9 /* entry point of program */
-#define AT_NOTELF 10 /* program is not ELF */
-#define AT_UID 11 /* real uid */
-#define AT_EUID 12 /* effective uid */
-#define AT_GID 13 /* real gid */
-#define AT_EGID 14 /* effective gid */
-#define AT_PLATFORM 15 /* string identifying CPU for optimizations */
-#define AT_HWCAP 16 /* arch dependent hints at CPU capabilities */
-#define AT_CLKTCK 17 /* frequency at which times() increments */
-/* AT_* values 18 through 22 are reserved */
-#define AT_SECURE 23 /* secure mode boolean */
-#define AT_BASE_PLATFORM 24 /* string identifying real platform, may
- * differ from AT_PLATFORM. */
-#define AT_RANDOM 25 /* address of 16 random bytes */
-#define AT_HWCAP2 26 /* extension of AT_HWCAP */
-
-#define AT_EXECFN 31 /* filename of program */
-
-
-#endif /* _UAPI_LINUX_AUXVEC_H */
-
diff --git a/src/libelfloader/loader.c b/src/libelfloader/loader.c
deleted file mode 100644
index 945d8b939..000000000
--- a/src/libelfloader/loader.c
+++ /dev/null
@@ -1,440 +0,0 @@
-/*
- * Darling
- * Copyright (C) 2017 Lubos Dolezel
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include "auxvec.h"
-#include "loader.h"
-#include "native/elfcalls.h"
-
-#ifdef __APPLE__
-extern int getentropy(void* buf, size_t len);
-#endif
-
-#define ElfW(type) _ElfW (Elf, __ELF_NATIVE_CLASS, type)
-#define _ElfW(e,w,t) _ElfW_1 (e, w, _##t)
-#define _ElfW_1(e,w,t) e##w##t
-
-#if defined(__x86_64__) || defined(__arm64__)
-# define DUMMY_PATH "/usr/libexec/elfloader_dummy64"
-# define MY_ELF_CLASS ELFCLASS64
-# define __ELF_NATIVE_CLASS 64
-#else
-# define DUMMY_PATH "/usr/libexec/elfloader_dummy32"
-# define MY_ELF_CLASS ELFCLASS32
-# define __ELF_NATIVE_CLASS 32
-#endif
-
-#define ELF_MIN_ALIGN 0x1000
-#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_MIN_ALIGN-1))
-#define ELF_PAGEOFFSET(_v) ((_v) & (ELF_MIN_ALIGN-1))
-#define ELF_PAGEALIGN(_v) (((_v) + ELF_MIN_ALIGN - 1) & ~(ELF_MIN_ALIGN - 1))
-
-struct loader_context
-{
- uintptr_t interp_entry;
- uintptr_t exec_entry;
- uintptr_t exec_phstart;
- uintptr_t exec_phentsize;
- uintptr_t exec_phnum;
- uintptr_t interp_base;
-};
-
-static void run(const char* path, const char** envp);
-static void load(const char* path, struct loader_context* lc, bool isInterp);
-static void loader_return(void);
-static const char SYSTEM_ROOT[] = "/Volumes/SystemRoot";
-
-static jmp_buf jmpbuf;
-struct elf_calls* _elfcalls;
-
-#ifndef TEST
-__attribute__((constructor))
- static void runDummy(int argc, const char** argv, const char** envp)
-{
- _elfcalls = (struct elf_calls*) malloc(sizeof(struct elf_calls));
- run(DUMMY_PATH, envp);
-}
-#endif
-
-static unsigned long processEnvVariable(const char* var)
-{
- if (strncmp(var, "HOME=", 5) == 0)
- {
- char* home = (char*) malloc(512);
- char* symlink_path = (char*) malloc(strlen(var+5) + 10);
-
- strcpy(symlink_path, var+5);
- strcat(symlink_path, "/LinuxHome");
-
- int len = readlink(symlink_path, home, 512-1);
- free(symlink_path);
-
- if (len < 0)
- {
- free(home);
- goto out;
- }
- home[len] = '\0';
-
- if (strncmp(home, SYSTEM_ROOT, sizeof(SYSTEM_ROOT) - 1) != 0)
- {
- free(home);
- goto out;
- }
-
- strcpy(home, "HOME=");
- memmove(home+5, home + sizeof(SYSTEM_ROOT) - 1, len - sizeof(SYSTEM_ROOT) + 2);
-
- return (unsigned long) home;
- }
-out:
- return (unsigned long) var;
-}
-
-void run(const char* path, const char** envp)
-{
- struct loader_context lc;
- unsigned long *stack, *stackmem;
- int ptrcount, pos = 0;
- uint8_t entropy[16];
- char pointer1[20];
- char pointer2[20];
-
- load(path, &lc, false);
- if (!lc.interp_entry)
- return;
-
- stackmem = (unsigned long*) mmap(NULL, 4096*4, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANON, -1, 0);
-
- if (stackmem == MAP_FAILED)
- perror("mmap");
-
- stack = (unsigned long*) (((char*) stackmem) + 4096*4 - sizeof(unsigned long));
-
- // AT_*
- ptrcount = 10*2;
- ptrcount++; // argc
- ptrcount += 4; // argv
-
- // environ
- for (int i = 0; envp[i] != NULL; i++)
- ptrcount++;
- ptrcount++;
-
- // Ensure 16-byte alignment
- if (ptrcount % 2 == 0)
- ptrcount++;
-
- stack -= ptrcount;
-
- stack[pos++] = 3; // argc
- stack[pos++] = (unsigned long) "elfloader_dummy";
-
- snprintf(pointer1, sizeof(pointer1), "%lx", (unsigned long) _elfcalls);
- stack[pos++] = (unsigned long) pointer1;
-
- snprintf(pointer2, sizeof(pointer2), "%lx", (unsigned long) loader_return);
- stack[pos++] = (unsigned long) pointer2;
- stack[pos++] = 0;
-
-
- for (int i = 0; envp[i] != NULL; i++)
- stack[pos++] = processEnvVariable(envp[i]);
-
-#ifdef __linux__ // For testing
- getrandom(entropy, sizeof(entropy), 0);
-#else // Darwin
- getentropy(entropy, sizeof(entropy));
-#endif
-
- stack[pos++] = 0;
-
- stack[pos++] = AT_PHDR;
- stack[pos++] = lc.exec_phstart;
-
- stack[pos++] = AT_PHENT;
- stack[pos++] = lc.exec_phentsize;
-
- stack[pos++] = AT_PHNUM;
- stack[pos++] = lc.exec_phnum;
-
- stack[pos++] = AT_ENTRY;
- stack[pos++] = lc.exec_entry;
-
- stack[pos++] = AT_BASE;
- stack[pos++] = lc.interp_base;
-
- stack[pos++] = AT_PAGESZ;
- stack[pos++] = 4096;
-
- stack[pos++] = AT_FLAGS;
- stack[pos++] = 0;
-
- stack[pos++] = AT_RANDOM;
- stack[pos++] = (unsigned long) entropy;
-
- stack[pos++] = AT_NULL;
- stack[pos++] = 0;
-
- // TODO: AT_EXECFN?
-#ifdef __x86_64__
-# define JUMPX(stack, addr) __asm__ volatile("mov %1, %%rsp; jmpq *%0" :: "m"(addr), "r"(stack) :)
-#elif defined(__i386__)
-# define JUMPX(stack, addr) __asm__ volatile("mov %1, %%esp; jmp *%0" :: "m"(addr), "r"(stack) :)
-#else
-# error Unsupported platform!
-#endif
-
- if (!setjmp(jmpbuf))
- JUMPX(stack, lc.interp_entry);
- else
- {
- // puts("Back from loaded binary");
- }
-}
-
-// AT_PHDR -> loadaddr + phoff
-// AT_PHENT -> e_phentsize
-// AT_PHNUM -> e_phnum
-// AT_ENTRY -> exec->e_entry
-// AT_BASE -> interp_loadaddr
-// AT_EXECFN -> apple[0]
-// AT_PAGESZ -> PAGE_SIZE
-// AT_FLAGS -> 0
-// AT_NULL
-
-void load(const char* path, struct loader_context* lc, bool isInterp)
-{
- ElfW(Ehdr) elfHdr;
- void* phdrs = NULL;
-
- // the interpreter path should be relative to the Linux root, not the Darling prefix
- int fd = isInterp ? _open_for_libelfloader(path, O_RDONLY, 0) : open(path, O_RDONLY);
- uintptr_t slide, base;
-
- if (fd == -1)
- {
- perror("open");
- return;
- }
-
- if (read(fd, &elfHdr, sizeof(elfHdr)) != sizeof(elfHdr))
- {
- perror("read");
- goto out;
- }
-
- if (memcmp(elfHdr.e_ident, ELFMAG, SELFMAG) != 0 || elfHdr.e_ident[EI_CLASS] != MY_ELF_CLASS)
- {
- fprintf(stderr, "Wrong ELF signature\n");
- goto out;
- }
-
- if (elfHdr.e_type != ET_DYN)
- {
- fprintf(stderr, "Only position independent ELF are supported\n");
- goto out;
- }
- if (!elfHdr.e_phoff)
- {
- fprintf(stderr, "ELF is not loadable\n");
- goto out;
- }
-
- phdrs = malloc(elfHdr.e_phentsize * elfHdr.e_phnum);
- if (pread(fd, phdrs, elfHdr.e_phentsize * elfHdr.e_phnum, elfHdr.e_phoff) != elfHdr.e_phentsize * elfHdr.e_phnum)
- {
- fprintf(stderr, "Failed to read Elf phdrs\n");
- goto out;
- }
-
- // First, get total virtual range needed
- uintptr_t minAddr = UINTPTR_MAX, maxAddr = 0;
-
- for (int i = 0; i < elfHdr.e_phnum; i++)
- {
- ElfW(Phdr)* phdr = (ElfW(Phdr)*) (((char*) phdrs) + (i * elfHdr.e_phentsize));
-
- if (phdr->p_type == PT_LOAD)
- {
- if (phdr->p_vaddr < minAddr)
- minAddr = ELF_PAGESTART(phdr->p_vaddr);
- if (phdr->p_vaddr + phdr->p_memsz > maxAddr)
- maxAddr = phdr->p_vaddr + phdr->p_memsz;
- }
- else if (phdr->p_type == PT_INTERP && isInterp)
- {
- fprintf(stderr, "Interp with PT_INTERP?\n");
- goto out;
- }
- }
- if (maxAddr == 0)
- {
- fprintf(stderr, "No PT_LOAD headers?\n");
- goto out;
- }
-
- base = (uintptr_t) mmap(NULL, maxAddr-minAddr, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
- if (base == (uintptr_t) MAP_FAILED)
- {
- perror("mmap");
- fprintf(stderr, "Cannot reserve 0x%lx bytes in memory\n", maxAddr-minAddr);
- goto out;
- }
- slide = base - minAddr;
-
- for (int i = 0; i < elfHdr.e_phnum; i++)
- {
- ElfW(Phdr)* phdr = (ElfW(Phdr)*) (((char*) phdrs) + (i * elfHdr.e_phentsize));
-
- if (phdr->p_type == PT_LOAD)
- {
- int prot = 0;
- int flags = MAP_FIXED;
- void* result;
-
- uintptr_t addr = phdr->p_vaddr + slide;
- uintptr_t size = phdr->p_filesz + ELF_PAGEOFFSET(phdr->p_vaddr);
- uintptr_t memsize = phdr->p_memsz + ELF_PAGEOFFSET(phdr->p_vaddr);
- uintptr_t off = phdr->p_offset - ELF_PAGEOFFSET(phdr->p_vaddr);
-
- addr = ELF_PAGESTART(addr);
- // size = ELF_PAGEALIGN(size);
-
- if (phdr->p_flags & PF_X)
- prot |= PROT_EXEC;
- if (phdr->p_flags & PF_W)
- prot |= PROT_WRITE;
- if (phdr->p_flags & PF_R)
- prot |= PROT_READ;
-
- //if (phdr->p_flags & PF_W)
- flags |= MAP_PRIVATE;
- //else
- // flags |= MAP_SHARED;
-
- bool needszeroing = size != ELF_PAGEALIGN(size);
-
- if (needszeroing)
- prot |= PROT_WRITE;
- if (mprotect((void*) (addr), memsize, prot) == -1)
- {
- perror("mprotect");
- }
-
- result = mmap((void*) addr, size, prot, flags, fd, off);
- if (result == MAP_FAILED)
- {
- perror("mmap");
- goto out;
- }
-
- // Based on experiments, when we provide a size that is less than a multiple of page size
- // mmap() will map up to the whole page of file data anyway. Many ELF files, including ld.so,
- // however rely on the rest of the page being zeroed out.
- if (needszeroing)
- {
- memset((void*)(addr + size), 0, ELF_PAGEALIGN(size) - size);
- if (!(phdr->p_flags & PF_W))
- mprotect((void*) (addr), memsize, prot & ~PROT_WRITE);
- }
-
- /*
- if (phdr->p_filesz < phdr->p_memsz)
- {
- if (mprotect((void*) (addr + phdr->p_filesz), phdr->p_memsz - phdr->p_filesz, prot) == -1)
- {
- perror("mprotect");
- goto out;
- }
- }
- */
- }
- else if (phdr->p_type == PT_INTERP)
- {
- char* interp = malloc(phdr->p_filesz + 1);
-
- if (pread(fd, interp, phdr->p_filesz, phdr->p_offset) != phdr->p_filesz)
- {
- free(interp);
- perror("reading PT_INTERP");
- goto out;
- }
- interp[phdr->p_filesz] = '\0';
-
- // Load interpreter
- if (_access_for_libelfloader(interp, F_OK) != 0)
- {
- fprintf(stderr, "Cannot load interpreter at %s\n", interp);
- free(interp);
- goto out;
- }
-
- load(interp, lc, true);
-
- free(interp);
- }
- }
-
- if (isInterp)
- {
- lc->interp_base = base;
- lc->interp_entry = slide + elfHdr.e_entry;
- }
- else
- {
- lc->exec_phstart = slide + elfHdr.e_phoff;
- lc->exec_phentsize = elfHdr.e_phentsize;
- lc->exec_phnum = elfHdr.e_phnum;
- lc->exec_entry = slide + elfHdr.e_entry;
- }
-
-out:
- free(phdrs);
- close(fd);
-}
-
-void loader_return(void)
-{
- longjmp(jmpbuf, 1);
-}
-
-#ifdef TEST
-
-int main(int argc, const char** argv)
-{
- if (argc > 1)
- run(argv[1]);
- return 0;
-}
-
-#endif
-
diff --git a/src/libelfloader/loader.h b/src/libelfloader/loader.h
deleted file mode 100644
index f56743534..000000000
--- a/src/libelfloader/loader.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Darling
- * Copyright (C) 2017 Lubos Dolezel
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#ifndef _LOADER_H
-#define _LOADER_H
-#include "native/elfcalls.h"
-
-extern struct elf_calls* _elfcalls;
-
-#endif
-
-
diff --git a/src/libelfloader/native/CMakeLists.txt b/src/libelfloader/native/CMakeLists.txt
deleted file mode 100644
index 8e6915226..000000000
--- a/src/libelfloader/native/CMakeLists.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-project(libelfstub)
-
-set(elfstub_sources
- elfcalls.c
- threads.c
-)
-
-if (BUILD_TARGET_64BIT)
- add_executable(elfloader_dummy64 ${elfstub_sources})
- target_link_libraries(elfloader_dummy64 -lpthread -lrt -ldl)
- set_target_properties(elfloader_dummy64 PROPERTIES COMPILE_FLAGS "-fPIC"
- LINK_FLAGS "-pie -fPIC")
-endif ()
-
-if (BUILD_TARGET_32BIT)
- add_executable(elfloader_dummy32 ${elfstub_sources})
- target_link_libraries(elfloader_dummy32 -lpthread -lrt -ldl)
- set_target_properties(elfloader_dummy32 PROPERTIES COMPILE_FLAGS "-fPIC -m32"
- LINK_FLAGS "-pie -fPIC -m32")
-endif ()
-
-if (BUILD_TARGET_64BIT AND BUILD_TARGET_32BIT)
- install(TARGETS elfloader_dummy32 elfloader_dummy64 DESTINATION libexec/darling/usr/libexec)
-elseif (BUILD_TARGET_64BIT)
- install(TARGETS elfloader_dummy64 DESTINATION libexec/darling/usr/libexec)
-elseif (BUILD_TARGET_32BIT)
- install(TARGETS elfloader_dummy32 DESTINATION libexec/darling/usr/libexec)
-endif ()
\ No newline at end of file
diff --git a/src/libelfloader/wrapgen/CMakeLists.txt b/src/libelfloader/wrapgen/CMakeLists.txt
index 944656d72..58e3cae55 100644
--- a/src/libelfloader/wrapgen/CMakeLists.txt
+++ b/src/libelfloader/wrapgen/CMakeLists.txt
@@ -10,3 +10,8 @@ endif(COMMAND cmake_policy)
add_executable(wrapgen wrapgen.cpp)
target_link_libraries(wrapgen dl)
+# This tool is useful for packaging to detect ELF dependencies inside Mach-O libraries,
+# which standard distro tools cannot do.
+if (DEFINED WITH_PRINT_WRAPPED_ELF)
+ add_executable(print_wrapped_elf print_wrapped_elf.cpp)
+endif (DEFINED WITH_PRINT_WRAPPED_ELF)
diff --git a/src/startup/CMakeLists.txt b/src/startup/CMakeLists.txt
index 544182d8b..95019ec67 100644
--- a/src/startup/CMakeLists.txt
+++ b/src/startup/CMakeLists.txt
@@ -18,7 +18,7 @@ add_executable(darling darling.c)
target_link_libraries(darling -lutil)
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/src/darlingserver/include)
install(TARGETS darling DESTINATION bin
PERMISSIONS
@@ -27,14 +27,8 @@ install(TARGETS darling DESTINATION bin
WORLD_READ WORLD_EXECUTE
SETUID)
-
-# This tool is useful for packaging to detect ELF dependencies inside Mach-O libraries,
-# which standard distro tools cannot do.
-if (DEFINED WITH_PRINT_WRAPPED_ELF)
- add_executable(print_wrapped_elf wrapgen/print_wrapped_elf.cpp)
-endif (DEFINED WITH_PRINT_WRAPPED_ELF)
-
add_executable(rtsig rtsig.c)
add_custom_command(OUTPUT rtsig.h DEPENDS rtsig COMMAND ${CMAKE_CURRENT_BINARY_DIR}/rtsig rtsig.h COMMENT "Determining available RT signals")
add_custom_target(rtsig_h DEPENDS rtsig.h)
+add_subdirectory(mldr)
diff --git a/src/startup/darling.c b/src/startup/darling.c
index 010a34786..9470e9636 100644
--- a/src/startup/darling.c
+++ b/src/startup/darling.c
@@ -88,9 +88,6 @@ int main(int argc, char ** argv, char ** envp)
setuid(0);
setgid(0);
- if (!isModuleLoaded())
- loadKernelModule();
-
prefix = getenv("DPREFIX");
if (!prefix)
prefix = defaultPrefixPath();
@@ -688,39 +685,6 @@ void missingSetuidRoot(void)
fprintf(stderr, "Darling needs this in order to create mount and PID namespaces and to perform mounts.\n");
}
-void fixDirectoryPermissions(const char* path)
-{
- DIR* dir;
- struct dirent* ent;
-
- dir = opendir(path);
- if (!dir)
- return;
-
- while ((ent = readdir(dir)) != NULL)
- {
- if (ent->d_type == DT_DIR)
- {
- char* subdir;
-
- if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
- continue;
-
- subdir = (char*) malloc(strlen(path) + 2 + strlen(ent->d_name));
- sprintf(subdir, "%s/%s", path, ent->d_name);
-
- fixDirectoryPermissions(subdir);
-
- if (chown(subdir, g_originalUid, g_originalGid) == -1)
- fprintf(stderr, "Cannot chown %s: %s\n", subdir, strerror(errno));
-
- free(subdir);
- }
- }
-
- closedir(dir);
-}
-
static uint32_t linux_release(void)
{
struct utsname uts;
@@ -763,9 +727,9 @@ pid_t spawnInitProcess(void)
exit(1);
}
- if (unshare(CLONE_NEWPID | CLONE_NEWUTS | CLONE_NEWIPC) != 0)
+ if (unshare(CLONE_NEWUTS | CLONE_NEWIPC) != 0)
{
- fprintf(stderr, "Cannot unshare PID, UTS and IPC namespaces to create darling-init: %s\n", strerror(errno));
+ fprintf(stderr, "Cannot unshare UTS and IPC namespaces to create darling-init: %s\n", strerror(errno));
exit(1);
}
@@ -781,96 +745,20 @@ pid_t spawnInitProcess(void)
{
// The child
- char *opts;
- char putOld[4096];
- char *p;
-
- close(pipefd[0]);
-
- // Since overlay cannot be mounted inside user namespaces, we have to setup a new mount namespace
- // and do the mount while we can be root
- if (unshare(CLONE_NEWNS) != 0)
- {
- fprintf(stderr, "Cannot unshare mount namespace: %s\n", strerror(errno));
- exit(1);
- }
-
- // Because systemd marks / as MS_SHARED and we would inherit this into the overlay mount,
- // causing it not to be unmounted once the init process dies.
- if (mount(NULL, "/", NULL, MS_REC | MS_SLAVE, NULL) != 0)
- {
- fprintf(stderr, "Cannot remount / as slave: %s\n", strerror(errno));
- exit(1);
- }
-
- umount("/dev/shm");
- if (mount("tmpfs", "/dev/shm", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV, NULL) != 0)
- {
- fprintf(stderr, "Cannot mount new /dev/shm: %s\n", strerror(errno));
- exit(1);
- }
-
- opts = (char*) malloc(strlen(prefix)*2 + sizeof(LIBEXEC_PATH) + 100);
-
- const char* opts_fmt = "lowerdir=%s,upperdir=%s,workdir=%s.workdir,index=off";
-
- sprintf(opts, opts_fmt, LIBEXEC_PATH, prefix, prefix);
-
- // Mount overlay onto our prefix
- if (mount("overlay", prefix, "overlay", 0, opts) != 0)
- {
- fprintf(stderr, "Cannot mount overlay: %s\n", strerror(errno));
- exit(1);
- }
-
- free(opts);
-
- // This is executed once at prefix creation
- if (g_fixPermissions)
- fixDirectoryPermissions(prefix);
-
- snprintf(putOld, sizeof(putOld), "%s/proc", prefix);
-
- // mount procfs for our new PID namespace
- if (mount("proc", putOld, "proc", 0, "") != 0)
- {
- fprintf(stderr, "Cannot mount procfs: %s\n", strerror(errno));
- exit(1);
- }
+ char uid_str[21];
+ char gid_str[21];
+ char pipefd_str[21];
- // Drop the privileges. It's important to drop GID first, because
- // non-root users can't change their GID.
- setresgid(g_originalGid, g_originalGid, g_originalGid);
- setresuid(g_originalUid, g_originalUid, g_originalUid);
- prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
+ snprintf(uid_str, sizeof(uid_str), "%d", g_originalUid);
+ snprintf(gid_str, sizeof(gid_str), "%d", g_originalGid);
+ snprintf(pipefd_str, sizeof(pipefd_str), "%d", pipefd[1]);
- setupUserHome();
- setupCoredumpPattern();
-
- // Set name to darling-init
- prctl(PR_SET_NAME, DARLING_INIT_COMM, 0, 0);
- p = stpcpy(g_argv[0], DARLING_INIT_COMM);
- memset(p, 0, g_envp[0] - p);
-
- /*
- if (unshare(CLONE_NEWUSER) != 0)
- {
- fprintf(stderr, "Cannot unshare user namespace: %s\n", strerror(errno));
- exit(1);
- }
- */
-
- // Tell the parent we're ready
- write(pipefd[1], buffer, 1);
- close(pipefd[1]);
-
- // Here's where we wait for the parent to set up UID/GID mapping
- // if we enable user namespaces
+ close(pipefd[0]);
- darlingPreInit();
- spawnLaunchd();
+ execl(INSTALL_PREFIX "/bin/darlingserver", "darlingserver", prefix, uid_str, gid_str, pipefd_str, g_fixPermissions ? "1" : "0", NULL);
- // Never returns
+ fprintf(stderr, "Failed to start darlingserver\n");
+ exit(1);
}
// Wait for the child to drop UID/GIDs and unshare stuff
@@ -939,67 +827,6 @@ void putInitPid(pid_t pidInit)
fclose(fp);
}
-void spawnLaunchd(void)
-{
- puts("Bootstrapping the container with launchd...");
-
- // putenv("KQUEUE_DEBUG=1");
-
- setenv("DYLD_ROOT_PATH", LIBEXEC_PATH, 1);
- execl(LIBEXEC_PATH "/usr/libexec/darling/vchroot", "vchroot", prefix, "/sbin/launchd", NULL);
-
- fprintf(stderr, "Failed to exec launchd: %s\n", strerror(errno));
- abort();
-}
-
-static void wipeDir(const char* dirpath)
-{
- char path[4096];
- struct dirent* ent;
- DIR* dir = opendir(dirpath);
-
- if (!dir)
- return;
-
- while ((ent = readdir(dir)) != NULL)
- {
- if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
- continue;
-
- snprintf(path, sizeof(path), "%s/%s", dirpath, ent->d_name);
-
- if (ent->d_type == DT_DIR)
- {
- wipeDir(path);
- rmdir(path);
- }
- else
- unlink(path);
- }
-
- closedir(dir);
-}
-
-void darlingPreInit(void)
-{
- // TODO: Run /usr/libexec/makewhatis
- const char* dirs[] = {
- "/var/tmp",
- "/var/run"
- };
-
- char fullpath[4096];
- strcpy(fullpath, prefix);
- const size_t prefixLen = strlen(fullpath);
-
- for (size_t i = 0; i < sizeof(dirs)/sizeof(dirs[0]); i++)
- {
- fullpath[prefixLen] = 0;
- strcat(fullpath, dirs[i]);
- wipeDir(fullpath);
- }
-}
-
char* defaultPrefixPath(void)
{
const char defaultPath[] = "/.darling";
@@ -1320,168 +1147,3 @@ void checkPrefixOwner()
exit(1);
}
}
-
-int isModuleLoaded()
-{
- size_t len = 0;
- ssize_t read = 0;
- char * line = NULL;
- FILE *fp = NULL;
-
- if ((fp = fopen("/proc/modules", "r")) == NULL)
- {
- fprintf(stderr, "Failure opening /proc/modules: %s\n", strerror(errno));
- fclose(fp);
- return 0;
- }
-
- while (!feof(fp))
- {
- read = getline(&line, &len, fp);
- if (read > 0 && strstr(line, "darling_mach") != NULL)
- {
- fclose(fp);
- return 1;
- }
- }
-
- fclose(fp);
- return 0;
-}
-
-void loadKernelModule()
-{
- int status;
- FILE *fp = popen("/sbin/modprobe darling-mach", "w");
-
- if (fp == NULL)
- {
- fprintf(stderr, "Failed to run modprobe: %s\n", strerror(errno));
- exit(1);
- }
-
- status = pclose(fp);
- if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
- {
- fprintf(stderr, "Loaded the kernel module\n");
- return;
- }
- else
- {
- fprintf(stderr, "Failed to load the kernel module\n");
- exit(1);
- }
-}
-
-void setupCoredumpPattern(void)
-{
- FILE* f = fopen("/proc/sys/kernel/core_pattern", "w");
- if (f != NULL)
- {
- // This is how macOS saves core dumps
- fputs("/cores/core.%p\n", f);
- fclose(f);
- }
-}
-
-const char* xdgDirectory(const char* name)
-{
- static char dir[4096];
- char* cmd = (char*) malloc(16 + strlen(name));
-
- sprintf(cmd, "xdg-user-dir %s", name);
-
- FILE* proc = popen(cmd, "r");
-
- free(cmd);
-
- if (!proc)
- return NULL;
-
- fgets(dir, sizeof(dir)-1, proc);
-
- pclose(proc);
-
- size_t len = strlen(dir);
- if (len <= 1)
- return NULL;
-
- if (dir[len-1] == '\n')
- dir[len-1] = '\0';
- return dir;
-}
-
-void setupUserHome(void)
-{
- char buf[4096], buf2[4096];
-
- snprintf(buf, sizeof(buf), "%s/Users", prefix);
-
- // Remove the old /Users symlink that may exist
- unlink(buf);
-
- // mkdir /Users
- mkdir(buf, 0777);
-
- // mkdir /Users/Shared
- strcat(buf, "/Shared");
- mkdir(buf, 0777);
-
- const char* home = getenv("HOME");
-
- const char* login = NULL;
- struct passwd* pw = getpwuid(getuid());
-
- if (pw != NULL)
- login = pw->pw_name;
-
- if (!login)
- login = getlogin();
-
- if (!login)
- {
- fprintf(stderr, "Cannot determine your user name\n");
- exit(1);
- }
- if (!home)
- {
- fprintf(stderr, "Cannot determine your home directory\n");
- exit(1);
- }
-
- snprintf(buf, sizeof(buf), "%s/Users/%s", prefix, login);
-
- // mkdir /Users/$LOGIN
- mkdir(buf, 0755);
-
- snprintf(buf2, sizeof(buf2), "/Volumes/SystemRoot%s", home);
-
- strcat(buf, "/LinuxHome");
- unlink(buf);
-
- // symlink /Users/$LOGIN/LinuxHome -> $HOME
- symlink(buf2, buf);
-
- static const char* xdgmap[][2] = {
- { "DESKTOP", "Desktop" },
- { "DOWNLOAD", "Downloads" },
- { "PUBLICSHARE", "Public" },
- { "DOCUMENTS", "Documents" },
- { "MUSIC", "Music" },
- { "PICTURES", "Pictures" },
- { "VIDEOS", "Movies" },
- };
-
- for (int i = 0; i < sizeof(xdgmap) / sizeof(xdgmap[0]); i++)
- {
- const char* dir = xdgDirectory(xdgmap[i][0]);
- if (!dir)
- continue;
-
- snprintf(buf2, sizeof(buf2), "/Volumes/SystemRoot%s", dir);
- snprintf(buf, sizeof(buf), "%s/Users/%s/%s", prefix, login, xdgmap[i][1]);
-
- unlink(buf);
- symlink(buf2, buf);
- }
-}
diff --git a/src/startup/dirstructure.cpp b/src/startup/dirstructure.cpp
deleted file mode 100644
index 1948f5ea5..000000000
--- a/src/startup/dirstructure.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
-This file is part of Darling.
-
-Copyright (C) 2015 Lubos Dolezel
-
-Darling is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Darling is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Darling. If not, see .
-*/
-
-#include "dirstructure.h"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-static std::string GetUserLibrary()
-{
- const char *home, *prefix;
- std::stringstream ss;
- std::string path;
-
- home = getenv("HOME");
- if (!home)
- return std::string(); // give up on this user
-
- ss << home << '/' << "Library" << '/';
- return ss.str();
-}
-
-bool HasUserDirectoryStructure()
-{
- std::string path = GetUserLibrary();
-
- if (path.empty())
- return true; // give up on this user
-
- if (::access(path.c_str(), F_OK) == -1)
- return false;
- else
- return true;
-}
-
-void SetupUserDirectoryStructure()
-{
- const char* dirs[] = {
- "Application Support",
- "Assistants",
- "Audio",
- "Audio/MIDI Drivers",
- "Audio/Plug-Ins",
- "Audio/Plug-Ins/Components",
- "Audio/Plug-Ins/Digidesign",
- "Audio/Plug-Ins/VST",
- "Audio/Sounds",
- "Audio/Sounds/Alerts",
- "Audio/Sounds/Banks",
- "Caches",
- "ColorPickers",
- "Colors",
- "Compositions",
- "Favorites",
- "FontCollections",
- "Fonts",
- "iMovie",
- "iMovie/Plug-ins",
- "iMovie/Sound Effects",
- "Input Methods",
- "Internet Plug-Ins",
- "Keyboard Layouts",
- "Logs",
- "PreferencePanes",
- "Preferences",
- "Printers",
- "Screen Savers",
- "Sounds",
- "Spelling",
- "Voices",
- };
-
- std::string path = GetUserLibrary();
-
- if (path.empty())
- return;
-
- std::cerr << "Darling: Creating Library structure at " << path << std::endl;
-
- if (::mkdir(path.c_str(), 0777) == -1)
- std::cerr << "Darling: Cannot mkdir(" << path << "): " << strerror(errno) << std::endl;
-
- for (const char* dir : dirs)
- {
- std::string s = path;
-
- s += dir;
-
- if (::mkdir(s.c_str(), 0777) == -1)
- std::cerr << "Darling: Cannot mkdir(" << s << "): " << strerror(errno) << std::endl;
- }
-}
diff --git a/src/startup/dirstructure.h b/src/startup/dirstructure.h
deleted file mode 100644
index d40963b55..000000000
--- a/src/startup/dirstructure.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
-This file is part of Darling.
-
-Copyright (C) 2015 Lubos Dolezel
-
-Darling is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Darling is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Darling. If not, see .
-*/
-
-#ifndef DIRSTRUCTURE_H
-#define DIRSTRUCTURE_H
-
-// Checks whether $HOME/Library exists
-bool HasUserDirectoryStructure();
-
-// Creates the default OS X $HOME/Library directory structure
-void SetupUserDirectoryStructure();
-
-#endif
diff --git a/src/startup/mldr/CMakeLists.txt b/src/startup/mldr/CMakeLists.txt
new file mode 100644
index 000000000..acc0efe33
--- /dev/null
+++ b/src/startup/mldr/CMakeLists.txt
@@ -0,0 +1,47 @@
+project(mldr)
+
+cmake_minimum_required(VERSION 3.10)
+
+include_directories(include)
+
+add_library(mldr_dserver_rpc ${CMAKE_BINARY_DIR}/src/darlingserver/src/rpc.c)
+add_library(mldr32_dserver_rpc ${CMAKE_BINARY_DIR}/src/darlingserver/src/rpc.c)
+
+add_dependencies(mldr_dserver_rpc generate_dserver_rpc_wrappers)
+add_dependencies(mldr32_dserver_rpc generate_dserver_rpc_wrappers)
+
+target_compile_options(mldr_dserver_rpc PRIVATE -include ${CMAKE_CURRENT_SOURCE_DIR}/resources/dserver-rpc-defs.h)
+target_compile_options(mldr32_dserver_rpc PRIVATE -include ${CMAKE_CURRENT_SOURCE_DIR}/resources/dserver-rpc-defs.h)
+
+target_compile_options(mldr32_dserver_rpc PRIVATE -m32)
+target_link_options(mldr32_dserver_rpc PRIVATE -m32)
+
+set(mldr_sources
+ mldr.c
+ commpage.c
+ elfcalls/elfcalls.c
+ elfcalls/threads.c
+)
+
+add_executable(mldr ${mldr_sources})
+
+add_executable(mldr32 ${mldr_sources})
+target_compile_options(mldr32 PRIVATE -m32)
+target_link_options(mldr32 PRIVATE -m32)
+
+target_compile_options(mldr PRIVATE -pthread)
+target_compile_options(mldr32 PRIVATE -pthread)
+target_link_options(mldr PRIVATE -pthread)
+target_link_options(mldr32 PRIVATE -pthread)
+
+target_link_libraries(mldr PRIVATE -lrt -ldl mldr_dserver_rpc)
+target_link_libraries(mldr32 PRIVATE -lrt -ldl mldr32_dserver_rpc)
+
+install(TARGETS mldr mldr32 DESTINATION libexec/darling/usr/libexec/darling)
+
+include(setcap)
+setcap(libexec/darling/usr/libexec/darling/mldr cap_sys_rawio,cap_sys_resource+ep)
+setcap(libexec/darling/usr/libexec/darling/mldr32 cap_sys_rawio,cap_sys_resource+ep)
+
+configure_file(darling.conf.in darling.conf @ONLY)
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/darling.conf" DESTINATION lib/binfmt.d)
diff --git a/src/startup/mldr/commpage.c b/src/startup/mldr/commpage.c
new file mode 100644
index 000000000..5c45ff246
--- /dev/null
+++ b/src/startup/mldr/commpage.c
@@ -0,0 +1,151 @@
+#include "commpage.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// Include commpage definitions
+#define PRIVATE
+#include
+
+static const char* SIGNATURE32 = "commpage 32-bit";
+static const char* SIGNATURE64 = "commpage 64-bit";
+
+static uint64_t get_cpu_caps(void);
+
+#define CGET(p) (commpage + ((p)-_COMM_PAGE_START_ADDRESS))
+
+void commpage_setup(bool _64bit)
+{
+ uint8_t* commpage;
+ uint64_t* cpu_caps64;
+ uint32_t* cpu_caps;
+ uint16_t* version;
+ char* signature;
+ uint64_t my_caps;
+ uint8_t *ncpus, *nactivecpus;
+ uint8_t *physcpus, *logcpus;
+ struct sysinfo si;
+
+ commpage = (uint8_t*) mmap((void*)(_64bit ? _COMM_PAGE64_BASE_ADDRESS : _COMM_PAGE32_BASE_ADDRESS),
+ _64bit ? _COMM_PAGE64_AREA_LENGTH : _COMM_PAGE32_AREA_LENGTH, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+ if (commpage == MAP_FAILED)
+ {
+ fprintf(stderr, "Cannot mmap commpage: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ signature = (char*)CGET(_COMM_PAGE_SIGNATURE);
+ version = (uint16_t*)CGET(_COMM_PAGE_VERSION);
+ cpu_caps64 = (uint64_t*)CGET(_COMM_PAGE_CPU_CAPABILITIES64);
+ cpu_caps = (uint32_t*)CGET(_COMM_PAGE_CPU_CAPABILITIES);
+
+ strcpy(signature, _64bit ? SIGNATURE64 : SIGNATURE32);
+ *version = _COMM_PAGE_THIS_VERSION;
+
+ ncpus = (uint8_t*)CGET(_COMM_PAGE_NCPUS);
+ *ncpus = sysconf(_SC_NPROCESSORS_CONF);
+
+ nactivecpus = (uint8_t*)CGET(_COMM_PAGE_ACTIVE_CPUS);
+ *nactivecpus = sysconf(_SC_NPROCESSORS_ONLN);
+
+ // Better imprecise information than no information
+ physcpus = (uint8_t*)CGET(_COMM_PAGE_PHYSICAL_CPUS);
+ logcpus = (uint8_t*)CGET(_COMM_PAGE_LOGICAL_CPUS);
+ *physcpus = *logcpus = *ncpus;
+
+ my_caps = get_cpu_caps();
+ if (*ncpus == 1)
+ my_caps |= kUP;
+
+ *cpu_caps = (uint32_t) my_caps;
+ *cpu_caps64 = my_caps;
+
+ if (sysinfo(&si) == 0)
+ {
+ uint64_t* memsize = (uint64_t*)CGET(_COMM_PAGE_MEMORY_SIZE);
+ *memsize = si.totalram * si.mem_unit;
+ }
+}
+
+uint64_t get_cpu_caps(void)
+{
+ uint64_t caps = 0;
+
+ {
+ union cpu_flags1 eax;
+ union cpu_flags2 ecx;
+ union cpu_flags3 edx;
+ uint32_t ebx;
+
+ eax.reg = ecx.reg = edx.reg = 0;
+ __cpuid(1, eax.reg, ebx, ecx.reg, edx.reg);
+
+ if (ecx.mmx)
+ caps |= kHasMMX;
+ if (ecx.sse)
+ caps |= kHasSSE;
+ if (ecx.sse2)
+ caps |= kHasSSE2;
+ if (edx.sse3)
+ caps |= kHasSSE3;
+ if (ecx.ia64)
+ caps |= k64Bit;
+ if (edx.ssse3)
+ caps |= kHasSupplementalSSE3;
+ if (edx.sse41)
+ caps |= kHasSSE4_1;
+ if (edx.sse42)
+ caps |= kHasSSE4_2;
+ if (edx.aes)
+ caps |= kHasAES;
+ if (edx.avx)
+ caps |= kHasAVX1_0;
+ if (edx.rdrnd)
+ caps |= kHasRDRAND;
+ if (edx.fma)
+ caps |= kHasFMA;
+ if (edx.f16c)
+ caps |= kHasF16C;
+ }
+ {
+ union cpu_flags4 ebx;
+ union cpu_flags5 ecx;
+ uint32_t edx = 0, eax = 7;
+
+ __cpuid(7, eax, ebx.reg, ecx.reg, edx);
+
+ if (ebx.erms)
+ caps |= kHasENFSTRG;
+ if (ebx.avx2)
+ caps |= kHasAVX2_0;
+ if (ebx.bmi1)
+ caps |= kHasBMI1;
+ if (ebx.bmi2)
+ caps |= kHasBMI2;
+ if (ebx.rtm)
+ caps |= kHasRTM;
+ if (ebx.hle)
+ caps |= kHasHLE;
+ if (ebx.rdseed)
+ caps |= kHasRDSEED;
+ if (ebx.adx)
+ caps |= kHasADX;
+ if (ebx.mpx)
+ caps |= kHasMPX;
+ if (ebx.sgx)
+ caps |= kHasSGX;
+ }
+
+ return caps;
+}
+
+unsigned long commpage_address(bool _64bit)
+{
+ return _64bit ? _COMM_PAGE64_BASE_ADDRESS : _COMM_PAGE32_BASE_ADDRESS;
+}
+
diff --git a/src/startup/mldr/commpage.h b/src/startup/mldr/commpage.h
new file mode 100644
index 000000000..4d9558c72
--- /dev/null
+++ b/src/startup/mldr/commpage.h
@@ -0,0 +1,155 @@
+#ifndef _COMMPAGE_H
+#define _COMMPAGE_H
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void commpage_setup(bool _64bit);
+unsigned long commpage_address(bool _64bit);
+
+union cpu_flags1 {
+ struct {
+ uint8_t step: 4;
+ uint8_t model: 4;
+ uint8_t family: 4;
+ uint8_t type: 2;
+ uint8_t pad1: 2;
+ uint8_t emodel: 4;
+ uint8_t efamily: 8;
+ uint8_t pad2: 4;
+ };
+ uint32_t reg;
+};
+
+union cpu_flags2 {
+ struct {
+ uint8_t fpu: 1;
+ uint8_t vme: 1;
+ uint8_t de: 1;
+ uint8_t pse: 1;
+ uint8_t tsc: 1;
+ uint8_t msr: 1;
+ uint8_t pae: 1;
+ uint8_t mce: 1;
+ uint8_t cx8: 1;
+ uint8_t apic: 1;
+ uint8_t reserved1: 1;
+ uint8_t sep: 1;
+ uint8_t mtrr: 1;
+ uint8_t pge: 1;
+ uint8_t mca: 1;
+ uint8_t cmov: 1;
+ uint8_t pat: 1;
+ uint8_t pse36: 1;
+ uint8_t psn: 1;
+ uint8_t clfsh: 1;
+ uint8_t reserved2: 1;
+ uint8_t ds: 1;
+ uint8_t acpi: 1;
+ uint8_t mmx: 1;
+ uint8_t fxsr: 1;
+ uint8_t sse: 1;
+ uint8_t sse2: 1;
+ uint8_t ss: 1;
+ uint8_t htt: 1;
+ uint8_t tm: 1;
+ uint8_t ia64: 1;
+ uint8_t pbe: 1;
+ };
+ uint32_t reg;
+};
+
+union cpu_flags3 {
+ struct {
+ uint32_t sse3: 1;
+ uint32_t pclmulqdq: 1;
+ uint32_t dtes64: 1;
+ uint32_t monitor: 1;
+ uint32_t dscpl: 1;
+ uint32_t vmx: 1;
+ uint32_t smx: 1;
+ uint32_t est: 1;
+ uint32_t tm2: 1;
+ uint32_t ssse3: 1;
+ uint32_t cntxid: 1;
+ uint32_t sdbg: 1;
+ uint32_t fma: 1;
+ uint32_t cx16: 1;
+ uint32_t xtpr: 1;
+ uint32_t pdcm: 1;
+ uint32_t reserved1: 1;
+ uint32_t pcid: 1;
+ uint32_t dca: 1;
+ uint32_t sse41: 1;
+ uint32_t sse42: 1;
+ uint32_t x2apic: 1;
+ uint32_t movbe: 1;
+ uint32_t popcnt: 1;
+ uint32_t tscdadline: 1;
+ uint32_t aes: 1;
+ uint32_t xsave: 1;
+ uint32_t osxsave: 1;
+ uint32_t avx: 1;
+ uint32_t f16c: 1;
+ uint32_t rdrnd: 1;
+ uint32_t hypervisor: 1;
+ };
+ uint32_t reg;
+};
+
+union cpu_flags4 {
+ struct {
+ uint8_t fsgsbase: 1;
+ uint8_t ia32tscadjust: 1;
+ uint8_t sgx: 1;
+ uint8_t bmi1: 1;
+ uint8_t hle: 1;
+ uint8_t avx2: 1;
+ uint8_t reserved1: 1;
+ uint8_t smep: 1;
+ uint8_t bmi2: 1;
+ uint8_t erms: 1;
+ uint8_t invpcid: 1;
+ uint8_t rtm: 1;
+ uint8_t pqm: 1;
+ uint8_t fpucsdsdeprecated: 1;
+ uint8_t mpx: 1;
+ uint8_t pqe: 1;
+ uint8_t avx512f: 1;
+ uint8_t avx512dq: 1;
+ uint8_t rdseed: 1;
+ uint8_t adx: 1;
+ uint8_t smap: 1;
+ uint8_t avx512ifma: 1;
+ uint8_t pcommit: 1;
+ uint8_t clflushopt: 1;
+ uint8_t clwb: 1;
+ uint8_t intelproctrace: 1;
+ uint8_t avx512pf: 1;
+ uint8_t avx512er: 1;
+ uint8_t avx512cd: 1;
+ uint8_t sha: 1;
+ uint8_t avx512bw: 1;
+ uint8_t avx512vl: 1;
+ };
+ uint32_t reg;
+};
+
+union cpu_flags5 {
+ struct {
+ uint8_t prefetchwt1: 1;
+ uint8_t avx512vbmi: 1;
+ uint32_t reserved: 30;
+ };
+ uint32_t reg;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/src/startup/mldr/darling.conf.in b/src/startup/mldr/darling.conf.in
new file mode 100644
index 000000000..d3f1c3824
--- /dev/null
+++ b/src/startup/mldr/darling.conf.in
@@ -0,0 +1,3 @@
+:Mach-O-64-bit:M::\xcf\xfa\xed\xfe::@CMAKE_INSTALL_PREFIX@/libexec/darling/bin/mldr:P
+:Mach-O-32-bit:M::\xce\xfa\xed\xfe::@CMAKE_INSTALL_PREFIX@/libexec/darling/bin/mldr32:P
+:Fat-Mach-O:M::\xca\xfe\xba\xbe::@CMAKE_INSTALL_PREFIX@/libexec/darling/bin/mldr:P
diff --git a/src/libelfloader/native/dthreads.h b/src/startup/mldr/elfcalls/dthreads.h
similarity index 100%
rename from src/libelfloader/native/dthreads.h
rename to src/startup/mldr/elfcalls/dthreads.h
diff --git a/src/libelfloader/native/elfcalls.c b/src/startup/mldr/elfcalls/elfcalls.c
similarity index 78%
rename from src/libelfloader/native/elfcalls.c
rename to src/startup/mldr/elfcalls/elfcalls.c
index afb5a8b61..b5ad83b8e 100644
--- a/src/libelfloader/native/elfcalls.c
+++ b/src/startup/mldr/elfcalls/elfcalls.c
@@ -8,6 +8,7 @@
#include
#include "elfcalls.h"
#include "threads.h"
+#include
static void* dlopen_simple(const char* name)
{
@@ -51,6 +52,12 @@ static int get_errno(void)
return errno;
}
+extern struct sockaddr_un __dserver_socket_address_data[];
+
+static const void* __dserver_socket_address(void) {
+ return &__dserver_socket_address_data;
+};
+
void elfcalls_make(struct elf_calls* calls)
{
calls->dlopen = dlopen_simple;
@@ -80,31 +87,6 @@ void elfcalls_make(struct elf_calls* calls)
*((void**)&calls->shm_open) = shm_open;
*((void**)&calls->shm_unlink) = shm_unlink;
-}
-
-int main(int argc, const char** argv)
-{
- typedef void (*retfunc)(void);
-
- struct elf_calls* calls;
- retfunc ret;
- // for (int i = 0; i < argc; i++)
- // printf("arg %d: %s\n", i, argv[i]);
-
- calls = (struct elf_calls*) strtoul(argv[1], NULL, 16);
- ret = (retfunc) strtoul(argv[2], NULL, 16);
-
- // Enable locales
- setlocale(LC_ALL, "");
-
- // puts("before elfcalls_make");
-
- elfcalls_make(calls);
- // puts("after elfcalls_make");
- // printf("Will call %p\n", ret);
- ret();
-
- __builtin_unreachable();
+ calls->dserver_socket_address = __dserver_socket_address;
}
-
diff --git a/src/libelfloader/native/elfcalls.h b/src/startup/mldr/elfcalls/elfcalls.h
similarity index 95%
rename from src/libelfloader/native/elfcalls.h
rename to src/startup/mldr/elfcalls/elfcalls.h
index 3b9ebbc00..f34293603 100644
--- a/src/libelfloader/native/elfcalls.h
+++ b/src/startup/mldr/elfcalls/elfcalls.h
@@ -49,6 +49,9 @@ struct elf_calls
// POSIX sysconf
long (*sysconf)(int name);
+
+ // darlingserver RPC info
+ const void* (*dserver_socket_address)(void);
};
#endif
diff --git a/src/libelfloader/native/threads.c b/src/startup/mldr/elfcalls/threads.c
similarity index 100%
rename from src/libelfloader/native/threads.c
rename to src/startup/mldr/elfcalls/threads.c
diff --git a/src/libelfloader/native/threads.h b/src/startup/mldr/elfcalls/threads.h
similarity index 100%
rename from src/libelfloader/native/threads.h
rename to src/startup/mldr/elfcalls/threads.h
diff --git a/src/startup/mldr/include/architecture/byte_order.h b/src/startup/mldr/include/architecture/byte_order.h
new file mode 120000
index 000000000..efebd0a7a
--- /dev/null
+++ b/src/startup/mldr/include/architecture/byte_order.h
@@ -0,0 +1 @@
+../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/architecture/byte_order.h
\ No newline at end of file
diff --git a/src/startup/mldr/include/i386/_types.h b/src/startup/mldr/include/i386/_types.h
new file mode 100644
index 000000000..4cf4ec764
--- /dev/null
+++ b/src/startup/mldr/include/i386/_types.h
@@ -0,0 +1,6 @@
+#ifndef _MLDR_I386__TYPES_SHIM_H_
+#define _MLDR_I386__TYPES_SHIM_H_
+
+typedef unsigned int __darwin_natural_t;
+
+#endif // _MLDR_I386__TYPES_SHIM_H_
diff --git a/src/startup/mldr/include/i386/cpu_capabilities.h b/src/startup/mldr/include/i386/cpu_capabilities.h
new file mode 120000
index 000000000..9c3140aa6
--- /dev/null
+++ b/src/startup/mldr/include/i386/cpu_capabilities.h
@@ -0,0 +1 @@
+../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/i386/cpu_capabilities.h
\ No newline at end of file
diff --git a/src/startup/mldr/include/libkern/OSByteOrder.h b/src/startup/mldr/include/libkern/OSByteOrder.h
new file mode 120000
index 000000000..9ceedf027
--- /dev/null
+++ b/src/startup/mldr/include/libkern/OSByteOrder.h
@@ -0,0 +1 @@
+../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/libkern/OSByteOrder.h
\ No newline at end of file
diff --git a/src/startup/mldr/include/libkern/_OSByteOrder.h b/src/startup/mldr/include/libkern/_OSByteOrder.h
new file mode 120000
index 000000000..1341ca0a6
--- /dev/null
+++ b/src/startup/mldr/include/libkern/_OSByteOrder.h
@@ -0,0 +1 @@
+../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/libkern/_OSByteOrder.h
\ No newline at end of file
diff --git a/src/startup/mldr/include/libkern/i386/OSByteOrder.h b/src/startup/mldr/include/libkern/i386/OSByteOrder.h
new file mode 120000
index 000000000..f9f2f4427
--- /dev/null
+++ b/src/startup/mldr/include/libkern/i386/OSByteOrder.h
@@ -0,0 +1 @@
+../../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/libkern/i386/OSByteOrder.h
\ No newline at end of file
diff --git a/src/startup/mldr/include/libkern/i386/_OSByteOrder.h b/src/startup/mldr/include/libkern/i386/_OSByteOrder.h
new file mode 120000
index 000000000..78527ecf7
--- /dev/null
+++ b/src/startup/mldr/include/libkern/i386/_OSByteOrder.h
@@ -0,0 +1 @@
+../../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/libkern/i386/_OSByteOrder.h
\ No newline at end of file
diff --git a/src/startup/mldr/include/mach-o b/src/startup/mldr/include/mach-o
new file mode 120000
index 000000000..0366ea6cf
--- /dev/null
+++ b/src/startup/mldr/include/mach-o
@@ -0,0 +1 @@
+../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/mach-o
\ No newline at end of file
diff --git a/src/startup/mldr/include/mach/boolean.h b/src/startup/mldr/include/mach/boolean.h
new file mode 120000
index 000000000..16055acbc
--- /dev/null
+++ b/src/startup/mldr/include/mach/boolean.h
@@ -0,0 +1 @@
+../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/mach/boolean.h
\ No newline at end of file
diff --git a/src/startup/mldr/include/mach/i386/boolean.h b/src/startup/mldr/include/mach/i386/boolean.h
new file mode 120000
index 000000000..84112c2c9
--- /dev/null
+++ b/src/startup/mldr/include/mach/i386/boolean.h
@@ -0,0 +1 @@
+../../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/mach/i386/boolean.h
\ No newline at end of file
diff --git a/src/startup/mldr/include/mach/i386/vm_param.h b/src/startup/mldr/include/mach/i386/vm_param.h
new file mode 100644
index 000000000..d8d322095
--- /dev/null
+++ b/src/startup/mldr/include/mach/i386/vm_param.h
@@ -0,0 +1,6 @@
+#ifndef _MLDR_MACH_I386_VM_PARAM_SHIM_H_
+#define _MLDR_MACH_I386_VM_PARAM_SHIM_H_
+
+
+
+#endif // _MLDR_MACH_I386_VM_PARAM_SHIM_H_
diff --git a/src/startup/mldr/include/mach/i386/vm_types.h b/src/startup/mldr/include/mach/i386/vm_types.h
new file mode 120000
index 000000000..fd1618003
--- /dev/null
+++ b/src/startup/mldr/include/mach/i386/vm_types.h
@@ -0,0 +1 @@
+../../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/mach/i386/vm_types.h
\ No newline at end of file
diff --git a/src/startup/mldr/include/mach/machine.h b/src/startup/mldr/include/mach/machine.h
new file mode 120000
index 000000000..1fc06aa21
--- /dev/null
+++ b/src/startup/mldr/include/mach/machine.h
@@ -0,0 +1 @@
+../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/mach/machine.h
\ No newline at end of file
diff --git a/src/startup/mldr/include/mach/machine/boolean.h b/src/startup/mldr/include/mach/machine/boolean.h
new file mode 120000
index 000000000..6d9646a55
--- /dev/null
+++ b/src/startup/mldr/include/mach/machine/boolean.h
@@ -0,0 +1 @@
+../../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/mach/machine/boolean.h
\ No newline at end of file
diff --git a/src/startup/mldr/include/mach/machine/thread_status.h b/src/startup/mldr/include/mach/machine/thread_status.h
new file mode 100644
index 000000000..76cf666e9
--- /dev/null
+++ b/src/startup/mldr/include/mach/machine/thread_status.h
@@ -0,0 +1,6 @@
+#ifndef _MLDR_MACH_MACHINE_THREAD_STATUS_SHIM_H_
+#define _MLDR_MACH_MACHINE_THREAD_STATUS_SHIM_H_
+
+
+
+#endif // _MLDR_MACH_MACHINE_THREAD_STATUS_SHIM_H_
diff --git a/src/startup/mldr/include/mach/machine/vm_types.h b/src/startup/mldr/include/mach/machine/vm_types.h
new file mode 120000
index 000000000..fd1618003
--- /dev/null
+++ b/src/startup/mldr/include/mach/machine/vm_types.h
@@ -0,0 +1 @@
+../../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/mach/i386/vm_types.h
\ No newline at end of file
diff --git a/src/startup/mldr/include/mach/vm_prot.h b/src/startup/mldr/include/mach/vm_prot.h
new file mode 120000
index 000000000..99f521287
--- /dev/null
+++ b/src/startup/mldr/include/mach/vm_prot.h
@@ -0,0 +1 @@
+../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/mach/vm_prot.h
\ No newline at end of file
diff --git a/src/startup/mldr/include/sys/_types.h b/src/startup/mldr/include/sys/_types.h
new file mode 100644
index 000000000..eb510ccf1
--- /dev/null
+++ b/src/startup/mldr/include/sys/_types.h
@@ -0,0 +1,6 @@
+#ifndef _MLDR_SYS__TYPES_SHIM_H_
+#define _MLDR_SYS__TYPES_SHIM_H_
+
+
+
+#endif // _MLDR_SYS__TYPES_SHIM_H_
diff --git a/src/startup/mldr/include/sys/_types/_os_inline.h b/src/startup/mldr/include/sys/_types/_os_inline.h
new file mode 120000
index 000000000..627bd5fe0
--- /dev/null
+++ b/src/startup/mldr/include/sys/_types/_os_inline.h
@@ -0,0 +1 @@
+../../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_os_inline.h
\ No newline at end of file
diff --git a/src/startup/mldr/include/sys/commpage.h b/src/startup/mldr/include/sys/commpage.h
new file mode 120000
index 000000000..82ff5172a
--- /dev/null
+++ b/src/startup/mldr/include/sys/commpage.h
@@ -0,0 +1 @@
+../../../../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/commpage.h
\ No newline at end of file
diff --git a/src/startup/mldr/loader.c b/src/startup/mldr/loader.c
new file mode 100644
index 000000000..59356a931
--- /dev/null
+++ b/src/startup/mldr/loader.c
@@ -0,0 +1,330 @@
+#include
+#include
+#include