diff --git a/src/dyld/dyld.cpp b/src/dyld/dyld.cpp index 1c5e0fb69..6c8607be8 100644 --- a/src/dyld/dyld.cpp +++ b/src/dyld/dyld.cpp @@ -20,6 +20,7 @@ along with Darling. If not, see . #include #include #include +#include #include #include #include @@ -89,6 +90,9 @@ int main(int argc, char** argv, char** envp) __prefix_set(path); unprefixed_argv0 = __prefix_untranslate_path(argv[1], strlen(argv[1])); } + + if (getenv("DYLD_GDBJIT") != nullptr) + Darling::SetupGDBJIT(); if (isELF(argv[1])) { diff --git a/src/libdyld/CMakeLists.txt b/src/libdyld/CMakeLists.txt index cf6236b69..edbcf6223 100644 --- a/src/libdyld/CMakeLists.txt +++ b/src/libdyld/CMakeLists.txt @@ -40,6 +40,7 @@ set(dyld_SRCS environ.c threads.cpp VirtualPrefix.cpp + gdbjit.cpp ) #if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64" OR ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "i686") diff --git a/src/libdyld/eh/BufWriter.h b/src/libdyld/eh/BufWriter.h index 492d0b297..cd3219277 100644 --- a/src/libdyld/eh/BufWriter.h +++ b/src/libdyld/eh/BufWriter.h @@ -28,7 +28,7 @@ class BufWriter public: BufWriter(void* mem, uintptr_t length); - template void writeType(T t) { writeBlock(&t, sizeof(T)); } + template void writeType(const T& t) { writeBlock(&t, sizeof(T)); } void writePtr(uintptr_t v) { return writeType(v); } void writePtrS(intptr_t v) { return writeType(v); } diff --git a/src/libdyld/gdbjit.cpp b/src/libdyld/gdbjit.cpp new file mode 100644 index 000000000..b413606cc --- /dev/null +++ b/src/libdyld/gdbjit.cpp @@ -0,0 +1,420 @@ +/* +This file is part of Darling. + +Copyright (C) 2016 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 +#include "MachOMgr.h" +#include "MachOObject.h" +#include "gdbjit.h" +#include +#include +#include +#include +#include "eh/BufWriter.h" + +enum jit_actions_t +{ + JIT_NOACTION = 0, + JIT_REGISTER_FN, + JIT_UNREGISTER_FN +}; + +struct jit_code_entry +{ + struct jit_code_entry *next_entry; + struct jit_code_entry *prev_entry; + uint8_t *symfile_addr; + uint64_t symfile_size; +}; + +struct jit_descriptor +{ + uint32_t version; + /* This type should be jit_actions_t, but we use uint32_t + to be explicit about the bitwidth. */ + uint32_t action_flag; + struct jit_code_entry *relevant_entry; + struct jit_code_entry *first_entry; +}; + +/* GDB puts a breakpoint in this function. */ +extern "C" +void __attribute__((noinline)) __jit_debug_register_code() { }; + +/* Make sure to specify the version statically, because the + debugger may check the version before we can set it. */ +extern "C" +struct jit_descriptor __jit_debug_descriptor = { 1, 0, 0, 0 }; + +// MachO loading operates under a mutex, so the following code needs no further locking +// when registering stuff with GDB and so on. +static std::map g_machoToElf; +static void OnMachOLoaded(const struct ::mach_header* mh, intptr_t vmaddr_slide); +static void OnMachOUnloaded(const struct ::mach_header* mh, intptr_t vmaddr_slide); + +void Darling::SetupGDBJIT() +{ + Darling::MachOMgr* mgr = Darling::MachOMgr::instance(); + mgr->registerLoadHook(OnMachOLoaded); + mgr->registerUnloadHook(OnMachOUnloaded); +} + +static int MachO2ElfFlags(vm_prot_t prot) +{ + int flags = 0; + if (prot & VM_PROT_READ) + flags |= PF_R; + if (prot & VM_PROT_WRITE) + flags |= PF_W; + if (prot & VM_PROT_EXECUTE) + flags |= PF_X; + return flags; +} + +void OnMachOLoaded(const struct ::mach_header* mh, intptr_t vmaddr_slide) +{ + Darling::MachOObject* object = Darling::MachOMgr::instance()->objectByHeader(const_cast(mh)); + Darling::ELFProducer producer; + MachO* mo; + + mo = object->getMachOFile(); + const std::vector symbols = mo->symbols(); + + for (const MachO::Symbol& sym : symbols) + { + producer.addSymbol(sym.name.c_str(), (void*)(sym.addr + vmaddr_slide), true, false); + } + + if (mo->is64()) + { + const std::vector& segs = mo->segments64(); + for (const segment_command_64* seg : segs) + { + producer.addSegment(Darling::ELFProducer::Segment { seg->segname, seg->fileoff, (void*)(seg->vmaddr + vmaddr_slide), + seg->vmsize, seg->filesize, MachO2ElfFlags(seg->initprot) }); + } + } + else + { + const std::vector& segs = mo->segments(); + for (const segment_command* seg : segs) + { + producer.addSegment(Darling::ELFProducer::Segment { seg->segname, seg->fileoff, (void*)(seg->vmaddr + vmaddr_slide), + seg->vmsize, seg->filesize, MachO2ElfFlags(seg->initprot) }); + } + } + + producer.setEHSection(mo->get_eh_frame().first, mo->get_eh_frame().second); + + std::pair elfData = producer.produceElf(); + jit_code_entry* entry = new jit_code_entry; + + entry->symfile_addr = elfData.first; + entry->symfile_size = elfData.second; + + std::ofstream dump("/tmp/test.elf"); + dump.write((char*)entry->symfile_addr, entry->symfile_size); + dump.close(); + + entry->next_entry = __jit_debug_descriptor.first_entry; + if (entry->next_entry) + entry->next_entry->prev_entry = entry; + entry->prev_entry = nullptr; + + g_machoToElf[object] = entry; + + std::cout << "Registering " << object->name() << std::endl; + + if (!__jit_debug_descriptor.first_entry) + __jit_debug_descriptor.first_entry = entry; + + __jit_debug_descriptor.relevant_entry = entry; + __jit_debug_descriptor.action_flag = JIT_REGISTER_FN; + __jit_debug_register_code(); +} + +void OnMachOUnloaded(const struct ::mach_header* mh, intptr_t vmaddr_slide) +{ + Darling::MachOObject* object = Darling::MachOMgr::instance()->objectByHeader(const_cast(mh)); + jit_code_entry* jitEntry; + + std::map::iterator it = g_machoToElf.find(object); + + if (it == g_machoToElf.end()) + return; + + jitEntry = it->second; + + // Remove entry from linked list + if (jitEntry->prev_entry) + jitEntry->prev_entry->next_entry = jitEntry->next_entry; + if (jitEntry->next_entry) + jitEntry->next_entry->prev_entry = jitEntry->prev_entry; + + if (__jit_debug_descriptor.first_entry == jitEntry) + __jit_debug_descriptor.first_entry = nullptr; + + // Signal removal to GDB + __jit_debug_descriptor.relevant_entry = jitEntry; + __jit_debug_descriptor.action_flag = JIT_UNREGISTER_FN; + __jit_debug_register_code(); + + g_machoToElf.erase(it); + delete [] jitEntry->symfile_addr; + delete jitEntry; +} + +Darling::ELFProducer::ELFProducer() +{ + addString(""); + m_dotSymtab = addString(".symtab"); + m_dotStrtab = addString(".strtab"); + m_dotText = addString(".text"); + // m_dotEhframe = addString(".eh_frame"); + + ElfW(Shdr) shdr; + + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_NULL; + addSection(shdr); +} + +void Darling::ELFProducer::addSymbol(const char* name, void* addr, bool function, bool local) +{ + ElfW(Sym) sym; + + sym.st_name = addString(name); + sym.st_value = uintptr_t(addr); + sym.st_info = ELF32_ST_INFO(local ? STB_LOCAL : STB_GLOBAL, function ? STT_FUNC : STT_OBJECT); + sym.st_shndx = SHN_ABS; + sym.st_other = 0; + sym.st_size = 0x100; + + m_symbols.push_back(sym); +} + +void Darling::ELFProducer::addSegment(Segment seg) +{ + m_segments.push_back(seg); +} + +void Darling::ELFProducer::setEHSection(uintptr_t addr, uintptr_t size) +{ + m_ehAddr = addr; + m_ehSize = size; +} + +uintptr_t Darling::ELFProducer::addString(const char* str) +{ + uintptr_t offset = m_stringTable.size(); + m_stringTable.insert(m_stringTable.end(), str, str + strlen(str) + 1); + return offset; +} + +template +inline static size_t pushStruct(std::vector& buf, const T& _struct) +{ + size_t off = buf.size(); + buf.insert(buf.end(), reinterpret_cast(&_struct), reinterpret_cast((&_struct) + 1)); + return off; +} + +std::pair Darling::ELFProducer::produceElf() +{ + std::unique_ptr bufWriter; + size_t memSize; + uint8_t* mem; + + memSize = sizeof(ElfW(Ehdr)); + memSize += m_stringTable.size(); + memSize += 5*sizeof(ElfW(Shdr)); + memSize += sizeof(ElfW(Sym)) * (2+m_symbols.size()); + + mem = new uint8_t[memSize]; + bufWriter.reset(new BufWriter(mem, memSize)); + + ElfW(Ehdr) elfHeader, *elfHeaderPtr; + + elfHeader.e_ident[EI_MAG0] = ELFMAG0; + elfHeader.e_ident[EI_MAG1] = ELFMAG1; + elfHeader.e_ident[EI_MAG2] = ELFMAG2; + elfHeader.e_ident[EI_MAG3] = ELFMAG3; +#ifdef __x86_64__ + elfHeader.e_ident[EI_CLASS] = ELFCLASS64; +#else + elfHeader.e_ident[EI_CLASS] = ELFCLASS32; +#endif + + elfHeader.e_ident[EI_DATA] = ELFDATA2LSB; + elfHeader.e_ident[EI_VERSION] = EV_CURRENT; + elfHeader.e_ident[EI_OSABI] = ELFOSABI_GNU; + elfHeader.e_ident[EI_ABIVERSION] = 0; + +#if defined(__x86_64__) + elfHeader.e_machine = EM_X86_64; +#elif defined(__i386__) + elfHeader.e_machine = EM_386; +#elif defined(__arm__) + elfHeader.e_machine = EM_ARM; +#else +# warning Add support for your arch here + elfHeader.e_machine = 0; +#endif + + elfHeader.e_type = ET_DYN; + elfHeader.e_entry = 0; + elfHeader.e_flags = 0; + elfHeader.e_phoff = 0; //sizeof(elfHeader); + elfHeader.e_phnum = 0; //m_segments.size() /*+ (m_ehSize ? 1 : 0)*/; + elfHeader.e_phentsize = sizeof(ElfW(Phdr)); + elfHeader.e_shentsize = sizeof(ElfW(Shdr)); + elfHeader.e_shstrndx = 2; // string section + elfHeader.e_shnum = 4 /*+ (m_ehSize ? 1 : 0)*/; // string table + symbol table + elfHeader.e_ehsize = sizeof(elfHeader); + + bufWriter->writeType(elfHeader); + + const Segment* textSegment = nullptr; + for (const Segment& seg : m_segments) + { + if (strcmp(seg.name, "__TEXT") == 0) + textSegment = &seg; + } + + elfHeaderPtr = (ElfW(Ehdr)*) mem; + elfHeaderPtr->e_shoff = bufWriter->relativePos(); + + ElfW(Shdr)* symtab; + + { + ElfW(Shdr) shdr; + + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_NULL; + bufWriter->writeType(shdr); + + shdr.sh_name = m_dotSymtab; + shdr.sh_type = SHT_SYMTAB; + shdr.sh_entsize = sizeof(ElfW(Sym)); + shdr.sh_size = shdr.sh_entsize * (m_symbols.size() + 2); + shdr.sh_addr = 0; + shdr.sh_offset = bufWriter->relativePos() + 3*sizeof(shdr) + m_stringTable.size(); // after this shdr and the string table + shdr.sh_flags = SHF_ALLOC; + shdr.sh_addralign = 0; + shdr.sh_link = 2; // which string table section + shdr.sh_info = 0; + + symtab = (ElfW(Shdr)*) bufWriter->posPtr(); + bufWriter->writeType(shdr); + } + + { + ElfW(Shdr) shdr; + + shdr.sh_name = m_dotStrtab; + shdr.sh_type = SHT_STRTAB; + shdr.sh_size = m_stringTable.size(); + shdr.sh_addr = 0; + shdr.sh_offset = bufWriter->relativePos() + 2*sizeof(shdr); // after this and next shdr + shdr.sh_entsize = 0; + shdr.sh_flags = 0; + shdr.sh_link = 0; + shdr.sh_addralign = 0; + shdr.sh_link = 0; + shdr.sh_info = 0; + + bufWriter->writeType(shdr); + } + + if (textSegment != nullptr) + { + ElfW(Shdr) shdr; + + shdr.sh_name = m_dotText; + shdr.sh_type = SHT_PROGBITS; + shdr.sh_size = textSegment->memSize; + shdr.sh_addr = uintptr_t(textSegment->vaddr); + shdr.sh_offset = 0; + shdr.sh_entsize = 0; + shdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR; + shdr.sh_link = 0; + shdr.sh_addralign = 1; + shdr.sh_link = 0; + shdr.sh_info = 0; + + bufWriter->writeType(shdr); + } + + /* + if (m_ehSize) + { + ElfW(Shdr) shdr; + + shdr.sh_name = m_dotEhframe; + shdr.sh_type = SHT_PROGBITS; + shdr.sh_size = m_ehSize; + shdr.sh_addr = uintptr_t(m_ehAddr); + shdr.sh_offset = 0; + shdr.sh_entsize = 0; + shdr.sh_flags = SHF_ALLOC; + shdr.sh_link = 0; + shdr.sh_addralign = 1; + shdr.sh_link = 0; + shdr.sh_info = 0; + + bufWriter->writeType(shdr); + } + */ + + bufWriter->writeBlock(&m_stringTable[0], m_stringTable.size()); + symtab->sh_addr = (uintptr_t) bufWriter->posPtr(); + + ElfW(Sym) sym; + + sym.st_name = 0; + sym.st_value = 0; + sym.st_info = ELF32_ST_INFO(STB_LOCAL, STT_NOTYPE); + sym.st_shndx = 0; + sym.st_other = 0; + sym.st_size = 0; + + bufWriter->writeType(sym); + + sym.st_name = 0; + sym.st_value = 0; + sym.st_info = ELF32_ST_INFO(STB_LOCAL, STT_SECTION); + sym.st_shndx = 3; + sym.st_other = 0; + + bufWriter->writeType(sym); + + for (ElfW(Sym) sym : m_symbols) + { + sym.st_shndx = 3; // .text section + // sym.st_value -= uintptr_t(textSegment->vaddr); + bufWriter->writeType(sym); + } + + return std::make_pair(mem, memSize); +} + +void Darling::ELFProducer::addSection(const ElfW(Shdr)& section, const std::vector& data) +{ + Section sect { section, data }; + m_sections.push_back(sect); +} diff --git a/src/libdyld/gdbjit.h b/src/libdyld/gdbjit.h new file mode 100644 index 000000000..2999fe6c1 --- /dev/null +++ b/src/libdyld/gdbjit.h @@ -0,0 +1,85 @@ +/* +This file is part of Darling. + +Copyright (C) 2016 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 GDBJIT_H +#define GDBJIT_H +#include +#include +#include + +#ifndef ElfW +#ifdef __x86_64__ +# define ElfW(name) Elf64_##name +#else +# define ElfW(name) Elf32_##name +#endif +#endif + +namespace Darling +{ + // Prepare for supplying information to GDB through its JIT interface + // (although we're surely no JIT compiler) + void SetupGDBJIT(); + + class ELFProducer + { + public: + struct Segment + { + const char* name; + uintptr_t offsetInFile; + void* vaddr; + uintptr_t memSize; + uintptr_t fileSize; + int flags; + }; + + ELFProducer(); + void addSymbol(const char* name, void* addr, bool function, bool local); + void addSegment(Segment seg); + void setEHSection(uintptr_t addr, uintptr_t size); + std::pair produceElf(); + private: + uintptr_t addString(const char* str); + void addSection(const ElfW(Shdr)& section, const std::vector& data = std::vector()); + private: + std::vector m_stringTable; + + struct Symbol + { + uintptr_t name; + void* addr; + bool function, local; + }; + std::vector m_symbols; + std::vector m_segments; + + struct Section + { + ElfW(Shdr) hdr; + std::vector data; + }; + std::vector
m_sections; + + uintptr_t m_ehAddr = 0, m_ehSize = 0; + uintptr_t m_dotSymtab, m_dotStrtab, m_dotText, m_dotEhframe; + }; +} + +#endif /* GDBJIT_H */ +