From 84535d893a6d233ca6618c0c6acaba782bb60a52 Mon Sep 17 00:00:00 2001 From: Davide Beatrici Date: Sun, 6 Nov 2022 21:22:12 +0100 Subject: [PATCH] FIX(overlay): Implement library search list for dlsym(), add "libc.so.6" to it glibc 2.34 moved all functionality of various libraries into the main one. As a result, dlsym() is now exported by libc instead of libdl. This commit also makes the related code more robust. We now iterate through a common library name/path list, regardless of the OS. --- overlay_gl/init_unix.c | 212 ++++++++++++++++++++++------------------- 1 file changed, 112 insertions(+), 100 deletions(-) diff --git a/overlay_gl/init_unix.c b/overlay_gl/init_unix.c index cf17acfe133..0cd0c1f58f0 100644 --- a/overlay_gl/init_unix.c +++ b/overlay_gl/init_unix.c @@ -162,125 +162,137 @@ __attribute__((visibility("default"))) void *dlsym(void *handle, const char *nam } static int find_odlsym() { -#if defined(__linux__) - void *dl = dlopen("libdl.so.2", RTLD_LAZY); - if (!dl) { - ods("Failed to open libdl.so.2!"); - return -1; - } + // clang-format off + const char *libs[] = { + "libc.so.6", + "libdl.so.2", + "/libexec/ld-elf.so.1" + }; + // clang-format on + + for (uint8_t libIdx = 0; libIdx < sizeof(libs) / sizeof(libs[0]); ++libIdx) { + const char *lib = libs[libIdx]; + ods("Searching for dlsym() in \"%s\"...", lib); +#ifdef RTLD_SELF + struct link_map *lm = NULL; + if (dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &lm) == -1) { + ods("Unable to acquire link_map: %s", dlerror()); + return -1; + } - struct link_map *lm = dl; -#elif defined(__FreeBSD__) - struct link_map *lm = NULL; - if (dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &lm) == -1) { - ods("Unable to acquire link_map: %s", dlerror()); - return -1; - } + while (lm) { + if (strcmp(lm->l_name, lib) == 0) { + break; + } - while (lm) { - if (strcmp(lm->l_name, "/libexec/ld-elf.so.1") == 0) { - break; + lm = lm->l_next; } - lm = lm->l_next; - } + if (!lm) { + ods("Failed to find \"%s\"!", lib); + continue; + } +#else + void *dl = dlopen(lib, RTLD_LAZY); + if (!dl) { + ods("dlopen() failed: %s", dlerror()); + continue; + } - if (!lm) { - ods("Failed to find ld-elf.so.1!"); - return -1; - } + const struct link_map *lm = dl; #endif - bool hashTableGNU = false; - uintptr_t hashTable = 0; - const char *strTable = NULL; - Elf_Sym *symTable = NULL; + bool hashTableGNU = false; + uintptr_t hashTable = 0; + const char *strTable = NULL; + Elf_Sym *symTable = NULL; #if defined(__GLIBC__) - const uintptr_t base = 0; + const uintptr_t base = 0; #else - const uintptr_t base = (uintptr_t) lm->l_addr; + const uintptr_t base = (uintptr_t) lm->l_addr; #endif - for (const Elf_Dyn *dyn = lm->l_ld; dyn; ++dyn) { - switch (dyn->d_tag) { - case DT_GNU_HASH: - if (!hashTable) { - hashTable = base + dyn->d_un.d_ptr; - hashTableGNU = true; - } - break; - case DT_HASH: - if (!hashTable) { - hashTable = base + dyn->d_un.d_ptr; - } - break; - case DT_STRTAB: - strTable = (const char *) (base + dyn->d_un.d_ptr); - break; - case DT_SYMTAB: - symTable = (Elf_Sym *) (base + dyn->d_un.d_ptr); + for (const Elf_Dyn *dyn = lm->l_ld; dyn; ++dyn) { + switch (dyn->d_tag) { + case DT_GNU_HASH: + if (!hashTable) { + hashTable = base + dyn->d_un.d_ptr; + hashTableGNU = true; + } + break; + case DT_HASH: + if (!hashTable) { + hashTable = base + dyn->d_un.d_ptr; + } + break; + case DT_STRTAB: + strTable = (const char *) (base + dyn->d_un.d_ptr); + break; + case DT_SYMTAB: + symTable = (Elf_Sym *) (base + dyn->d_un.d_ptr); + break; + } + + if (hashTable && strTable && symTable) { break; + } } - if (hashTable && strTable && symTable) { - break; - } - } + ods("hashTable: 0x%" PRIxPTR ", strTable: %p, symTable: %p", hashTable, strTable, symTable); - ods("hashTable: 0x%" PRIxPTR ", strTable: %p, symTable: %p", hashTable, strTable, symTable); + if (!hashTable || !strTable || !symTable) { + continue; + } - if (!hashTable || !strTable || !symTable) { - return -1; - } + if (!hashTableGNU) { + ods("Using DT_HASH"); + // Hash table pseudo-struct: + // uint32_t nBucket; + // uint32_t nChain; + // uint32_t bucket[nBucket]; + // uint32_t chain[nChain]; + const uint32_t nChain = ((uint32_t *) hashTable)[1]; + + for (uint32_t i = 0; i < nChain; ++i) { + if (ELF_ST_TYPE(symTable[i].st_info) != STT_FUNC) { + continue; + } - if (!hashTableGNU) { - ods("Using DT_HASH"); - // Hash table pseudo-struct: - // uint32_t nBucket; - // uint32_t nChain; - // uint32_t bucket[nBucket]; - // uint32_t chain[nChain]; - const uint32_t nChain = ((uint32_t *) hashTable)[1]; - - for (uint32_t i = 0; i < nChain; ++i) { - if (ELF_ST_TYPE(symTable[i].st_info) != STT_FUNC) { - continue; + if (strcmp(strTable + symTable[i].st_name, "dlsym") == 0) { + odlsym = (void *) lm->l_addr + symTable[i].st_value; + break; + } } + } else { + ods("Using DT_GNU_HASH"); + // Hash table pseudo-struct: + // uint32_t nBucket; + // uint32_t symOffset; + // uint32_t nBloom; + // uint32_t bloomShift; + // uintptr_t blooms[nBloom]; + // uint32_t buckets[nBucket]; + // uint32_t chain[]; + uint32_t *hashStruct = (uint32_t *) hashTable; + + const uint32_t nBucket = hashStruct[0]; + const uint32_t symOffset = hashStruct[1]; + const uint32_t nBloom = hashStruct[2]; + const uintptr_t *bloom = (uintptr_t *) &hashStruct[4]; + const uint32_t *buckets = (uint32_t *) &bloom[nBloom]; + const uint32_t *chain = &buckets[nBucket]; + + for (uint32_t i = 0; i < nBucket; ++i) { + uint32_t symIndex = buckets[i]; + if (symIndex < symOffset) { + continue; + } - if (strcmp(strTable + symTable[i].st_name, "dlsym") == 0) { - odlsym = (void *) lm->l_addr + symTable[i].st_value; - break; + do { + if (strcmp(strTable + symTable[symIndex].st_name, "dlsym") == 0) { + odlsym = (void *) lm->l_addr + symTable[symIndex].st_value; + } + } while (!odlsym && !(chain[symIndex++ - symOffset] & 1)); } } - } else { - ods("Using DT_GNU_HASH"); - // Hash table pseudo-struct: - // uint32_t nBucket; - // uint32_t symOffset; - // uint32_t nBloom; - // uint32_t bloomShift; - // uintptr_t blooms[nBloom]; - // uint32_t buckets[nBucket]; - // uint32_t chain[]; - uint32_t *hashStruct = (uint32_t *) hashTable; - - const uint32_t nBucket = hashStruct[0]; - const uint32_t symOffset = hashStruct[1]; - const uint32_t nBloom = hashStruct[2]; - const uintptr_t *bloom = (uintptr_t *) &hashStruct[4]; - const uint32_t *buckets = (uint32_t *) &bloom[nBloom]; - const uint32_t *chain = &buckets[nBucket]; - - for (uint32_t i = 0; i < nBucket; ++i) { - uint32_t symIndex = buckets[i]; - if (symIndex < symOffset) { - continue; - } - - do { - if (strcmp(strTable + symTable[symIndex].st_name, "dlsym") == 0) { - odlsym = (void *) lm->l_addr + symTable[symIndex].st_value; - } - } while (!odlsym && !(chain[symIndex++ - symOffset] & 1)); - } } if (!odlsym) {