diff options
author | Magnus Malmborn <magnus.malmborn@sonymobile.com> | 2012-09-12 13:00:55 +0200 |
---|---|---|
committer | Giulio Cervera <giulio.cervera@cyanogenmod.org> | 2013-03-19 22:08:28 +0100 |
commit | ed26d701c4955bc3a4720bc2c7d9b6654a6956f1 (patch) | |
tree | 54515a5d6b01bc5bb48d50dda6a1071a7c9b3ff5 | |
parent | c32672ec97d77cb935f82e663d967dd4521b11d1 (diff) | |
download | bionic-cm-10.1-M3.zip bionic-cm-10.1-M3.tar.gz bionic-cm-10.1-M3.tar.bz2 |
Dynamically allocate soinfo-structs in linkercm-10.1.0-RC2cm-10.1.0-RC1cm-10.1-M3
Request memory from the system when needed instead of having a fixed
array for soinfo structs. Note that malloc() et al can't be used in
linker, so use mmap() instead.
Change-Id: Ia77e6a835db0f4a0bfeccc4a6610b857459fd437
-rw-r--r-- | linker/linker.cpp | 96 |
1 files changed, 60 insertions, 36 deletions
diff --git a/linker/linker.cpp b/linker/linker.cpp index 2362099..c6a6df2 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -51,7 +51,6 @@ #include "linker_phdr.h" #define ALLOW_SYMBOLS_FROM_MAIN 1 -#define SO_MAX 128 /* Assume average path length of 64 and max 8 paths */ #define LDPATH_BUFSIZE 512 @@ -76,16 +75,22 @@ * 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 int soinfo_link_image(soinfo *si); -static int socount = 0; -static soinfo sopool[SO_MAX]; -static soinfo *freelist = NULL; +// 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 soinfo *solist = &libdl_info; static soinfo *sonext = &libdl_info; #if ALLOW_SYMBOLS_FROM_MAIN @@ -272,38 +277,57 @@ extern "C" 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; + } + + // 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; +} - /* 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; - } +static soinfo* soinfo_alloc(const char* name) { + if (strlen(name) >= SOINFO_NAME_LEN) { + DL_ERR("library name \"%s\" too long", name); + return NULL; + } - soinfo* si = freelist; - freelist = freelist->next; + if (!ensure_free_list_non_empty()) { + DL_ERR("out of memory when loading \"%s\"", 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; + // Take the head element off the free list. + soinfo* si = gSoInfoFreeList; + gSoInfoFreeList = gSoInfoFreeList->next; - TRACE("%5d name %s: allocated soinfo @ %p\n", pid, name, si); - return si; + // 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) @@ -332,8 +356,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 |