/* 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 "MachOMgr.h" #include #include #include #include "MachOObject.h" #include "NativeObject.h" #include #include namespace Darling { bool MachOMgr::m_bTerminated = false; extern "C" char** __darwin_environ; MachOMgr::MachOMgr() : m_mainModule(nullptr), m_bindAtLaunch(false), m_printInitializers(false), m_printLibraries(false), m_printSegments(false), m_printBindings(false), m_printRpathExpansion(false), m_loadAny(false), m_forceFlatNamespace(false), m_addedDefaultLoader(false), m_destroying(false) { __darwin_environ = environ; } MachOMgr::~MachOMgr() { m_destroying = true; if (m_mainModule) m_mainModule->delRef(); while (!m_loadablesInOrder.empty()) { LoadableObject* obj = m_loadablesInOrder.front(); if (dynamic_cast(obj) != nullptr) obj->unload(); else m_loadablesInOrder.pop_front(); // let ld.so do it on its own, otherwise crashes may follow } m_bTerminated = true; } void MachOMgr::atexit() { for (LoadableObject* obj : m_loadablesInOrder) { obj->atExit(); } } MachOMgr* MachOMgr::instance() { static MachOMgr obj; return &obj; } void* MachOMgr::maxAddress() const { Darling::RWMutexReadLock l(m_lock); if (m_objects.empty()) { return nullptr; } else { auto it = m_objects.end(); void* addr; it--; addr = it->second->maxAddress(); assert(uintptr_t(addr) % getpagesize() == 0); return addr; } } MachOObject* MachOMgr::objectForAddress(void* addr) { Darling::RWMutexReadLock l(m_lock); auto it = m_objects.upper_bound(addr); if (it == m_objects.begin()) return nullptr; it--; if (it->second->maxAddress() < addr) return nullptr; return it->second; } void MachOMgr::registerLoadHook(LoaderHookFunc* func) { Darling::RWMutexWriteLock l(m_lock); m_loadHooks.insert(func); } void MachOMgr::registerUnloadHook(LoaderHookFunc* func) { Darling::RWMutexWriteLock l(m_lock); m_unloadHooks.insert(func); } void MachOMgr::deregisterLoadHook(LoaderHookFunc* func) { Darling::RWMutexWriteLock l(m_lock); m_loadHooks.erase(func); } void MachOMgr::deregisterUnloadHook(LoaderHookFunc* func) { Darling::RWMutexWriteLock l(m_lock); m_unloadHooks.erase(func); } void MachOMgr::addDefaultLoader() { // if (!m_addedDefaultLoader) // { // add(new NativeObject(RTLD_DEFAULT, "")); // m_addedDefaultLoader = true; // } } void MachOMgr::add(MachOObject* obj, bool mainModule) { Darling::RWMutexWriteLock l(m_lock); if (mainModule) addDefaultLoader(); m_objects[obj->baseAddress()] = obj; m_objectNames[obj->path()] = obj; m_objectHeaders[obj->getMachHeader()] = obj; m_objectsInOrder.push_back(obj); m_loadablesInOrder.push_back(obj); if (mainModule) { assert(m_mainModule == nullptr); m_mainModule = obj; } } void MachOMgr::notifyAdd(MachOObject* obj) { assert(m_objects.find(obj->baseAddress()) != m_objects.end()); for (LoaderHookFunc* func : m_loadHooks) func(obj->getMachHeader(), obj->slide()); } void MachOMgr::remove(MachOObject* obj) { Darling::RWMutexWriteLock l(m_lock); for (LoaderHookFunc* func : m_unloadHooks) func(obj->getMachHeader(), obj->slide()); m_objects.erase(obj->baseAddress()); m_objectNames.erase(obj->path()); m_objectHeaders.erase(obj->getMachHeader()); auto it = std::find(m_objectsInOrder.begin(), m_objectsInOrder.end(), obj); if (it != m_objectsInOrder.end()) m_objectsInOrder.erase(it); auto itl = std::find(m_loadablesInOrder.begin(), m_loadablesInOrder.end(), obj); if (itl != m_loadablesInOrder.end()) m_loadablesInOrder.erase(itl); if (m_mainModule == obj) m_mainModule = nullptr; } void MachOMgr::add(NativeObject* obj) { LOG << "MachOMgr::add: " << obj << " - " << obj->name() << std::endl; m_objectNames[obj->path()] = obj; m_loadablesInOrder.push_back(obj); // m_objectHeaders[(struct mach_header*) obj->baseAddress()] = obj; m_nativeRefToObject[obj->nativeRef()] = obj; } void MachOMgr::remove(NativeObject* obj) { LOG << "MachOMgr::remove: " << obj << " - " << obj->name() << std::endl; auto it = std::find(m_loadablesInOrder.begin(), m_loadablesInOrder.end(), obj); if (it != m_loadablesInOrder.end()) m_loadablesInOrder.erase(it); //auto it2 = m_objectHeaders.find(obj->baseAddress()); //if (it2 != m_objectHeaders.end()) // m_objectHeaders.erase(it2); m_objectNames.erase(obj->path()); m_nativeRefToObject.erase(obj->nativeRef()); } MachOObject* MachOMgr::objectByIndex(size_t index) { Darling::RWMutexReadLock l(m_lock); if (index >= m_objectsInOrder.size()) return nullptr; else return m_objectsInOrder[index]; } MachOObject* MachOMgr::objectByHeader(struct mach_header* hdr) { Darling::RWMutexReadLock l(m_lock); auto it = m_objectHeaders.find(hdr); if (it == m_objectHeaders.end()) return nullptr; else return it->second; } NativeObject* MachOMgr::objectByNativeRef(void* nativeRef) { auto it = m_nativeRefToObject.find(nativeRef); if (it == m_nativeRefToObject.end()) return nullptr; else return it->second; } void* MachOMgr::getExportedSymbol(const std::string& symbolName, LoadableObject* nextAfter) { Darling::RWMutexReadLock l(m_lock); void* weak = nullptr; bool isAfter = false; // TODO: add default loader if missing for (auto it = m_loadablesInOrder.begin(); it != m_loadablesInOrder.end(); it++) { void* p; if (nextAfter && !isAfter) { if (*it == nextAfter) isAfter = true; continue; } if (!(*it)->globalExports()) continue; p = (*it)->getExportedSymbol(symbolName, true); // try non-weak only if (p) return p; if (!weak) weak = (*it)->getExportedSymbol(symbolName, false); // save the first weak export as a fallback } return weak; } LoadableObject* MachOMgr::lookup(const std::string& absolutePath) { Darling::RWMutexReadLock l(m_lock); char path[PATH_MAX]; realpath(absolutePath.c_str(), path); auto it = m_objectNames.find(path); if (it != m_objectNames.end()) return it->second; else return nullptr; } bool MachOMgr::detectSysRootFromPath(std::string path) { if (path.empty()) return false; if (path[0] != '/') { char* rp = realpath(path.c_str(), nullptr); if (!rp) return false; path = rp; free(rp); } size_t pos = path.find("/usr/"); if (pos != std::string::npos && pos != 0) { m_sysroot = path.substr(0, pos); return true; } return false; } #ifdef HAS_DEBUG_HELPERS void MachOMgr::setUseTrampolines(bool useTrampolines, const std::string& funcInfo) { delete m_pTrampolineMgr; if (useTrampolines) { m_pTrampolineMgr = new TrampolineMgr; m_pTrampolineMgr->loadFunctionInfo(funcInfo.c_str()); } else { m_pTrampolineMgr = nullptr; } } void MachOMgr::setIgnoreMissingSymbols(bool ignoreMissingSymbols) { delete m_pUndefMgr; if (ignoreMissingSymbols) m_pUndefMgr = new UndefMgr; else m_pUndefMgr = nullptr; } #else void MachOMgr::setUseTrampolines(bool useTrampolines, const std::string& funcInfo) { } void MachOMgr::setIgnoreMissingSymbols(bool ignoreMissingSymbols) { } #endif } // namespace Darling