/* This file is part of Darling. Copyright (C) 2012 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 "Trampoline.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "../util/log.h" #include "../util/stlutils.h" TrampolineMgr* TrampolineMgr::m_pInstance = 0; std::map TrampolineMgr::m_functionInfo; struct timeval TrampolineMgr::m_startup; static __thread std::stack* 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" #else const 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(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::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; 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(::reg_saveall); this->reg_saveall2 = reinterpret_cast(::reg_saveall); this->reg_restoreall = reinterpret_cast(::reg_restoreall); this->reg_restoreall2 = reinterpret_cast(::reg_restoreall); this->index = i; this->index2 = i; this->debugFcn = reinterpret_cast(pDebug); this->debugFcnR = reinterpret_cast(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(); } #else std::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