diff options
Diffstat (limited to 'linker')
| -rw-r--r-- | linker/debugger.cpp | 1 | ||||
| -rw-r--r-- | linker/dlfcn.cpp | 9 | ||||
| -rwxr-xr-x | linker/linker.cpp | 622 | ||||
| -rw-r--r-- | linker/linker.h | 184 |
4 files changed, 413 insertions, 403 deletions
diff --git a/linker/debugger.cpp b/linker/debugger.cpp index bba89b8..28c939a 100644 --- a/linker/debugger.cpp +++ b/linker/debugger.cpp @@ -32,7 +32,6 @@ #include <stdio.h> #include <stdlib.h> #include <unistd.h> -#include <stdbool.h> #include <signal.h> #include <sys/prctl.h> #include <errno.h> diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp index c87d29a..24006e2 100644 --- a/linker/dlfcn.cpp +++ b/linker/dlfcn.cpp @@ -57,13 +57,11 @@ const char* dlerror() { void* dlopen(const char* filename, int flag) { ScopedPthreadMutexLocker locker(&gDlMutex); - soinfo* result = find_library(filename); + soinfo* result = do_dlopen(filename); if (result == NULL) { __bionic_format_dlerror("dlopen failed", linker_get_error()); return NULL; } - soinfo_call_constructors(result); - result->refcount++; return result; } @@ -139,7 +137,7 @@ int dladdr(const void* addr, Dl_info* info) { int dlclose(void* handle) { ScopedPthreadMutexLocker locker(&gDlMutex); - return soinfo_unload((soinfo*) handle); + return do_dlclose(reinterpret_cast<soinfo*>(handle)); } #if defined(ANDROID_ARM_LINKER) @@ -236,7 +234,8 @@ soinfo libdl_info = { refcount: 0, { l_addr: 0, l_name: 0, l_ld: 0, l_next: 0, l_prev: 0, }, - constructors_called: 0, load_bias: 0, + constructors_called: false, + load_bias: 0, has_text_relocations: false, has_DT_SYMBOLIC: true, }; diff --git a/linker/linker.cpp b/linker/linker.cpp index 8bb989a..e064ccf 100755 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -31,7 +31,6 @@ #include <fcntl.h> #include <linux/auxvec.h> #include <pthread.h> -#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -51,8 +50,6 @@ #include "linker_format.h" #include "linker_phdr.h" -#define SO_MAX 128 - /* Assume average path length of 64 and max 8 paths */ #define LDPATH_BUFSIZE 512 #define LDPATH_MAX 8 @@ -74,27 +71,36 @@ * - cleaner error reporting * - after linking, set as much stuff as possible to READONLY * and NOEXEC - * - linker hardcodes PAGE_SIZE and PAGE_MASK because the kernel - * headers provide versions that are negative... - * - allocate space for soinfo structs dynamically instead of - * having a hard limit (SO_MAX) */ +static bool soinfo_link_image(soinfo* si); -static int soinfo_link_image(soinfo *si); +// We can't use malloc(3) in the dynamic linker. We use a linked list of anonymous +// maps, each a single page in size. The pages are broken up into as many struct soinfo +// objects as will fit, and they're all threaded together on a free list. +#define SOINFO_PER_POOL ((PAGE_SIZE - sizeof(soinfo_pool_t*)) / sizeof(soinfo)) +struct soinfo_pool_t { + soinfo_pool_t* next; + soinfo info[SOINFO_PER_POOL]; +}; +static struct soinfo_pool_t* gSoInfoPools = NULL; +static soinfo* gSoInfoFreeList = NULL; -static int socount = 0; -static soinfo sopool[SO_MAX]; -static soinfo *freelist = NULL; static soinfo *solist = &libdl_info; static soinfo *sonext = &libdl_info; static soinfo *somain; /* main process, always the one after libdl_info */ -static char ldpaths_buf[LDPATH_BUFSIZE]; -static const char *ldpaths[LDPATH_MAX + 1]; +static const char* const gSoPaths[] = { + "/vendor/lib", + "/system/lib", + NULL +}; + +static char gLdPathsBuffer[LDPATH_BUFSIZE]; +static const char* gLdPaths[LDPATH_MAX + 1]; -static char ldpreloads_buf[LDPRELOAD_BUFSIZE]; -static const char *ldpreload_names[LDPRELOAD_MAX + 1]; +static char gLdPreloadsBuffer[LDPRELOAD_BUFSIZE]; +static const char* gLdPreloadNames[LDPRELOAD_MAX + 1]; static soinfo *preloads[LDPRELOAD_MAX + 1]; @@ -257,38 +263,65 @@ void notify_gdb_of_libraries() { rtld_db_dlactivity(); } -static soinfo *soinfo_alloc(const char *name) -{ - if (strlen(name) >= SOINFO_NAME_LEN) { - DL_ERR("library name \"%s\" too long", name); - return NULL; - } +static bool ensure_free_list_non_empty() { + if (gSoInfoFreeList != NULL) { + return true; + } - /* The freelist is populated when we call soinfo_free(), which in turn is - done only by dlclose(), which is not likely to be used. - */ - if (!freelist) { - if (socount == SO_MAX) { - DL_ERR("too many libraries when loading \"%s\"", name); - return NULL; - } - freelist = sopool + socount++; - freelist->next = NULL; + // Allocate a new pool. + soinfo_pool_t* pool = reinterpret_cast<soinfo_pool_t*>(mmap(NULL, sizeof(*pool), + PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, 0, 0)); + if (pool == MAP_FAILED) { + return false; + } + + // Add the pool to our list of pools. + pool->next = gSoInfoPools; + gSoInfoPools = pool; + + // Chain the entries in the new pool onto the free list. + gSoInfoFreeList = &pool->info[0]; + soinfo* next = NULL; + for (int i = SOINFO_PER_POOL - 1; i >= 0; --i) { + pool->info[i].next = next; + next = &pool->info[i]; + } + + return true; +} + +static void set_soinfo_pool_protection(int protection) { + for (soinfo_pool_t* p = gSoInfoPools; p != NULL; p = p->next) { + if (mprotect(p, sizeof(*p), protection) == -1) { + abort(); // Can't happen. } + } +} - soinfo* si = freelist; - freelist = freelist->next; +static soinfo* soinfo_alloc(const char* name) { + if (strlen(name) >= SOINFO_NAME_LEN) { + DL_ERR("library name \"%s\" too long", name); + return NULL; + } - /* Make sure we get a clean block of soinfo */ - memset(si, 0, sizeof(soinfo)); - strlcpy((char*) si->name, name, sizeof(si->name)); - sonext->next = si; - si->next = NULL; - si->refcount = 0; - sonext = si; + if (!ensure_free_list_non_empty()) { + DL_ERR("out of memory when loading \"%s\"", name); + return NULL; + } - TRACE("%5d name %s: allocated soinfo @ %p\n", pid, name, si); - return si; + // Take the head element off the free list. + soinfo* si = gSoInfoFreeList; + gSoInfoFreeList = gSoInfoFreeList->next; + + // Initialize the new element. + memset(si, 0, sizeof(soinfo)); + strlcpy(si->name, name, sizeof(si->name)); + sonext->next = si; + sonext = si; + + TRACE("%5d name %s: allocated soinfo @ %p\n", pid, name, si); + return si; } static void soinfo_free(soinfo* si) @@ -317,8 +350,8 @@ static void soinfo_free(soinfo* si) */ prev->next = si->next; if (si == sonext) sonext = prev; - si->next = freelist; - freelist = si; + si->next = gSoInfoFreeList; + gSoInfoFreeList = si; } #ifdef ANDROID_ARM_LINKER @@ -610,56 +643,40 @@ static void dump(soinfo *si) } #endif -static const char * const sopaths[] = { - "/vendor/lib", - "/system/lib", - 0 -}; - -static int _open_lib(const char* name) { - // TODO: why not just call open? - struct stat sb; - if (stat(name, &sb) == -1 || !S_ISREG(sb.st_mode)) { - return -1; +static int open_library_on_path(const char* name, const char* const paths[]) { + char buf[512]; + for (size_t i = 0; paths[i] != NULL; ++i) { + int n = format_buffer(buf, sizeof(buf), "%s/%s", paths[i], name); + if (n < 0 || n >= static_cast<int>(sizeof(buf))) { + WARN("Ignoring very long library path: %s/%s\n", paths[i], name); + continue; + } + int fd = TEMP_FAILURE_RETRY(open(buf, O_RDONLY | O_CLOEXEC)); + if (fd != -1) { + return fd; } - return TEMP_FAILURE_RETRY(open(name, O_RDONLY)); + } + return -1; } -static int open_library(const char *name) -{ - int fd; - char buf[512]; - const char * const*path; - int n; - - TRACE("[ %5d opening %s ]\n", pid, name); +static int open_library(const char* name) { + TRACE("[ %5d opening %s ]\n", pid, name); - if(name == 0) return -1; - if(strlen(name) > 256) return -1; - - if ((name[0] == '/') && ((fd = _open_lib(name)) >= 0)) - return fd; - - for (path = ldpaths; *path; path++) { - n = format_buffer(buf, sizeof(buf), "%s/%s", *path, name); - if (n < 0 || n >= (int)sizeof(buf)) { - WARN("Ignoring very long library path: %s/%s\n", *path, name); - continue; - } - if ((fd = _open_lib(buf)) >= 0) - return fd; - } - for (path = sopaths; *path; path++) { - n = format_buffer(buf, sizeof(buf), "%s/%s", *path, name); - if (n < 0 || n >= (int)sizeof(buf)) { - WARN("Ignoring very long library path: %s/%s\n", *path, name); - continue; - } - if ((fd = _open_lib(buf)) >= 0) - return fd; + // If the name contains a slash, we should attempt to open it directly and not search the paths. + if (strchr(name, '/') != NULL) { + int fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_CLOEXEC)); + if (fd != -1) { + return fd; } + // ...but nvidia binary blobs (at least) rely on this behavior, so fall through for now. + } - return -1; + // Otherwise we try LD_LIBRARY_PATH first, and fall back to the built-in well known paths. + int fd = open_library_on_path(name, gLdPaths); + if (fd == -1) { + fd = open_library_on_path(name, gSoPaths); + } + return fd; } // Returns 'true' if the library is prelinked or on failure so we error out @@ -757,8 +774,7 @@ struct phdr_ptr { Elf32_Addr phdr_size; }; -static soinfo* load_library(const char* name) -{ +static soinfo* load_library(const char* name) { // Open the file. scoped_fd fd; fd.fd = open_library(name); @@ -857,20 +873,18 @@ static soinfo* load_library(const char* name) return si.release(); } -static soinfo * -init_library(soinfo *si) -{ - /* At this point we know that whatever is loaded @ base is a valid ELF - * shared library whose segments are properly mapped in. */ - TRACE("[ %5d init_library base=0x%08x sz=0x%08x name='%s') ]\n", - pid, si->base, si->size, si->name); +static soinfo* init_library(soinfo* si) { + // At this point we know that whatever is loaded @ base is a valid ELF + // shared library whose segments are properly mapped in. + TRACE("[ %5d init_library base=0x%08x sz=0x%08x name='%s') ]\n", + pid, si->base, si->size, si->name); - if(soinfo_link_image(si)) { - munmap((void *)si->base, si->size); - return NULL; - } + if (!soinfo_link_image(si)) { + munmap((void *)si->base, si->size); + return NULL; + } - return si; + return si; } static soinfo *find_loaded_library(const char *name) @@ -892,63 +906,86 @@ static soinfo *find_loaded_library(const char *name) return NULL; } -soinfo *find_library(const char *name) -{ - soinfo *si; +static soinfo* find_library_internal(const char* name) { + if (name == NULL) { + return somain; + } + + soinfo* si = find_loaded_library(name); + if (si != NULL) { + if (si->flags & FLAG_ERROR) { + DL_ERR("\"%s\" failed to load previously", name); + return NULL; + } + if (si->flags & FLAG_LINKED) { + return si; + } + DL_ERR("OOPS: recursive link to \"%s\"", si->name); + return NULL; + } - if (name == NULL) - return somain; + TRACE("[ %5d '%s' has not been loaded yet. Locating...]\n", pid, name); + si = load_library(name); + if (si != NULL) { + si = init_library(si); + } - si = find_loaded_library(name); - if (si != NULL) { - if(si->flags & FLAG_ERROR) { - DL_ERR("\"%s\" failed to load previously", name); - return NULL; + return si; +} + +static soinfo* find_library(const char* name) { + soinfo* si = find_library_internal(name); + if (si != NULL) { + si->refcount++; + } + return si; +} + +static int soinfo_unload(soinfo* si) { + if (si->refcount == 1) { + TRACE("%5d unloading '%s'\n", pid, si->name); + si->CallDestructors(); + + for (unsigned* d = si->dynamic; *d; d += 2) { + if (d[0] == DT_NEEDED) { + soinfo* lsi = find_loaded_library(si->strtab + d[1]); + if (lsi != NULL) { + TRACE("%5d %s needs to unload %s\n", pid, si->name, lsi->name); + soinfo_unload(lsi); + } else { + // TODO: should we return -1 in this case? + DL_ERR("\"%s\": could not unload dependent library", si->name); } - if(si->flags & FLAG_LINKED) return si; - DL_ERR("OOPS: recursive link to \"%s\"", si->name); - return NULL; + } } - TRACE("[ %5d '%s' has not been loaded yet. Locating...]\n", pid, name); - si = load_library(name); - if(si == NULL) - return NULL; - return init_library(si); + munmap(reinterpret_cast<void*>(si->base), si->size); + notify_gdb_of_unload(si); + soinfo_free(si); + si->refcount = 0; + } else { + si->refcount--; + PRINT("%5d not unloading '%s', decrementing refcount to %d\n", + pid, si->name, si->refcount); + } + return 0; } -static void call_destructors(soinfo *si); - -int soinfo_unload(soinfo* si) { - if (si->refcount == 1) { - TRACE("%5d unloading '%s'\n", pid, si->name); - call_destructors(si); - - for (unsigned* d = si->dynamic; *d; d += 2) { - if(d[0] == DT_NEEDED){ - soinfo *lsi = find_loaded_library(si->strtab + d[1]); - if (lsi) { - TRACE("%5d %s needs to unload %s\n", pid, - si->name, lsi->name); - soinfo_unload(lsi); - } else { - // TODO: should we return -1 in this case? - DL_ERR("\"%s\": could not unload dependent library", - si->name); - } - } - } +soinfo* do_dlopen(const char* name) { + set_soinfo_pool_protection(PROT_READ | PROT_WRITE); + soinfo* si = find_library(name); + if (si != NULL) { + si->CallConstructors(); + } + set_soinfo_pool_protection(PROT_READ); + return si; +} - munmap((char *)si->base, si->size); - notify_gdb_of_unload(si); - soinfo_free(si); - si->refcount = 0; - } else { - si->refcount--; - PRINT("%5d not unloading '%s', decrementing refcount to %d\n", - pid, si->name, si->refcount); - } - return 0; +int do_dlclose(soinfo* si) { + set_soinfo_pool_protection(PROT_READ | PROT_WRITE); + int result = soinfo_unload(si); + set_soinfo_pool_protection(PROT_READ); + return result; } /* TODO: don't use unsigned for addrs below. It works, but is not @@ -1294,106 +1331,89 @@ static int mips_relocate_got(soinfo* si, soinfo* needed[]) { * * DT_FINI_ARRAY must be parsed in reverse order. */ +void soinfo::CallArray(const char* array_name UNUSED, unsigned* array, int count, bool reverse) { + if (array == NULL) { + return; + } + + int step = 1; + if (reverse) { + array += (count-1); + step = -1; + } + + TRACE("[ %5d Calling %s @ %p [%d] for '%s' ]\n", pid, array_name, array, count, name); + + for (int n = count; n > 0; n--) { + TRACE("[ %5d Looking at %s[%d] *%p == 0x%08x ]\n", pid, array_name, n, array, *array); + void (*func)() = (void (*)()) *array; + array += step; + if (((int) func == 0) || ((int) func == -1)) { + continue; + } + TRACE("[ %5d Calling func @ %p ]\n", pid, func); + func(); + } + + TRACE("[ %5d Done calling %s for '%s' ]\n", pid, array_name, name); +} -static void call_array(unsigned *ctor, int count, int reverse) -{ - int n, inc = 1; - - if (reverse) { - ctor += (count-1); - inc = -1; - } +void soinfo::CallFunction(const char* function_name UNUSED, void (*function)()) { + if (function == NULL) { + return; + } - for(n = count; n > 0; n--) { - TRACE("[ %5d Looking at %s *0x%08x == 0x%08x ]\n", pid, - reverse ? "dtor" : "ctor", - (unsigned)ctor, (unsigned)*ctor); - void (*func)() = (void (*)()) *ctor; - ctor += inc; - if(((int) func == 0) || ((int) func == -1)) continue; - TRACE("[ %5d Calling func @ 0x%08x ]\n", pid, (unsigned)func); - func(); - } + TRACE("[ %5d Calling %s @ %p for '%s' ]\n", pid, function_name, function, name); + function(); + TRACE("[ %5d Done calling %s for '%s' ]\n", pid, function_name, name); } -static void soinfo_call_preinit_constructors(soinfo *si) -{ - TRACE("[ %5d Calling preinit_array @ 0x%08x [%d] for '%s' ]\n", - pid, (unsigned)si->preinit_array, si->preinit_array_count, - si->name); - call_array(si->preinit_array, si->preinit_array_count, 0); - TRACE("[ %5d Done calling preinit_array for '%s' ]\n", pid, si->name); +void soinfo::CallPreInitConstructors() { + CallArray("DT_PREINIT_ARRAY", preinit_array, preinit_array_count, false); } -void soinfo_call_constructors(soinfo *si) -{ - if (si->constructors_called) - return; - - // Set this before actually calling the constructors, otherwise it doesn't - // protect against recursive constructor calls. One simple example of - // constructor recursion is the libc debug malloc, which is implemented in - // libc_malloc_debug_leak.so: - // 1. The program depends on libc, so libc's constructor is called here. - // 2. The libc constructor calls dlopen() to load libc_malloc_debug_leak.so. - // 3. dlopen() calls soinfo_call_constructors() with the newly created - // soinfo for libc_malloc_debug_leak.so. - // 4. The debug so depends on libc, so soinfo_call_constructors() is - // called again with the libc soinfo. If it doesn't trigger the early- - // out above, the libc constructor will be called again (recursively!). - si->constructors_called = 1; - - if (!(si->flags & FLAG_EXE) && si->preinit_array) { - DL_ERR("shared library \"%s\" has a preinit_array table @ 0x%08x. " - "This is INVALID.", si->name, (unsigned) si->preinit_array); - } - - if (si->dynamic) { - unsigned *d; - for(d = si->dynamic; *d; d += 2) { - if(d[0] == DT_NEEDED){ - soinfo* lsi = find_loaded_library(si->strtab + d[1]); - if (!lsi) { - DL_ERR("\"%s\": could not initialize dependent library", - si->name); - } else { - soinfo_call_constructors(lsi); - } - } +void soinfo::CallConstructors() { + if (constructors_called) { + return; + } + + // We set constructors_called before actually calling the constructors, otherwise it doesn't + // protect against recursive constructor calls. One simple example of constructor recursion + // is the libc debug malloc, which is implemented in libc_malloc_debug_leak.so: + // 1. The program depends on libc, so libc's constructor is called here. + // 2. The libc constructor calls dlopen() to load libc_malloc_debug_leak.so. + // 3. dlopen() calls the constructors on the newly created + // soinfo for libc_malloc_debug_leak.so. + // 4. The debug .so depends on libc, so CallConstructors is + // called again with the libc soinfo. If it doesn't trigger the early- + // out above, the libc constructor will be called again (recursively!). + constructors_called = true; + + if (!(flags & FLAG_EXE) && preinit_array) { + DL_ERR("shared library \"%s\" has a preinit_array table @ %p", name, preinit_array); + return; + } + + if (dynamic) { + for (unsigned* d = dynamic; *d; d += 2) { + if (d[0] == DT_NEEDED) { + soinfo* lsi = find_loaded_library(strtab + d[1]); + if (lsi == NULL) { + DL_ERR("\"%s\": could not initialize dependent library", name); + } else { + lsi->CallConstructors(); } + } } + } - if (si->init_func) { - TRACE("[ %5d Calling init_func @ 0x%08x for '%s' ]\n", pid, - (unsigned)si->init_func, si->name); - si->init_func(); - TRACE("[ %5d Done calling init_func for '%s' ]\n", pid, si->name); - } - - if (si->init_array) { - TRACE("[ %5d Calling init_array @ 0x%08x [%d] for '%s' ]\n", pid, - (unsigned)si->init_array, si->init_array_count, si->name); - call_array(si->init_array, si->init_array_count, 0); - TRACE("[ %5d Done calling init_array for '%s' ]\n", pid, si->name); - } - + CallFunction("DT_INIT", init_func); + CallArray("DT_INIT_ARRAY", init_array, init_array_count, false); } -static void call_destructors(soinfo *si) -{ - if (si->fini_array) { - TRACE("[ %5d Calling fini_array @ 0x%08x [%d] for '%s' ]\n", pid, - (unsigned)si->fini_array, si->fini_array_count, si->name); - call_array(si->fini_array, si->fini_array_count, 1); - TRACE("[ %5d Done calling fini_array for '%s' ]\n", pid, si->name); - } - - if (si->fini_func) { - TRACE("[ %5d Calling fini_func @ 0x%08x for '%s' ]\n", pid, - (unsigned)si->fini_func, si->name); - si->fini_func(); - TRACE("[ %5d Done calling fini_func for '%s' ]\n", pid, si->name); - } +void soinfo::CallDestructors() { + CallArray("DT_FINI_ARRAY", fini_array, fini_array_count, true); + CallFunction("DT_FINI", fini_func); } /* Force any of the closed stdin, stdout and stderr to be associated with @@ -1457,16 +1477,15 @@ static int nullify_closed_stdio() { return return_value; } -static int soinfo_link_image(soinfo *si) -{ - unsigned *d; +static bool soinfo_link_image(soinfo* si) { + si->flags |= FLAG_ERROR; + /* "base" might wrap around UINT32_MAX. */ Elf32_Addr base = si->load_bias; const Elf32_Phdr *phdr = si->phdr; int phnum = si->phnum; int relocating_linker = (si->flags & FLAG_LINKER) != 0; soinfo **needed, **pneeded; - size_t dynamic_count; /* We can't debug anything until the linker is relocated */ if (!relocating_linker) { @@ -1476,13 +1495,14 @@ static int soinfo_link_image(soinfo *si) } /* Extract dynamic section */ + size_t dynamic_count; phdr_table_get_dynamic_section(phdr, phnum, base, &si->dynamic, &dynamic_count); if (si->dynamic == NULL) { if (!relocating_linker) { - DL_ERR("missing PT_DYNAMIC?!"); + DL_ERR("missing PT_DYNAMIC in \"%s\"", si->name); } - goto fail; + return false; } else { if (!relocating_linker) { DEBUG("%5d dynamic = %p\n", pid, si->dynamic); @@ -1495,7 +1515,7 @@ static int soinfo_link_image(soinfo *si) #endif /* extract useful information from dynamic section */ - for(d = si->dynamic; *d; d++){ + for (unsigned* d = si->dynamic; *d; ++d) { DEBUG("%5d d = %p, d[0] = 0x%08x d[1] = 0x%08x\n", pid, d, d[0], d[1]); switch(*d++){ case DT_HASH: @@ -1512,8 +1532,8 @@ static int soinfo_link_image(soinfo *si) break; case DT_PLTREL: if(*d != DT_REL) { - DL_ERR("DT_RELA not supported"); - goto fail; + DL_ERR("unsupported DT_RELA in \"%s\"", si->name); + return false; } break; case DT_JMPREL: @@ -1539,8 +1559,8 @@ static int soinfo_link_image(soinfo *si) #endif break; case DT_RELA: - DL_ERR("DT_RELA not supported"); - goto fail; + DL_ERR("unsupported DT_RELA in \"%s\"", si->name); + return false; case DT_INIT: si->init_func = (void (*)(void))(base + *d); DEBUG("%5d %s constructors (init func) found at %p\n", @@ -1641,24 +1661,31 @@ static int soinfo_link_image(soinfo *si) DEBUG("%5d si->base = 0x%08x, si->strtab = %p, si->symtab = %p\n", pid, si->base, si->strtab, si->symtab); - if((si->strtab == 0) || (si->symtab == 0)) { - DL_ERR("missing essential tables"); - goto fail; + // Sanity checks. + if (si->nbucket == 0) { + DL_ERR("empty/missing DT_HASH in \"%s\" (built with --hash-style=gnu?)", si->name); + return false; + } + if (si->strtab == 0) { + DL_ERR("empty/missing DT_STRTAB in \"%s\"", si->name); + return false; + } + if (si->symtab == 0) { + DL_ERR("empty/missing DT_SYMTAB in \"%s\"", si->name); + return false; } /* if this is the main executable, then load all of the preloads now */ - if(si->flags & FLAG_EXE) { - int i; + if (si->flags & FLAG_EXE) { memset(preloads, 0, sizeof(preloads)); - for(i = 0; ldpreload_names[i] != NULL; i++) { - soinfo *lsi = find_library(ldpreload_names[i]); - if(lsi == 0) { + for (size_t i = 0; gLdPreloadNames[i] != NULL; i++) { + soinfo* lsi = find_library(gLdPreloadNames[i]); + if (lsi == NULL) { strlcpy(tmp_err_buf, linker_get_error(), sizeof(tmp_err_buf)); DL_ERR("could not load library \"%s\" needed by \"%s\"; caused by %s", - ldpreload_names[i], si->name, tmp_err_buf); - goto fail; + gLdPreloadNames[i], si->name, tmp_err_buf); + return false; } - lsi->refcount++; preloads[i] = lsi; } } @@ -1666,18 +1693,17 @@ static int soinfo_link_image(soinfo *si) /* dynamic_count is an upper bound for the number of needed libs */ pneeded = needed = (soinfo**) alloca((1 + dynamic_count) * sizeof(soinfo*)); - for(d = si->dynamic; *d; d += 2) { - if(d[0] == DT_NEEDED){ + for (unsigned* d = si->dynamic; *d; d += 2) { + if (d[0] == DT_NEEDED) { DEBUG("%5d %s needs %s\n", pid, si->name, si->strtab + d[1]); - soinfo *lsi = find_library(si->strtab + d[1]); - if(lsi == 0) { + soinfo* lsi = find_library(si->strtab + d[1]); + if (lsi == NULL) { strlcpy(tmp_err_buf, linker_get_error(), sizeof(tmp_err_buf)); DL_ERR("could not load library \"%s\" needed by \"%s\"; caused by %s", si->strtab + d[1], si->name, tmp_err_buf); - goto fail; + return false; } *pneeded++ = lsi; - lsi->refcount++; } } *pneeded = NULL; @@ -1691,24 +1717,26 @@ static int soinfo_link_image(soinfo *si) if (phdr_table_unprotect_segments(si->phdr, si->phnum, si->load_bias) < 0) { DL_ERR("can't unprotect loadable segments for \"%s\": %s", si->name, strerror(errno)); - goto fail; + return false; } } - if(si->plt_rel) { + if (si->plt_rel) { DEBUG("[ %5d relocating %s plt ]\n", pid, si->name ); - if(soinfo_relocate(si, si->plt_rel, si->plt_rel_count, needed)) - goto fail; + if(soinfo_relocate(si, si->plt_rel, si->plt_rel_count, needed)) { + return false; + } } - if(si->rel) { + if (si->rel) { DEBUG("[ %5d relocating %s ]\n", pid, si->name ); - if(soinfo_relocate(si, si->rel, si->rel_count, needed)) - goto fail; + if(soinfo_relocate(si, si->rel, si->rel_count, needed)) { + return false; + } } #ifdef ANDROID_MIPS_LINKER - if(mips_relocate_got(si, needed)) { - goto fail; + if (mips_relocate_got(si, needed)) { + return false; } #endif @@ -1721,7 +1749,7 @@ static int soinfo_link_image(soinfo *si) if (phdr_table_protect_segments(si->phdr, si->phnum, si->load_bias) < 0) { DL_ERR("can't protect segments for \"%s\": %s", si->name, strerror(errno)); - goto fail; + return false; } } @@ -1729,25 +1757,17 @@ static int soinfo_link_image(soinfo *si) if (phdr_table_protect_gnu_relro(si->phdr, si->phnum, si->load_bias) < 0) { DL_ERR("can't enable GNU RELRO protection for \"%s\": %s", si->name, strerror(errno)); - goto fail; + return false; } - /* If this is a SET?ID program, dup /dev/null to opened stdin, - stdout and stderr to close a security hole described in: - - ftp://ftp.freebsd.org/pub/FreeBSD/CERT/advisories/FreeBSD-SA-02:23.stdio.asc - - */ + // If this is a setuid/setgid program, close the security hole described in + // ftp://ftp.freebsd.org/pub/FreeBSD/CERT/advisories/FreeBSD-SA-02:23.stdio.asc if (get_AT_SECURE()) { nullify_closed_stdio(); } notify_gdb_of_load(si); - return 0; - -fail: - ERROR("failed to link %s\n", si->name); - si->flags |= FLAG_ERROR; - return -1; + si->flags &= ~FLAG_ERROR; + return true; } static void parse_path(const char* path, const char* delimiters, @@ -1777,14 +1797,14 @@ static void parse_path(const char* path, const char* delimiters, } static void parse_LD_LIBRARY_PATH(const char* path) { - parse_path(path, ":", ldpaths, - ldpaths_buf, sizeof(ldpaths_buf), LDPATH_MAX); + parse_path(path, ":", gLdPaths, + gLdPathsBuffer, sizeof(gLdPathsBuffer), LDPATH_MAX); } static void parse_LD_PRELOAD(const char* path) { // We have historically supported ':' as well as ' ' in LD_PRELOAD. - parse_path(path, " :", ldpreload_names, - ldpreloads_buf, sizeof(ldpreloads_buf), LDPRELOAD_MAX); + parse_path(path, " :", gLdPreloadNames, + gLdPreloadsBuffer, sizeof(gLdPreloadsBuffer), LDPRELOAD_MAX); } /* @@ -1924,17 +1944,17 @@ static unsigned __linker_init_post_relocation(unsigned **elfdata, unsigned linke somain = si; - if(soinfo_link_image(si)) { + if (!soinfo_link_image(si)) { char errmsg[] = "CANNOT LINK EXECUTABLE\n"; write(2, __linker_dl_err_buf, strlen(__linker_dl_err_buf)); write(2, errmsg, sizeof(errmsg)); exit(EXIT_FAILURE); } - soinfo_call_preinit_constructors(si); + si->CallPreInitConstructors(); for (size_t i = 0; preloads[i] != NULL; ++i) { - soinfo_call_constructors(preloads[i]); + preloads[i]->CallConstructors(); } /*After the link_image, the si->base is initialized. @@ -1943,7 +1963,7 @@ static unsigned __linker_init_post_relocation(unsigned **elfdata, unsigned linke *for some arch like x86 could work correctly within so exe. */ map->l_addr = si->base; - soinfo_call_constructors(si); + si->CallConstructors(); #if TIMING gettimeofday(&t1,NULL); @@ -2063,7 +2083,7 @@ extern "C" unsigned __linker_init(unsigned **elfdata) { linker_so.phnum = elf_hdr->e_phnum; linker_so.flags |= FLAG_LINKER; - if (soinfo_link_image(&linker_so)) { + if (!soinfo_link_image(&linker_so)) { // It would be nice to print an error message, but if the linker // can't link itself, there's no guarantee that we'll be able to // call write() (because it involves a GOT reference). @@ -2077,6 +2097,8 @@ extern "C" unsigned __linker_init(unsigned **elfdata) { // the main part of the linker now. unsigned start_address = __linker_init_post_relocation(elfdata, linker_addr); + set_soinfo_pool_protection(PROT_READ); + // Return the address that the calling assembly stub should jump to. return start_address; } diff --git a/linker/linker.h b/linker/linker.h index cb33602..51869e7 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -36,54 +36,39 @@ #include <link.h> -#undef PAGE_MASK -#undef PAGE_SIZE -#define PAGE_SIZE 4096 -#define PAGE_MASK (PAGE_SIZE-1) +// Returns the address of the page containing address 'x'. +#define PAGE_START(x) ((x) & PAGE_MASK) -/* Convenience macros to make page address/offset computations more explicit */ +// Returns the offset of address 'x' in its page. +#define PAGE_OFFSET(x) ((x) & ~PAGE_MASK) -/* Returns the address of the page starting at address 'x' */ -#define PAGE_START(x) ((x) & ~PAGE_MASK) - -/* Returns the offset of address 'x' in its memory page, i.e. this is the - * same than 'x' - PAGE_START(x) */ -#define PAGE_OFFSET(x) ((x) & PAGE_MASK) - -/* Returns the address of the next page after address 'x', unless 'x' is - * itself at the start of a page. Equivalent to: - * - * (x == PAGE_START(x)) ? x : PAGE_START(x)+PAGE_SIZE - */ +// Returns the address of the next page after address 'x', unless 'x' is +// itself at the start of a page. #define PAGE_END(x) PAGE_START((x) + (PAGE_SIZE-1)) -void debugger_init(); - -/* magic shared structures that GDB knows about */ +// Magic shared structures that GDB knows about. -struct link_map -{ - uintptr_t l_addr; - char * l_name; - uintptr_t l_ld; - struct link_map * l_next; - struct link_map * l_prev; +struct link_map { + uintptr_t l_addr; + char * l_name; + uintptr_t l_ld; + struct link_map * l_next; + struct link_map * l_prev; }; // Values for r_debug->state enum { - RT_CONSISTENT, - RT_ADD, - RT_DELETE + RT_CONSISTENT, + RT_ADD, + RT_DELETE }; -struct r_debug -{ - int32_t r_version; - struct link_map * r_map; - void (*r_brk)(void); - int32_t r_state; - uintptr_t r_ldbase; +struct r_debug { + int32_t r_version; + struct link_map* r_map; + void (*r_brk)(void); + int32_t r_state; + uintptr_t r_ldbase; }; #define FLAG_LINKED 0x00000001 @@ -94,82 +79,86 @@ struct r_debug #define SOINFO_NAME_LEN 128 struct soinfo { - char name[SOINFO_NAME_LEN]; - const Elf32_Phdr *phdr; - int phnum; - unsigned entry; - unsigned base; - unsigned size; + char name[SOINFO_NAME_LEN]; + const Elf32_Phdr* phdr; + int phnum; + unsigned entry; + unsigned base; + unsigned size; - int unused; // DO NOT USE, maintained for compatibility. + int unused; // DO NOT USE, maintained for compatibility. - unsigned *dynamic; + unsigned* dynamic; - unsigned unused2; // DO NOT USE, maintained for compatibility - unsigned unused3; // DO NOT USE, maintained for compatibility + unsigned unused2; // DO NOT USE, maintained for compatibility + unsigned unused3; // DO NOT USE, maintained for compatibility - soinfo *next; - unsigned flags; + soinfo* next; + unsigned flags; - const char *strtab; - Elf32_Sym *symtab; + const char* strtab; + Elf32_Sym* symtab; - unsigned nbucket; - unsigned nchain; - unsigned *bucket; - unsigned *chain; + unsigned nbucket; + unsigned nchain; + unsigned* bucket; + unsigned* chain; - unsigned *plt_got; + unsigned* plt_got; - Elf32_Rel *plt_rel; - unsigned plt_rel_count; + Elf32_Rel* plt_rel; + unsigned plt_rel_count; - Elf32_Rel *rel; - unsigned rel_count; + Elf32_Rel* rel; + unsigned rel_count; - unsigned *preinit_array; - unsigned preinit_array_count; + unsigned* preinit_array; + unsigned preinit_array_count; - unsigned *init_array; - unsigned init_array_count; - unsigned *fini_array; - unsigned fini_array_count; + unsigned* init_array; + unsigned init_array_count; + unsigned* fini_array; + unsigned fini_array_count; - void (*init_func)(void); - void (*fini_func)(void); + void (*init_func)(); + void (*fini_func)(); #if defined(ANDROID_ARM_LINKER) - /* ARM EABI section used for stack unwinding. */ - unsigned *ARM_exidx; - unsigned ARM_exidx_count; + // ARM EABI section used for stack unwinding. + unsigned* ARM_exidx; + unsigned ARM_exidx_count; #elif defined(ANDROID_MIPS_LINKER) #if 0 - /* not yet */ - unsigned *mips_pltgot + // Not yet. + unsigned* mips_pltgot +#endif + unsigned mips_symtabno; + unsigned mips_local_gotno; + unsigned mips_gotsym; #endif - unsigned mips_symtabno; - unsigned mips_local_gotno; - unsigned mips_gotsym; -#endif /* ANDROID_*_LINKER */ - - unsigned refcount; - struct link_map linkmap; - int constructors_called; + unsigned refcount; + struct link_map linkmap; - /* When you read a virtual address from the ELF file, add this - * value to get the corresponding address in the process' address space */ - Elf32_Addr load_bias; + bool constructors_called; - bool has_text_relocations; - bool has_DT_SYMBOLIC; -}; + // When you read a virtual address from the ELF file, add this + // value to get the corresponding address in the process' address space. + Elf32_Addr load_bias; + bool has_text_relocations; + bool has_DT_SYMBOLIC; -extern soinfo libdl_info; + void CallConstructors(); + void CallDestructors(); + void CallPreInitConstructors(); + private: + void CallArray(const char* array_name, unsigned* array, int count, bool reverse); + void CallFunction(const char* function_name, void (*function)()); +}; -#include <asm/elf.h> +extern soinfo libdl_info; #if defined(ANDROID_ARM_LINKER) @@ -218,16 +207,17 @@ extern soinfo libdl_info; #define DT_PREINIT_ARRAYSZ 33 #endif -soinfo *find_library(const char *name); -Elf32_Sym *lookup(const char *name, soinfo **found, soinfo *start); -soinfo *find_containing_library(const void *addr); -const char *linker_get_error(void); +soinfo* do_dlopen(const char* name); +int do_dlclose(soinfo* si); + +Elf32_Sym* lookup(const char* name, soinfo** found, soinfo* start); +soinfo* find_containing_library(const void* addr); +const char* linker_get_error(); -int soinfo_unload(soinfo* si); -Elf32_Sym *soinfo_find_symbol(soinfo* si, const void *addr); -Elf32_Sym *soinfo_lookup(soinfo *si, const char *name); -void soinfo_call_constructors(soinfo *si); +Elf32_Sym* soinfo_find_symbol(soinfo* si, const void* addr); +Elf32_Sym* soinfo_lookup(soinfo* si, const char* name); +void debugger_init(); extern "C" void notify_gdb_of_libraries(); #endif |
