From 9d41fa327206613d9a38992cb06cc915dde1fbc7 Mon Sep 17 00:00:00 2001 From: Lubos Dolezel Date: Fri, 4 Dec 2015 00:06:51 +0100 Subject: [PATCH] Fix stack trouble in TLS.cpp Start using Apple's libunwind --- src/external/libcxxabi | 2 +- src/libdyld/MachOObject.cpp | 2 +- src/libdyld/MachOObject.h | 1 + src/libdyld/TLS.cpp | 12 +-- src/libdyld/TLS.h | 2 +- src/libdyld/dyld_public.cpp | 195 ++++++++++++++++++++++++++++------- src/libsystem/CMakeLists.txt | 3 +- 7 files changed, 171 insertions(+), 46 deletions(-) diff --git a/src/external/libcxxabi b/src/external/libcxxabi index 649b91806..83ad1e295 160000 --- a/src/external/libcxxabi +++ b/src/external/libcxxabi @@ -1 +1 @@ -Subproject commit 649b91806fafcc4edbcade249ce4bcd690222946 +Subproject commit 83ad1e2950e5f293b372f40ed4d12bd08baa9f00 diff --git a/src/libdyld/MachOObject.cpp b/src/libdyld/MachOObject.cpp index 7b284e002..f8ce5b2b6 100644 --- a/src/libdyld/MachOObject.cpp +++ b/src/libdyld/MachOObject.cpp @@ -226,7 +226,7 @@ void MachOObject::loadSegments() void* rv; int flags = MAP_PRIVATE; - if (strcmp(seg->segname, SEG_PAGEZERO) == 0 || seg->filesize == 0) + if (strcmp(seg->segname, SEG_PAGEZERO) == 0 || seg->vmsize == 0) continue; assert(seg->vmsize >= seg->filesize); diff --git a/src/libdyld/MachOObject.h b/src/libdyld/MachOObject.h index 4a5c2308e..864dac1b1 100644 --- a/src/libdyld/MachOObject.h +++ b/src/libdyld/MachOObject.h @@ -95,6 +95,7 @@ public: inline bool noRecursion() const { return m_noRecursion; } std::vector declaredDependencies() const { return m_file->dylibs(); } + inline MachO* getMachOFile() const { return m_file; } protected: friend class DylibSearch; diff --git a/src/libdyld/TLS.cpp b/src/libdyld/TLS.cpp index dbdefb705..5f4eef79e 100644 --- a/src/libdyld/TLS.cpp +++ b/src/libdyld/TLS.cpp @@ -27,9 +27,9 @@ along with Darling. If not, see . #include "mutex.h" extern "C" { -int __darwin_pthread_key_create(pthread_key_t *key, void (*destructor)(void*)); -int __darwin_pthread_key_delete(pthread_key_t key); -int __darwin_pthread_setspecific(pthread_key_t key, const void *value); +int __darwin_pthread_key_create(long *key, void (*destructor)(void*)); +int __darwin_pthread_key_delete(long key); +int __darwin_pthread_setspecific(long key, const void *value); } static void TLSRunDestructors(void* p); @@ -39,7 +39,7 @@ static std::map m_imagesKey; static Darling::Mutex m_imagesMutex; // Used for a thread-local list of destructor-object pairs (DestructorLinkedListElement) -static pthread_key_t m_keyDestructors; +static long m_keyDestructors; static void TLSSetupDestructors() { @@ -69,7 +69,7 @@ static void TLSRunDestructors(void* p) void Darling::TLSSetup(void* imageKey, std::vector& descriptors, std::vector& initializers, void* start, uintptr_t length) { Darling::MutexLock _l(&m_imagesMutex); - pthread_key_t key; + long key; ImageData* id; pthread_once_t once; @@ -96,7 +96,7 @@ void Darling::TLSTeardown(void* imageKey) Darling::MutexLock _l(&m_imagesMutex); auto it = m_images.find(imageKey); - pthread_key_t key; + long key; if (it == m_images.end()) return; diff --git a/src/libdyld/TLS.h b/src/libdyld/TLS.h index b2d90d710..0ffa845fb 100644 --- a/src/libdyld/TLS.h +++ b/src/libdyld/TLS.h @@ -31,7 +31,7 @@ namespace Darling struct ImageData { - pthread_key_t key; + long key; // Location of initial variable values void* start; diff --git a/src/libdyld/dyld_public.cpp b/src/libdyld/dyld_public.cpp index 1974b60b4..38f52af12 100644 --- a/src/libdyld/dyld_public.cpp +++ b/src/libdyld/dyld_public.cpp @@ -109,41 +109,6 @@ const char* dyld_image_path_containing_address(const void* addr) return module->path().c_str(); } - -bool _dyld_find_unwind_sections(void* addr, struct dyld_unwind_sections* info) -{ -#if 0 - TRACE1(addr); - const FileMap::ImageMap* map = g_file_map.imageMapForAddr(addr); - - if (!map) // in ELF - { - memset(info, 0, sizeof(*info)); - - CBData data = { addr, info }; - - dl_iterate_phdr(dlCallback, &data); - std::cout << "Dwarf section at " << info->dwarf_section << std::endl; - return info->dwarf_section != 0; - } - else // in Mach-O - { - info->mh = map->header; - info->dwarf_section = reinterpret_cast(map->eh_frame.first + map->slide); - info->dwarf_section_length = map->eh_frame.second; - - // FIXME: we would get "malformed __unwind_info" warnings otherwise - // info->compact_unwind_section = reinterpret_cast(map->unwind_info.first + map->slide); - // info->compact_unwind_section_length = map->unwind_info.second; - info->compact_unwind_section = 0; - info->compact_unwind_section_length = 0; - - return true; - } -#endif - return false; -} - int32_t NSVersionOfRunTimeLibrary(const char* libraryName) { return -1; @@ -197,3 +162,163 @@ void _dyld_fork_child(void) // glibc that we have forked. } +struct CBData +{ + void* addr; + struct dyld_unwind_sections* info; +}; + +#pragma pack(1) +struct eh_frame_hdr +{ + uint8_t version, eh_frame_ptr_enc, fde_count_enc, table_enc; + uint8_t eh_frame_ptr[]; +}; +#pragma pack() + +static uintptr_t readEncodedPointer(const eh_frame_hdr* hdr) +{ + uint8_t format = hdr->eh_frame_ptr_enc & 0xf; + uint8_t rel = hdr->eh_frame_ptr_enc & 0xf0; + uintptr_t val; + bool isSigned = false; + + if (hdr->eh_frame_ptr_enc == 0xff) + return 0; + + switch (format) + { + case 1: // unsigned LEB + { + const uint8_t* ptr = reinterpret_cast(hdr->eh_frame_ptr); + val = uleb128(ptr); + break; + } + case 2: // 2 bytes + val = *reinterpret_cast(hdr->eh_frame_ptr); + break; + case 3: + val = *reinterpret_cast(hdr->eh_frame_ptr); + break; + case 4: + val = *reinterpret_cast(hdr->eh_frame_ptr); + break; + case 9: // signed LEB + { + const uint8_t* ptr = reinterpret_cast(hdr->eh_frame_ptr); + val = sleb128(ptr); + break; + } + // FIXME: add 'dlpi_addr' (base address) to these? + case 0xa: + val = *reinterpret_cast(hdr->eh_frame_ptr); + break; + case 0xb: + val = *reinterpret_cast(hdr->eh_frame_ptr); + break; + case 0xc: + val = *reinterpret_cast(hdr->eh_frame_ptr); + break; + default: + return 0; + } + + switch (rel) + { + case 0: // no change + break; + case 0x10: // pcrel + val += reinterpret_cast(hdr) + 4; + break; + case 0x30: // eh_frame_hdr rel + val += reinterpret_cast(hdr); + break; + default: + return 0; + } + + return val; +} + +static int dlCallback(struct dl_phdr_info *info, size_t size, void *data) +{ + CBData* cbdata = static_cast(data); + bool addrMatch = false; + void* maxAddr = 0; + const eh_frame_hdr* ehdr = 0; + + if (cbdata->info->dwarf_section) // we already have a match + return 0; + + //std::cout << "Looking into " << info->dlpi_name << std::endl; + + if (size < offsetof(struct dl_phdr_info, dlpi_phnum)) + return 0; + + for (int i = 0; i < info->dlpi_phnum; i++) + { + const ElfW(Phdr)* phdr = &info->dlpi_phdr[i]; + + if (phdr->p_type == PT_LOAD) + { + void* from = reinterpret_cast(uintptr_t(info->dlpi_addr) + uintptr_t(phdr->p_vaddr)); + void* to = reinterpret_cast(from) + phdr->p_memsz; + + if (cbdata->addr >= from && cbdata->addr < to) + addrMatch = true; + if (to > maxAddr) + maxAddr = to; // TODO: could this be improved? libunwind does the same + } + else if (phdr->p_type == PT_GNU_EH_FRAME) + { + //std::cout << "Found .eh_frame_hdr in " << info->dlpi_name << std::endl; + ehdr = reinterpret_cast(uintptr_t(info->dlpi_addr) + phdr->p_vaddr); + // cbdata->info->dwarf_section_length = phdr->p_memsz; + } + } + + if (addrMatch && ehdr) + { + //std::cout << "*** Match found! " << info->dlpi_name << std::endl; + + // Now we find .eh_frame from .eh_frame_hdr + if (ehdr->version != 1) + return 0; + cbdata->info->dwarf_section = reinterpret_cast(readEncodedPointer(ehdr)); + cbdata->info->dwarf_section_length = uintptr_t(maxAddr) - uintptr_t(cbdata->info->dwarf_section); + } + + return 0; +} + +bool _dyld_find_unwind_sections(void* addr, struct dyld_unwind_sections* info) +{ + TRACE1(addr); + MachOObject* module = MachOMgr::instance()->objectForAddress((void*) addr); + + if (!module) // try ELF + { + memset(info, 0, sizeof(*info)); + + CBData data = { addr, info }; + + dl_iterate_phdr(dlCallback, &data); + // std::cout << "Dwarf section at " << info->dwarf_section << std::endl; + return info->dwarf_section != 0; + } + else // in Mach-O + { + info->mh = (const struct mach_header *) module->baseAddress(); + info->dwarf_section = reinterpret_cast(module->getMachOFile()->get_eh_frame().first + module->slide()); + info->dwarf_section_length = module->getMachOFile()->get_eh_frame().second; + + // FIXME: we would get "malformed __unwind_info" warnings otherwise + info->compact_unwind_section = reinterpret_cast(module->getMachOFile()->get_unwind_info().first + module->slide()); + info->compact_unwind_section_length = module->getMachOFile()->get_unwind_info().second; + //info->compact_unwind_section = 0; + //info->compact_unwind_section_length = 0; + + return true; + } +} + diff --git a/src/libsystem/CMakeLists.txt b/src/libsystem/CMakeLists.txt index a3d29762a..48f3099d6 100644 --- a/src/libsystem/CMakeLists.txt +++ b/src/libsystem/CMakeLists.txt @@ -40,8 +40,7 @@ add_library(system SHARED ${libsystem_sources}) target_link_libraries(system PRIVATE dyld) target_link_libraries(system PUBLIC system_malloc system_c system_kernel keymgr system_m system_info system_notify libdispatch_shared objc launch - removefile system_copyfile) -# FIXME: unwind is removed until it receives EH sections from ELFs too + removefile system_copyfile unwind) install(TARGETS system DESTINATION lib${SUFFIX}/darling) -- 2.51.2