summaryrefslogtreecommitdiffstats
path: root/linker
diff options
context:
space:
mode:
Diffstat (limited to 'linker')
-rw-r--r--linker/debugger.cpp1
-rw-r--r--linker/dlfcn.cpp9
-rwxr-xr-xlinker/linker.cpp622
-rw-r--r--linker/linker.h184
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