Something went wrong. Try again.
Cider Isn't Darwin Emulation, Really
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721/*This file is part of Darling.
Copyright (C) 2012 Lubos Dolezel
Darling is free software: you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.
You should have received a copy of the GNU General Public Licensealong with Darling. If not, see <http://www.gnu.org/licenses/>.*/
#include "Trampoline.h"#include <unistd.h>#include <sys/mman.h>#include <stdexcept>#include <cassert>#include <cstring>#include <iostream>#include <fstream>#include <sstream>#include <stack>#include <cxxabi.h>#include <dlfcn.h>#include <sys/syscall.h>#include "../util/log.h"#include "../util/stlutils.h"
TrampolineMgr* TrampolineMgr::m_pInstance = 0;std::map<std::string, TrampolineMgr::FunctionInfo> TrampolineMgr::m_functionInfo;struct timeval TrampolineMgr::m_startup;
static __thread std::stack<TrampolineMgr::ReturnInfo>* g_returnInfo = 0;static __thread std::ofstream* g_logger = 0;static __thread pid_t g_loggerForPid = 0;static const char* LIB_OBJCDARWIN = "libobjc.A.dylib.so";static std::string (*objc_helper)(const std::string& /*invoker*/, void* /*arg1*/, void* /*arg2*/, std::string& /*searchable*/) = 0;
extern "C" void reg_saveall();extern "C" void reg_restoreall();extern "C" void* trampoline_start;extern "C" void* trampoline_end;
#ifndef TEST# include "MachOMgr.h"# include "MachOObject.h"#elseconst char* g_argv[] = { "Trampoline", 0 };#endif
TrampolineMgr::TrampolineMgr(int entries) : m_nNext(0){ assert(m_pInstance == 0); m_pInstance = this;
int ps = getpagesize(); void* mem;
int bytes = entries * sizeof(Trampoline); bytes = (bytes + ps - 1) / ps * ps;
mem = ::mmap(0, bytes, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); if (mem == MAP_FAILED) throw std::runtime_error("Failed to map pages for TrampolineMgr");
m_pMem = static_cast<Trampoline*>(mem); m_nMax = bytes / sizeof(Trampoline); if (char* p = get_current_dir_name()) { m_wd = p; free(p); }
::gettimeofday(&m_startup, nullptr);}
void TrampolineMgr::printPath(){ std::cout << logPath() << std::endl;}
TrampolineMgr::~TrampolineMgr(){ ::munmap(m_pMem, m_nMax * sizeof(Trampoline));}
void* TrampolineMgr::generate(void* targetAddr, const char* name){ if ((targetAddr > m_pMem && targetAddr < m_pMem+m_nMax) || !isExecutable(targetAddr)) return targetAddr; // will not create a trampoline for a trampoline if (strcmp(name, "dlopen") == 0) return targetAddr; // trampolines break RPATH resolution in dlopen if (strcmp(name, "dlsym") == 0) return targetAddr; // trampolines break RTLD_SELF
AddrEntry e = { name, name, targetAddr }; if (m_nNext >= m_nMax) throw std::runtime_error("TrampolineMgr buffer full"); //std::cout << e.name << std::endl; if (e.name.compare(0, 2, "_Z") == 0) { int status; char* n = abi::__cxa_demangle(e.name.c_str(), 0, 0, &status); //std::cout << n << std::endl; if (n) { e.printName = n; e.printName += " - "; e.printName += e.name; free(n); } }
m_entries.push_back(e); m_pMem[m_nNext].init(m_nNext, TrampolineMgr::printInfo, TrampolineMgr::printInfoR); void* addr = &m_pMem[m_nNext++]; // std::cout << "Trampoline for " << name << " is at " << addr << std::endl; return addr;}
bool TrampolineMgr::isExecutable(void* addr){ if (m_memoryMap.empty()) loadMemoryMap(); // std::cout << "isExecutable(): " << addr << std::endl; for (auto it = m_memoryMap.begin(); it != m_memoryMap.end(); it++) { if (addr >= it->start && addr < it->end) return it->executable; } return false;}
std::string TrampolineMgr::inFile(void* addr){ for (auto it = m_memoryMap.begin(); it != m_memoryMap.end(); it++) { if (addr >= it->start && addr < it->end) return it->file; } return "?";}
void TrampolineMgr::invalidateMemoryMap(){ m_memoryMap.clear();}
void TrampolineMgr::loadMemoryMap(){ std::ifstream file("/proc/self/maps"); std::string line; while (std::getline(file, line)) { if (line.empty()) continue; const char* s = line.c_str(); MemoryPages pages; pages.start = (void*) strtol(s, (char**) &s, 16); s++; pages.end = (void*) strtol(s, (char**) &s, 16); pages.executable = (*(s+3) == 'x') && (*(s+2) == '-'); // not writable if (line.size() > 74) pages.file = line.substr(73); //std::cout << line << " -> " << pages.start << " - " << pages.end << " " << pages.executable << std::endl; m_memoryMap.push_back(pages); }}
void TrampolineMgr::loadFunctionInfo(const char* path){ std::ifstream file(path); std::string line; while (std::getline(file, line)) { size_t p = line.rfind(':'); if (p == std::string::npos) continue; FunctionInfo info; info.retType = line.at(p+1); info.arguments = line.substr(p+2); m_functionInfo[line.substr(0, p)] = info; }}
std::string TrampolineMgr::formatTime(double ms){ const double HRS = 1000.0*60*60; const double MINS = 1000.0*60; const double SECS = 1000.0; std::stringstream ss;
if (ms >= HRS) { int hrs = int(ms / HRS); ss << hrs << 'h'; ms -= double(hrs) * HRS; } if (ms >= MINS) { int mins = int(ms / MINS); ss << mins << 'm'; ms -= double(mins) * MINS; } if (ms >= SECS) { int secs = int(ms / SECS); ss << secs << 's'; ms -= double(secs) * SECS; } ss << int(ms) /*<< "ms"*/; // double value here sometime causes crashes, why? ss << '.' << int(ms*10)%10 << "ms"; // workaround return ss.str();}
std::string TrampolineMgr::callTime(){ double ms; struct timeval now;
::gettimeofday(&now, nullptr); ms = (now.tv_sec - g_returnInfo->top().callTime.tv_sec) * 1000; ms += (now.tv_usec - g_returnInfo->top().callTime.tv_usec) / 1000.0;
return formatTime(ms);}
std::string TrampolineMgr::timeStamp(){ double ms; struct timeval now;
::gettimeofday(&now, nullptr); ms = (now.tv_sec - m_pInstance->m_startup.tv_sec) * 1000; ms += (now.tv_usec - m_pInstance->m_startup.tv_usec) / 1000.0;
return formatTime(ms);}
std::string TrampolineMgr::logPath(){ pid_t pid = syscall(SYS_gettid); std::stringstream ss; const char* progname = Darling::MachOMgr::instance()->mainModule()->name(); //size_t pos;
//pos = progname.rfind('/'); //if (pos != std::string::npos) // progname = progname.substr(pos+1);
ss << m_wd << '/' << progname << '.' << pid << ".log"; return ss.str();}
std::ostream* TrampolineMgr::getLogger(){ if (g_loggerForPid == syscall(SYS_gettid)) return g_logger ? g_logger : &std::cerr; else { delete g_logger; g_logger = new std::ofstream(logPath(), std::ios_base::out | std::ios_base::app);
if (!g_logger->is_open()) { delete g_logger; g_logger = 0; } g_loggerForPid = getpid(); return g_logger; }}
bool TrampolineMgr::loadObjCHelper(){ *((void**)&objc_helper) = ::dlsym(RTLD_DEFAULT, "trampoline_objcMsgInfo"); return objc_helper != 0;}
void* TrampolineMgr::printInfo(uint32_t index, CallStack* stack){ const AddrEntry& e = m_pInstance->m_entries[index]; FunctionInfo* info = 0; std::map<std::string, FunctionInfo>::iterator it; std::ostream* out; ReturnInfo retInfo; ArgumentWalker w(stack); std::string searchable = e.name;
out = m_pInstance->getLogger(); if (!g_returnInfo) g_returnInfo = new std::stack<TrampolineMgr::ReturnInfo>; std::string stamp = timeStamp(); (*out) << std::string(20 - stamp.size(), ' '); (*out) << '[' << timeStamp() << "] "; (*out) << std::string(g_returnInfo->size(), ' '); // Special handling for Objective-C if (string_startsWith(e.name, "objc_msgSend")) { if (!objc_helper) { if (!loadObjCHelper()) goto no_objc; } if (string_endsWith(e.name, "ret")) w.nextPointer(); // skip the fpret/stret argument (*out) << objc_helper(e.name, w.nextPointer(), w.nextPointer(), searchable); } else no_objc: (*out) << e.printName; (*out) << '('; // std::cout << "Looking for " << searchable << std::endl; it = m_functionInfo.find(searchable); if (it != m_functionInfo.end()) { bool first = true; for (char c : it->second.arguments) { if (!first) (*out) << ", "; else first = false; (*out) << w.next(c); } (*out) << ") "; } else (*out) << "?) "; (*out) << "ret_ip=" << stack->retAddr << std::endl << std::flush;
retInfo.retAddr = stack->retAddr; retInfo.it = it; gettimeofday(&retInfo.callTime, 0);
g_returnInfo->push(retInfo); return m_pInstance->m_entries[index].addr;}
void* TrampolineMgr::printInfoR(uint32_t index, CallStack* stack){ void* rv = g_returnInfo->top().retAddr; std::ostream* out;
out = m_pInstance->getLogger();
auto it = g_returnInfo->top().it; std::string stamp = timeStamp(); (*out) << std::string(20 - stamp.size(), ' '); (*out) << '[' << stamp << "] "; (*out) << std::string(g_returnInfo->size(), ' ');
ArgumentWalker w(stack); if (it != m_functionInfo.end()) (*out) << "-> " << w.ret(it->second.retType); else (*out) << "-> ? (" << w.ret('p') << ')';
(*out) << " {" << callTime() << "} errno=" << errno << std::endl << std::flush; g_returnInfo->pop();
// standard retval in rax, double in xmm0 return rv;}
void Trampoline::init(uint32_t i, void* (*pDebug)(uint32_t,TrampolineMgr::CallStack*), void* (*pDebugR)(uint32_t,TrampolineMgr::CallStack*)){ uintptr_t funcLen = uintptr_t(&trampoline_end) - uintptr_t(&trampoline_start); memcpy(this, &trampoline_start, funcLen); this->reg_saveall = reinterpret_cast<uint64_t>(::reg_saveall); this->reg_saveall2 = reinterpret_cast<uint64_t>(::reg_saveall); this->reg_restoreall = reinterpret_cast<uint64_t>(::reg_restoreall); this->reg_restoreall2 = reinterpret_cast<uint64_t>(::reg_restoreall); this->index = i; this->index2 = i; this->debugFcn = reinterpret_cast<uint64_t>(pDebug); this->debugFcnR = reinterpret_cast<uint64_t>(pDebugR);}
TrampolineMgr::ArgumentWalker::ArgumentWalker(CallStack* stack): m_stack(stack){#ifdef __x86_64__ m_indexInt = m_indexXmm = 0;#else m_indexArg = 0;#endif}
TrampolineMgr::ArgumentWalker::ArgumentWalker(CallStack* stack, OutputArguments args): m_stack(stack), m_pointers(args){#ifdef __x86_64__ m_indexInt = m_indexXmm = 0;#else m_indexArg = 0;#endif}
#ifdef __x86_64__uint64_t TrampolineMgr::ArgumentWalker::next64bit(){ uint64_t rv; if (m_indexInt == 0) rv = m_stack->rdi; else if (m_indexInt == 1) rv = m_stack->rsi; else if (m_indexInt == 2) rv = m_stack->rdx; else if (m_indexInt == 3) rv = m_stack->rcx; else if (m_indexInt == 4) rv = m_stack->r8; else if (m_indexInt == 5) rv = m_stack->r9; else rv = m_stack->moreArguments[m_indexInt-6]; m_indexInt++; return rv;}
long long TrampolineMgr::ArgumentWalker::nextLL(){ uint64_t v = next64bit(); return *((long*) &v);}
int TrampolineMgr::ArgumentWalker::nextInt(){ uint64_t v = next64bit(); return *((int*) &v);}
long double TrampolineMgr::ArgumentWalker::nextLongDouble(){ long double rv; if (m_indexXmm >= 0 && m_indexXmm <= 7) return m_stack->xmm[m_indexXmm]; else throw std::out_of_range("8th double argument not supported"); m_indexXmm++; return rv;}
double TrampolineMgr::ArgumentWalker::nextDouble(){ long double v = nextLongDouble(); return *((double*)&v);}
float TrampolineMgr::ArgumentWalker::nextFloat(){ long double v = nextLongDouble(); return *((float*)&v);}
void* TrampolineMgr::ArgumentWalker::nextPointer(){ uint64_t v = next64bit(); return (void*) v;}
#else
void* TrampolineMgr::ArgumentWalker::nextPointer(){ uint32_t v = m_stack->arguments[m_indexArg++]; return (void*) v;}
int TrampolineMgr::ArgumentWalker::nextInt(){ uint32_t v = m_stack->arguments[m_indexArg++]; return *((int*) &v);}
long long TrampolineMgr::ArgumentWalker::nextLL(){ union { uint32_t b32[2]; long long b64; } ll;
ll.b32[0] = m_stack->arguments[m_indexArg+1]; ll.b32[1] = m_stack->arguments[m_indexArg]; m_indexArg += 2;
return ll.b64;}
float TrampolineMgr::ArgumentWalker::nextFloat(){ uint32_t v = m_stack->arguments[m_indexArg++]; return *((float*) &v);}
double TrampolineMgr::ArgumentWalker::nextDouble(){ double* d = (double*) &m_stack->arguments[m_indexArg]; m_indexArg += 2; return *d;}
#endif
std::string TrampolineMgr::ArgumentWalker::next(char type){ std::stringstream ss; void* ptr; if (type == 'u') ss << unsigned(nextInt()); else if (type == 'i') ss << nextInt(); else if (type == 'q') ss << nextLL(); else if (type == 'f') ss << nextFloat(); else if (type == 'd') ss << nextDouble(); else if (type == 'p' || isupper(type)) ss << nextPointer(); else if (type == 'c') ss << char(nextInt()); else if (type == 's') { const char* s = (const char*) nextPointer(); ss << (void*)s; if (s) ss << " \"" << safeString(s) << '"'; } else if (type == 'v') ss << "(void)"; else ss << '?'; if (isupper(type)) m_pointers.push_back(std::make_pair(tolower(type), ptr)); return ss.str();}
#ifdef __x86_64__std::string TrampolineMgr::ArgumentWalker::ret(char type){ std::stringstream ss; if (type == 'u') ss << m_stack->rax; else if (type == 'c') ss << char(m_stack->rax); else if (type == 'i' || type == 'q') { uint64_t u = m_stack->rax; ss << *((int64_t*) &u); } else if (type == 'f') { long double d = m_stack->xmm[0]; ss << *((float*)&d); } else if (type == 'd') { long double d = m_stack->xmm[0]; ss << *((double*)&d); } else if (type == 'p' || isupper(type)) ss << (void*)m_stack->rax << std::dec; else if (type == 's') { const char* s = (const char*) m_stack->rax; ss << (void*)s << " \"" << safeString(s) << '"'; } else ss << '?'; return ss.str();}#elsestd::string TrampolineMgr::ArgumentWalker::ret(char type){ std::stringstream ss; if (type == 'u') ss << m_stack->eax; else if (type == 'c') ss << char(m_stack->eax); else if (type == 'i') { uint32_t u = m_stack->eax; ss << *((int32_t*) &u); } else if (type == 'q') { union { int64_t v64; uint32_t v32[2]; } q; q.v32[0] = m_stack->eax; q.v32[1] = m_stack->edx; ss << q.v64; } //else if (type == 'f') //{ // double d = m_stack->st0; // ss << * (((float*)&d)+1); //} else if (type == 'd' || type == 'f') { double d = m_stack->st0; ss << d; } else if (type == 'p' || isupper(type)) ss << (void*)m_stack->eax << std::dec; else if (type == 's') { const char* s = (const char*) m_stack->eax; ss << (void*)s << " \"" << safeString(s) << '"'; } else ss << '?'; return ss.str();}#endif
std::string TrampolineMgr::ArgumentWalker::safeString(const char* in){ if (!in) return std::string(); std::stringstream rv; while (*in) { if (*in >= 32) rv << *in; else { if (*in == '\n') rv << "\\n"; else if (*in == '\r') rv << "\\r"; else if (*in == '\t') rv << "\\t"; else rv << "\\x" << std::hex << int(*in); } in++; } return rv.str();}
#ifdef TEST
float mytestfunc(int a, int b, double c){ return a*b+c;}
long long qfunc(){ return 1;}
int main(){ TrampolineMgr* mgr = new TrampolineMgr; TrampolineMgr::loadFunctionInfo("/tmp/fi"); float (*pFunc)(int,int,double) = (float (*)(int,int,double)) mgr->generate((void*) &mytestfunc, "mytestfunc"); int (*pPrintf)(FILE* f, const char*,...) = (int (*)(FILE* f, const char*,...)) mgr->generate((void*) &fprintf, "printf"); std::cout << pFunc(2,3,0.5) << std::endl; //pPrintf(stdout, "Hello world: %s\n", "Test"); delete mgr; return 0;}
#endif