From 4688279db5dcc4004941e7f133c4a1c3617d842c Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Fri, 3 Aug 2012 16:49:39 -0700 Subject: [PATCH] Clean up the linker a bit, remove prelinking support. Also make the errors more readable, since none of us seemed to know what they actually meant. The new style is still as verbose as the old, but that's probably necessary in the absence of chained exceptions in C. Here's what you'd see if you try to boot after removing libsurfaceflinger.so: 32267 32267 E AndroidRuntime: java.lang.UnsatisfiedLinkError: Cannot load library: (linker.c:1629, pid 32259) soinfo_link_image: could not load library "libsystem_server.so" needed by "libandroid_servers.so"; caused by (linker.c:1629, pid 32259) soinfo_link_image: could not load library "libsurfaceflinger.so" needed by "libsystem_server.so"; caused by (linker.c:709, pid 32259) load_library: library "libsurfaceflinger.so" not found This patch also fixes almost all of the compiler warnings. Change-Id: I64bb59aed6d4e039c15ea45be2367f319ef879f8 --- libc/private/logd.h | 8 + linker/Android.mk | 19 +- linker/README.TXT | 38 --- linker/debugger.c | 2 +- linker/{linker.c => linker.cpp} | 517 +++++++++++++++----------------- linker/linker.h | 10 +- linker/linker_environ.h | 10 +- linker/linker_format.c | 2 +- linker/linker_format.h | 8 + linker/linker_phdr.c | 46 ++- linker/linker_phdr.h | 21 +- 11 files changed, 321 insertions(+), 360 deletions(-) rename linker/{linker.c => linker.cpp} (84%) diff --git a/libc/private/logd.h b/libc/private/logd.h index 8970dafcf..c81a91a1c 100644 --- a/libc/private/logd.h +++ b/libc/private/logd.h @@ -59,6 +59,10 @@ enum { ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */ }; +#ifdef __cplusplus +extern "C" { +#endif + int __libc_android_log_write(int prio, const char* tag, const char* buffer); int __libc_android_log_print(int prio, const char *tag, const char *fmt, ...); int __libc_android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap); @@ -66,4 +70,8 @@ int __libc_android_log_vprint(int prio, const char *tag, const char *fmt, va_lis void __libc_android_log_event_int(int32_t tag, int value); void __libc_android_log_event_uid(int32_t tag); +#ifdef __cplusplus +}; +#endif + #endif /* _ANDROID_BIONIC_LOGD_H */ diff --git a/linker/Android.mk b/linker/Android.mk index d207f955f..b519976d1 100644 --- a/linker/Android.mk +++ b/linker/Android.mk @@ -3,13 +3,13 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ arch/$(TARGET_ARCH)/begin.S \ - linker.c \ + debugger.c \ + dlfcn.c \ + linker.cpp \ linker_environ.c \ linker_format.c \ linker_phdr.c \ - rt.c \ - dlfcn.c \ - debugger.c + rt.c LOCAL_LDFLAGS := -shared @@ -23,15 +23,14 @@ LOCAL_CFLAGS += -fno-stack-protector \ # LOCAL_CFLAGS += -DLINKER_DEBUG=0 -# we need to access the Bionic private header -# in the linker; duplicate the HAVE_ARM_TLS_REGISTER definition -# from the libc build +# We need to access Bionic private headers in the linker... +LOCAL_CFLAGS += -I$(LOCAL_PATH)/../libc/ + +# ...one of which is , for which we +# need HAVE_ARM_TLS_REGISTER. ifeq ($(TARGET_ARCH)-$(ARCH_ARM_HAVE_TLS_REGISTER),arm-true) LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER endif -LOCAL_CFLAGS += \ - -I$(LOCAL_PATH)/../libc/private \ - -I$(LOCAL_PATH)/../libc/arch-$(TARGET_ARCH)/bionic ifeq ($(TARGET_ARCH),arm) LOCAL_CFLAGS += -DANDROID_ARM_LINKER diff --git a/linker/README.TXT b/linker/README.TXT index f920b97bb..1770c8789 100644 --- a/linker/README.TXT +++ b/linker/README.TXT @@ -8,44 +8,6 @@ This document provides several notes related to the design of the Android dynamic linker. -Prelinking: ------------ - -System libraries in Android are internally prelinked, which means that -any internal relocations within them are stripped from the corresponding -shared object, in order to reduce size and speed up loading. - -Such libraries can only be loaded at the very specific virtual memory address -they have been prelinked to (during the build process). The list of prelinked -system libraries and their corresponding virtual memory address is found in -the file: - - build/core/prelink-linux-.map - -It should be updated each time a new system library is added to the -system. - -The prelink step happens at build time, and uses the 'soslim' and 'apriori' -tools: - - - 'apriori' is the real prelink tool which removes relocations from the - shared object, however, it must be given a list of symbols to remove - from the file. - - - 'soslim' is used to find symbols in an executable ELF file - and generate a list that can be passed to 'apriori'. - -By default, these tools are only used to remove internal symbols from -libraries, though they have been designed to allow more aggressive -optimizations (e.g. 'global' prelinking and symbol stripping, which -prevent replacing individual system libraries though). - -You can disable prelinking at build time by modifying your Android.mk with -a line like: - - LOCAL_PRELINK_MODULE := false - - Initialization and Termination functions: ----------------------------------------- diff --git a/linker/debugger.c b/linker/debugger.c index 7a1dd1584..dde66f353 100644 --- a/linker/debugger.c +++ b/linker/debugger.c @@ -156,7 +156,7 @@ static void logSignalSummary(int signum, const siginfo_t* info) * Catches fatal signals so we can ask debuggerd to ptrace us before * we crash. */ -void debugger_signal_handler(int n, siginfo_t* info, void* unused) +void debugger_signal_handler(int n, siginfo_t* info, void* unused __attribute__((unused))) { char msgbuf[128]; unsigned tid; diff --git a/linker/linker.c b/linker/linker.cpp similarity index 84% rename from linker/linker.c rename to linker/linker.cpp index e0c3bceeb..b2d813fb0 100644 --- a/linker/linker.c +++ b/linker/linker.cpp @@ -26,25 +26,23 @@ * SUCH DAMAGE. */ +#include +#include +#include #include - +#include +#include #include #include #include -#include -#include -#include -#include -#include - -#include - -#include - #include +#include +#include +#include -/* special private C library header - see Android.mk */ -#include +// Private C library headers. +#include +#include #include "linker.h" #include "linker_debug.h" @@ -79,8 +77,8 @@ * - 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 (64) -*/ + * having a hard limit (SO_MAX) + */ static int soinfo_link_image(soinfo *si, unsigned wr_offset); @@ -126,24 +124,30 @@ struct _link_stats linker_stats; unsigned bitmask[4096]; #endif -#define HOODLUM(name, ret, ...) \ - ret name __VA_ARGS__ \ - { \ - char errstr[] = "ERROR: " #name " called from the dynamic linker!\n"; \ - write(2, errstr, sizeof(errstr)); \ - abort(); \ +// You shouldn't try to call memory-allocating functions in the dynamic linker. +// Guard against the most obvious ones. +#define DISALLOW_ALLOCATION(return_type, name, ...) \ + return_type name __VA_ARGS__ \ + { \ + const char* msg = "ERROR: " #name " called from the dynamic linker!\n"; \ + __libc_android_log_write(ANDROID_LOG_FATAL, "linker", msg); \ + write(2, msg, sizeof(msg)); \ + abort(); \ } -HOODLUM(malloc, void *, (size_t size)); -HOODLUM(free, void, (void *ptr)); -HOODLUM(realloc, void *, (void *ptr, size_t size)); -HOODLUM(calloc, void *, (size_t cnt, size_t size)); +#define UNUSED __attribute__((unused)) +DISALLOW_ALLOCATION(void*, malloc, (size_t u UNUSED)); +DISALLOW_ALLOCATION(void, free, (void* u UNUSED)); +DISALLOW_ALLOCATION(void*, realloc, (void* u1 UNUSED, size_t u2 UNUSED)); +DISALLOW_ALLOCATION(void*, calloc, (size_t u1 UNUSED, size_t u2 UNUSED)); static char tmp_err_buf[768]; static char __linker_dl_err_buf[768]; +#define BASENAME(s) (strrchr(s, '/') != NULL ? strrchr(s, '/') + 1 : s) #define DL_ERR(fmt, x...) \ do { \ format_buffer(__linker_dl_err_buf, sizeof(__linker_dl_err_buf), \ - "%s[%d]: " fmt, __func__, __LINE__, ##x); \ + "(%s:%d, pid %d) %s: " fmt, \ + BASENAME(__FILE__), __LINE__, pid, __func__, ##x); \ ERROR(fmt "\n", ##x); \ } while(0) @@ -156,7 +160,7 @@ const char *linker_get_error(void) * This function is an empty stub where GDB locates a breakpoint to get notified * about linker activity. */ -extern void __attribute__((noinline)) __attribute__((visibility("default"))) rtld_db_dlactivity(void); +extern "C" void __attribute__((noinline)) __attribute__((visibility("default"))) rtld_db_dlactivity(void); static struct r_debug _r_debug = {1, NULL, &rtld_db_dlactivity, RT_CONSISTENT, 0}; @@ -243,7 +247,7 @@ void notify_gdb_of_unload(soinfo * info) pthread_mutex_unlock(&_r_debug_lock); } -void notify_gdb_of_libraries() +extern "C" void notify_gdb_of_libraries() { _r_debug.r_state = RT_ADD; rtld_db_dlactivity(); @@ -253,10 +257,8 @@ void notify_gdb_of_libraries() static soinfo *soinfo_alloc(const char *name) { - soinfo *si; - - if(strlen(name) >= SOINFO_NAME_LEN) { - DL_ERR("%5d library name %s too long", pid, name); + if (strlen(name) >= SOINFO_NAME_LEN) { + DL_ERR("library name \"%s\" too long", name); return NULL; } @@ -264,15 +266,15 @@ static soinfo *soinfo_alloc(const char *name) done only by dlclose(), which is not likely to be used. */ if (!freelist) { - if(socount == SO_MAX) { - DL_ERR("%5d too many libraries when loading %s", pid, name); + if (socount == SO_MAX) { + DL_ERR("too many libraries when loading \"%s\"", name); return NULL; } freelist = sopool + socount++; freelist->next = NULL; } - si = freelist; + soinfo* si = freelist; freelist = freelist->next; /* Make sure we get a clean block of soinfo */ @@ -287,8 +289,12 @@ static soinfo *soinfo_alloc(const char *name) return si; } -static void soinfo_free(soinfo *si) +static void soinfo_free(soinfo* si) { + if (si == NULL) { + return; + } + soinfo *prev = NULL, *trav; TRACE("%5d name %s: freeing soinfo @ %p\n", pid, si->name, si); @@ -300,7 +306,7 @@ static void soinfo_free(soinfo *si) } if (trav == NULL) { /* si was not ni solist */ - DL_ERR("%5d name %s is not in solist!", pid, si->name); + DL_ERR("name \"%s\" is not in solist!", si->name); return; } @@ -315,17 +321,16 @@ static void soinfo_free(soinfo *si) const char *addr_to_name(unsigned addr) { - soinfo *si; - - for(si = solist; si != 0; si = si->next){ - if((addr >= si->base) && (addr < (si->base + si->size))) { + for (soinfo* si = solist; si != 0; si = si->next) { + if ((addr >= si->base) && (addr < (si->base + si->size))) { return si->name; } } - return ""; } +#ifdef ANDROID_ARM_LINKER + /* For a given PC, find the .so that it belongs to. * Returns the base address of the .ARM.exidx section * for that .so, and the number of 8-byte entries @@ -335,7 +340,6 @@ const char *addr_to_name(unsigned addr) * * This function is exposed via dlfcn.c and libdl.so. */ -#ifdef ANDROID_ARM_LINKER _Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr pc, int *pcount) { soinfo *si; @@ -350,7 +354,9 @@ _Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr pc, int *pcount) *pcount = 0; return NULL; } + #elif defined(ANDROID_X86_LINKER) || defined(ANDROID_MIPS_LINKER) + /* Here, we only have to provide a callback to iterate across all the * loaded libraries. gcc_eh does the rest. */ int @@ -372,6 +378,7 @@ dl_iterate_phdr(int (*cb)(struct dl_phdr_info *info, size_t size, void *data), } return rv; } + #endif static Elf32_Sym *soinfo_elf_lookup(soinfo *si, unsigned hash, const char *name) @@ -454,8 +461,7 @@ soinfo_do_lookup(soinfo *si, const char *name, Elf32_Addr *offset) if(d[0] == DT_NEEDED){ lsi = (soinfo *)d[1]; if (!validate_soinfo(lsi)) { - DL_ERR("%5d bad DT_NEEDED pointer in %s", - pid, lsi->name); + DL_ERR("bad DT_NEEDED pointer in \"%s\"", lsi->name); return NULL; } @@ -638,35 +644,33 @@ static int open_library(const char *name) return -1; } -typedef struct { - long mmap_addr; - char tag[4]; /* 'P', 'R', 'E', ' ' */ -} prelink_info_t; - -/* Returns the requested base address if the library is prelinked, - * and 0 otherwise. */ -static unsigned long -is_prelinked(int fd, const char *name) +// Returns 'true' if the library is prelinked or on failure so we error out +// either way. We no longer support prelinking. +static bool is_prelinked(int fd, const char* name) { - off_t sz = lseek(fd, -sizeof(prelink_info_t), SEEK_END); + struct prelink_info_t { + long mmap_addr; + char tag[4]; // "PRE ". + }; + + off_t sz = lseek(fd, -sizeof(struct prelink_info_t), SEEK_END); if (sz < 0) { - DL_ERR("lseek() failed!"); - return 0; + DL_ERR("lseek failed: %s", strerror(errno)); + return true; } - prelink_info_t info; + struct prelink_info_t info; int rc = TEMP_FAILURE_RETRY(read(fd, &info, sizeof(info))); if (rc != sizeof(info)) { - WARN("Could not read prelink_info_t structure for `%s`\n", name); - return 0; + DL_ERR("could not read prelink_info_t structure for \"%s\":", name, strerror(errno)); + return true; } - if (memcmp(info.tag, "PRE ", 4)) { - WARN("`%s` is not a prelinked library\n", name); - return 0; + if (memcmp(info.tag, "PRE ", 4) == 0) { + DL_ERR("prelinked libraries no longer supported: %s", name); + return true; } - - return (unsigned long)info.mmap_addr; + return false; } /* verify_elf_header @@ -697,101 +701,106 @@ verify_elf_header(const Elf32_Ehdr* hdr) return 0; } +struct scoped_fd { + ~scoped_fd() { + if (fd != -1) { + close(fd); + } + } + int fd; +}; -static soinfo * -load_library(const char *name) +struct soinfo_ptr { + soinfo_ptr(const char* name) { + const char* bname = strrchr(name, '/'); + ptr = soinfo_alloc(bname ? bname + 1 : name); + } + ~soinfo_ptr() { + soinfo_free(ptr); + } + soinfo* release() { + soinfo* result = ptr; + ptr = NULL; + return result; + } + soinfo* ptr; +}; + +// TODO: rewrite linker_phdr.h to use a class, then lose this. +struct phdr_ptr { + phdr_ptr() : phdr_mmap(NULL) {} + ~phdr_ptr() { + if (phdr_mmap != NULL) { + phdr_table_unload(phdr_mmap, phdr_size); + } + } + void* phdr_mmap; + Elf32_Addr phdr_size; +}; + +static soinfo* load_library(const char* name) { - int fd = open_library(name); - int ret, cnt; - unsigned ext_sz; - unsigned req_base; - const char *bname; - struct stat sb; - soinfo *si = NULL; - Elf32_Ehdr header[1]; - int phdr_count; - void* phdr_mmap = NULL; - Elf32_Addr phdr_size; - const Elf32_Phdr* phdr_table; - - void* load_start = NULL; - Elf32_Addr load_size = 0; - Elf32_Addr load_bias = 0; - - if (fd == -1) { - DL_ERR("Library '%s' not found", name); + // Open the file. + scoped_fd fd; + fd.fd = open_library(name); + if (fd.fd == -1) { + DL_ERR("library \"%s\" not found", name); return NULL; } - /* Read the ELF header first */ - ret = TEMP_FAILURE_RETRY(read(fd, (void*)header, sizeof(header))); + // Read the ELF header. + Elf32_Ehdr header[1]; + int ret = TEMP_FAILURE_RETRY(read(fd.fd, (void*)header, sizeof(header))); if (ret < 0) { - DL_ERR("%5d can't read file %s: %s", pid, name, strerror(errno)); - goto fail; + DL_ERR("can't read file \"%s\": %s", name, strerror(errno)); + return NULL; } if (ret != (int)sizeof(header)) { - DL_ERR("%5d too small to be an ELF executable: %s", pid, name); - goto fail; + DL_ERR("too small to be an ELF executable: %s", name); + return NULL; } if (verify_elf_header(header) < 0) { - DL_ERR("%5d not a valid ELF executable: %s", pid, name); - goto fail; + DL_ERR("not a valid ELF executable: %s", name); + return NULL; } - /* Then read the program header table */ - ret = phdr_table_load(fd, header->e_phoff, header->e_phnum, - &phdr_mmap, &phdr_size, &phdr_table); + // Read the program header table. + const Elf32_Phdr* phdr_table; + phdr_ptr phdr_holder; + ret = phdr_table_load(fd.fd, header->e_phoff, header->e_phnum, + &phdr_holder.phdr_mmap, &phdr_holder.phdr_size, &phdr_table); if (ret < 0) { - DL_ERR("%5d can't load program header table: %s: %s", pid, - name, strerror(errno)); - goto fail; + DL_ERR("can't load program header table: %s: %s", name, strerror(errno)); + return NULL; } - phdr_count = header->e_phnum; + size_t phdr_count = header->e_phnum; - /* Get the load extents and the prelinked load address, if any */ - ext_sz = phdr_table_get_load_size(phdr_table, phdr_count); + // Get the load extents. + Elf32_Addr ext_sz = phdr_table_get_load_size(phdr_table, phdr_count); + TRACE("[ %5d - '%s' wants sz=0x%08x ]\n", pid, name, ext_sz); if (ext_sz == 0) { - DL_ERR("%5d no loadable segments in file: %s", pid, name); - goto fail; + DL_ERR("no loadable segments in file: %s", name); + return NULL; } - req_base = (unsigned) is_prelinked(fd, name); - if (req_base == (unsigned)-1) { - DL_ERR("%5d can't read end of library: %s: %s", pid, name, - strerror(errno)); - goto fail; - } - if (req_base != 0) { - TRACE("[ %5d - Prelinked library '%s' requesting base @ 0x%08x ]\n", - pid, name, req_base); - } else { - TRACE("[ %5d - Non-prelinked library '%s' found. ]\n", pid, name); + // We no longer support pre-linked libraries. + if (is_prelinked(fd.fd, name)) { + return NULL; } - TRACE("[ %5d - '%s' (%s) wants base=0x%08x sz=0x%08x ]\n", pid, name, - (req_base ? "prelinked" : "not pre-linked"), req_base, ext_sz); - - /* Now configure the soinfo struct where we'll store all of our data - * for the ELF object. If the loading fails, we waste the entry, but - * same thing would happen if we failed during linking. Configuring the - * soinfo struct here is a lot more convenient. - */ - bname = strrchr(name, '/'); - si = soinfo_alloc(bname ? bname + 1 : name); - if (si == NULL) - goto fail; - - /* Reserve address space for all loadable segments */ + // Reserve address space for all loadable segments. + void* load_start = NULL; + Elf32_Addr load_size = 0; + Elf32_Addr load_bias = 0; ret = phdr_table_reserve_memory(phdr_table, phdr_count, - req_base, &load_start, &load_size, &load_bias); if (ret < 0) { - DL_ERR("%5d Can't reserve %d bytes from 0x%08x in address space for %s: %s", - pid, ext_sz, req_base, name, strerror(errno)); - goto fail; + DL_ERR("can't reserve %d bytes in address space for \"%s\": %s", + ext_sz, name, strerror(errno)); + return NULL; } TRACE("[ %5d allocated memory for %s @ %p (0x%08x) ]\n", @@ -800,14 +809,12 @@ load_library(const char *name) /* Map all the segments in our address space with default protections */ ret = phdr_table_load_segments(phdr_table, phdr_count, - load_start, - load_size, load_bias, - fd); + fd.fd); if (ret < 0) { - DL_ERR("%5d Can't map loadable segments for %s: %s", - pid, name, strerror(errno)); - goto fail; + DL_ERR("can't map loadable segments for \"%s\": %s", + name, strerror(errno)); + return NULL; } /* Unprotect the segments, i.e. make them writable, to allow @@ -819,36 +826,30 @@ load_library(const char *name) phdr_count, load_bias); if (ret < 0) { - DL_ERR("%5d Can't unprotect loadable segments for %s: %s", - pid, name, strerror(errno)); - goto fail; + DL_ERR("can't unprotect loadable segments for \"%s\": %s", + name, strerror(errno)); + return NULL; } - si->base = (Elf32_Addr) load_start; - si->size = load_size; - si->load_bias = load_bias; - si->flags = 0; - si->entry = 0; - si->dynamic = (unsigned *)-1; - si->phnum = phdr_count; - si->phdr = phdr_table_get_loaded_phdr(phdr_table, phdr_count, load_bias); - if (si->phdr == NULL) { - DL_ERR("%5d Can't find loaded PHDR for %s", - pid, name); - goto fail; + soinfo_ptr si(name); + if (si.ptr == NULL) { + return NULL; } - phdr_table_unload(phdr_mmap, phdr_size); - close(fd); - return si; - -fail: - if (si) soinfo_free(si); - if (phdr_mmap != NULL) { - phdr_table_unload(phdr_mmap, phdr_size); + si.ptr->base = (Elf32_Addr) load_start; + si.ptr->size = load_size; + si.ptr->load_bias = load_bias; + si.ptr->flags = 0; + si.ptr->entry = 0; + si.ptr->dynamic = (unsigned *)-1; + si.ptr->phnum = phdr_count; + si.ptr->phdr = phdr_table_get_loaded_phdr(phdr_table, phdr_count, load_bias); + if (si.ptr->phdr == NULL) { + DL_ERR("can't find loaded PHDR for \"%s\"", name); + return NULL; } - close(fd); - return NULL; + + return si.release(); } static soinfo * @@ -891,11 +892,11 @@ soinfo *find_library(const char *name) for(si = solist; si != 0; si = si->next){ if(!strcmp(bname, si->name)) { if(si->flags & FLAG_ERROR) { - DL_ERR("%5d '%s' failed to load previously", pid, bname); + DL_ERR("\"%s\" failed to load previously", bname); return NULL; } if(si->flags & FLAG_LINKED) return si; - DL_ERR("OOPS: %5d recursive link to '%s'", pid, si->name); + DL_ERR("OOPS: recursive link to \"%s\"", si->name); return NULL; } } @@ -908,8 +909,7 @@ soinfo *find_library(const char *name) } /* TODO: - * notify gdb of unload - * for non-prelinked libraries, find a way to decrement libbase + * find a way to decrement libbase */ static void call_destructors(soinfo *si); unsigned soinfo_unload(soinfo *si) @@ -925,9 +925,9 @@ unsigned soinfo_unload(soinfo *si) */ if (phdr_table_unprotect_gnu_relro(si->phdr, si->phnum, si->load_bias) < 0) { - DL_ERR("%5d %s: could not undo GNU_RELRO protections. " + DL_ERR("%s: could not undo GNU_RELRO protections. " "Expect a crash soon. errno=%d (%s)", - pid, si->name, errno, strerror(errno)); + si->name, errno, strerror(errno)); } for(d = si->dynamic; *d; d += 2) { @@ -945,8 +945,8 @@ unsigned soinfo_unload(soinfo *si) soinfo_unload(lsi); } else - DL_ERR("%5d %s: could not unload dependent library", - pid, si->name); + DL_ERR("\"%s\": could not unload dependent library", + si->name); } } @@ -972,12 +972,10 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count) Elf32_Sym *symtab = si->symtab; const char *strtab = si->strtab; Elf32_Sym *s; - unsigned base; Elf32_Addr offset; Elf32_Rel *start = rel; - unsigned idx; - for (idx = 0; idx < count; ++idx, ++rel) { + for (size_t idx = 0; idx < count; ++idx, ++rel) { unsigned type = ELF32_R_TYPE(rel->r_info); unsigned sym = ELF32_R_SYM(rel->r_info); unsigned reloc = (unsigned)(rel->r_offset + si->load_bias); @@ -997,7 +995,7 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count) reference.. */ s = &symtab[sym]; if (ELF32_ST_BIND(s->st_info) != STB_WEAK) { - DL_ERR("%5d cannot locate '%s'...\n", pid, sym_name); + DL_ERR("cannot locate \"%s\"...", sym_name); return -1; } @@ -1043,8 +1041,8 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count) not found in run-time. */ #endif /* ANDROID_ARM_LINKER */ default: - DL_ERR("%5d unknown weak reloc type %d @ %p (%d)\n", - pid, type, rel, (int) (rel - start)); + DL_ERR("unknown weak reloc type %d @ %p (%d)", + type, rel, (int) (rel - start)); return -1; } } else { @@ -1052,8 +1050,8 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count) #if 0 if((base == 0) && (si->base != 0)){ /* linking from libraries to main image is bad */ - DL_ERR("%5d cannot locate '%s'...", - pid, strtab + symtab[sym].st_name); + DL_ERR("cannot locate \"%s\"...", + strtab + symtab[sym].st_name); return -1; } #endif @@ -1140,8 +1138,8 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count) #endif /* ANDROID_*_LINKER */ COUNT_RELOC(RELOC_RELATIVE); MARK(rel->r_offset); - if(sym){ - DL_ERR("%5d odd RELATIVE form...", pid); + if (sym) { + DL_ERR("odd RELATIVE form...", pid); return -1; } TRACE_TYPE(RELO, "%5d RELO RELATIVE %08x <- +%08x\n", pid, @@ -1180,8 +1178,8 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count) #endif /* ANDROID_ARM_LINKER */ default: - DL_ERR("%5d unknown reloc type %d @ %p (%d)", - pid, type, rel, (int) (rel - start)); + DL_ERR("unknown reloc type %d @ %p (%d)", + type, rel, (int) (rel - start)); return -1; } } @@ -1240,7 +1238,7 @@ int mips_relocate_got(struct soinfo *si) reference.. */ s = &symtab[g]; if (ELF32_ST_BIND(s->st_info) != STB_WEAK) { - DL_ERR("%5d cannot locate '%s'...\n", pid, sym_name); + DL_ERR("cannot locate \"%s\"...", sym_name); return -1; } *got = 0; @@ -1320,9 +1318,8 @@ void soinfo_call_constructors(soinfo *si) TRACE("[ %5d Done calling preinit_array for '%s' ]\n", pid, si->name); } else { if (si->preinit_array) { - DL_ERR("%5d Shared library '%s' has a preinit_array table @ 0x%08x." - " This is INVALID.", pid, si->name, - (unsigned)si->preinit_array); + DL_ERR("shared library \"%s\" has a preinit_array table @ 0x%08x. " + "This is INVALID.", si->name, (unsigned) si->preinit_array); } } @@ -1332,8 +1329,7 @@ void soinfo_call_constructors(soinfo *si) if(d[0] == DT_NEEDED){ soinfo* lsi = (soinfo *)d[1]; if (!validate_soinfo(lsi)) { - DL_ERR("%5d bad DT_NEEDED pointer in %s", - pid, si->name); + DL_ERR("bad DT_NEEDED pointer in \"%s\"", si->name); } else { soinfo_call_constructors(lsi); } @@ -1383,7 +1379,7 @@ static int nullify_closed_stdio (void) dev_null = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)); if (dev_null < 0) { - DL_ERR("Cannot open /dev/null."); + DL_ERR("cannot open /dev/null: %s", strerror(errno)); return -1; } TRACE("[ %5d Opened /dev/null file-descriptor=%d]\n", pid, dev_null); @@ -1392,24 +1388,22 @@ static int nullify_closed_stdio (void) with /dev/null, dup /dev/null to it. */ for (i = 0; i < 3; i++) { /* If it is /dev/null already, we are done. */ - if (i == dev_null) + if (i == dev_null) { continue; + } TRACE("[ %5d Nullifying stdio file descriptor %d]\n", pid, i); - /* The man page of fcntl does not say that fcntl(..,F_GETFL) - can be interrupted but we do this just to be safe. */ - do { - status = fcntl(i, F_GETFL); - } while (status < 0 && errno == EINTR); + status = TEMP_FAILURE_RETRY(fcntl(i, F_GETFL)); - /* If file is openned, we are good. */ - if (status >= 0) - continue; + /* If file is opened, we are good. */ + if (status != -1) { + continue; + } /* The only error we allow is that the file descriptor does not exist, in which case we dup /dev/null to it. */ if (errno != EBADF) { - DL_ERR("nullify_stdio: unhandled error %s", strerror(errno)); + DL_ERR("fcntl failed: %s", strerror(errno)); return_value = -1; continue; } @@ -1417,12 +1411,9 @@ static int nullify_closed_stdio (void) /* Try dupping /dev/null to this stdio file descriptor and repeat if there is a signal. Note that any errors in closing the stdio descriptor are lost. */ - do { - status = dup2(dev_null, i); - } while (status < 0 && errno == EINTR); - + status = TEMP_FAILURE_RETRY(dup2(dev_null, i)); if (status < 0) { - DL_ERR("nullify_stdio: dup2 error %s", strerror(errno)); + DL_ERR("dup2 failed: %s", strerror(errno)); return_value = -1; continue; } @@ -1431,12 +1422,9 @@ static int nullify_closed_stdio (void) /* If /dev/null is not one of the stdio file descriptors, close it. */ if (dev_null > 2) { TRACE("[ %5d Closing /dev/null file-descriptor=%d]\n", pid, dev_null); - do { - status = close(dev_null); - } while (status < 0 && errno == EINTR); - - if (status < 0) { - DL_ERR("nullify_stdio: close error %s", strerror(errno)); + status = TEMP_FAILURE_RETRY(close(dev_null)); + if (status == -1) { + DL_ERR("close failed: %s", strerror(errno)); return_value = -1; } } @@ -1464,7 +1452,7 @@ static int soinfo_link_image(soinfo *si, unsigned wr_offset) si->dynamic = phdr_table_get_dynamic_section(phdr, phnum, base); if (si->dynamic == NULL) { if (!relocating_linker) { - DL_ERR("%5d missing PT_DYNAMIC?!", pid); + DL_ERR("missing PT_DYNAMIC?!"); } goto fail; } else { @@ -1485,8 +1473,8 @@ static int soinfo_link_image(soinfo *si, unsigned wr_offset) /* We can't call DL_ERR if the linker's relocations haven't * been performed yet */ if (!relocating_linker) { - DL_ERR("%5d Can't unprotect segments for %s: %s", - pid, si->name, strerror(errno)); + DL_ERR("can't unprotect segments for \"%s\": %s", + si->name, strerror(errno)); } goto fail; } @@ -1537,7 +1525,7 @@ static int soinfo_link_image(soinfo *si, unsigned wr_offset) #endif break; case DT_RELA: - DL_ERR("%5d DT_RELA not supported", pid); + DL_ERR("DT_RELA not supported"); goto fail; case DT_INIT: si->init_func = (void (*)(void))(base + *d); @@ -1633,7 +1621,7 @@ static int soinfo_link_image(soinfo *si, unsigned wr_offset) pid, si->base, si->strtab, si->symtab); if((si->strtab == 0) || (si->symtab == 0)) { - DL_ERR("%5d missing essential tables", pid); + DL_ERR("missing essential tables"); goto fail; } @@ -1645,8 +1633,8 @@ static int soinfo_link_image(soinfo *si, unsigned wr_offset) soinfo *lsi = find_library(ldpreload_names[i]); if(lsi == 0) { strlcpy(tmp_err_buf, linker_get_error(), sizeof(tmp_err_buf)); - DL_ERR("%5d could not load needed library '%s' for '%s' (%s)", - pid, ldpreload_names[i], si->name, 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; } lsi->refcount++; @@ -1660,8 +1648,8 @@ static int soinfo_link_image(soinfo *si, unsigned wr_offset) soinfo *lsi = find_library(si->strtab + d[1]); if(lsi == 0) { strlcpy(tmp_err_buf, linker_get_error(), sizeof(tmp_err_buf)); - DL_ERR("%5d could not load needed library '%s' for '%s' (%s)", - pid, si->strtab + d[1], si->name, 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; } /* Save the soinfo of the loaded DT_NEEDED library in the payload @@ -1699,15 +1687,15 @@ static int soinfo_link_image(soinfo *si, unsigned wr_offset) /* All relocations are done, we can protect our segments back to * read-only. */ if (phdr_table_protect_segments(si->phdr, si->phnum, si->load_bias) < 0) { - DL_ERR("%5d Can't protect segments for %s: %s", - pid, si->name, strerror(errno)); + DL_ERR("can't protect segments for \"%s\": %s", + si->name, strerror(errno)); goto fail; } /* We can also turn on GNU RELRO protection */ if (phdr_table_protect_gnu_relro(si->phdr, si->phnum, si->load_bias) < 0) { - DL_ERR("%5d Can't enable GNU RELRO protection for %s: %s", - pid, si->name, strerror(errno)); + DL_ERR("can't enable GNU RELRO protection for \"%s\": %s", + si->name, strerror(errno)); goto fail; } @@ -1717,8 +1705,9 @@ static int soinfo_link_image(soinfo *si, unsigned wr_offset) ftp://ftp.freebsd.org/pub/FreeBSD/CERT/advisories/FreeBSD-SA-02:23.stdio.asc */ - if (program_is_setuid) - nullify_closed_stdio (); + if (program_is_setuid) { + nullify_closed_stdio(); + } notify_gdb_of_load(si); return 0; @@ -1728,53 +1717,43 @@ fail: return -1; } -static void parse_library_path(const char *path, char *delim) +static void parse_path(const char* path, const char* delimiters, + const char** array, char* buf, size_t buf_size, size_t max_count) { - size_t len; - char *ldpaths_bufp = ldpaths_buf; - int i = 0; - - len = strlcpy(ldpaths_buf, path, sizeof(ldpaths_buf)); - - while (i < LDPATH_MAX && (ldpaths[i] = strsep(&ldpaths_bufp, delim))) { - if (*ldpaths[i] != '\0') - ++i; + if (path == NULL) { + return; } - /* Forget the last path if we had to truncate; this occurs if the 2nd to - * last char isn't '\0' (i.e. not originally a delim). */ - if (i > 0 && len >= sizeof(ldpaths_buf) && - ldpaths_buf[sizeof(ldpaths_buf) - 2] != '\0') { - ldpaths[i - 1] = NULL; - } else { - ldpaths[i] = NULL; - } -} + size_t len = strlcpy(buf, path, buf_size); -static void parse_preloads(const char *path, char *delim) -{ - size_t len; - char *ldpreloads_bufp = ldpreloads_buf; - int i = 0; - - len = strlcpy(ldpreloads_buf, path, sizeof(ldpreloads_buf)); - - while (i < LDPRELOAD_MAX && (ldpreload_names[i] = strsep(&ldpreloads_bufp, delim))) { - if (*ldpreload_names[i] != '\0') { + size_t i = 0; + char* buf_p = buf; + while (i < max_count && (array[i] = strsep(&buf_p, delimiters))) { + if (*array[i] != '\0') { ++i; } } - /* Forget the last path if we had to truncate; this occurs if the 2nd to - * last char isn't '\0' (i.e. not originally a delim). */ - if (i > 0 && len >= sizeof(ldpreloads_buf) && - ldpreloads_buf[sizeof(ldpreloads_buf) - 2] != '\0') { - ldpreload_names[i - 1] = NULL; + // Forget the last path if we had to truncate; this occurs if the 2nd to + // last char isn't '\0' (i.e. wasn't originally a delimiter). + if (i > 0 && len >= buf_size && buf[buf_size - 2] != '\0') { + array[i - 1] = NULL; } else { - ldpreload_names[i] = NULL; + array[i] = NULL; } } +static void parse_LD_LIBRARY_PATH(const char* path) { + parse_path(path, ":", ldpaths, + ldpaths_buf, sizeof(ldpaths_buf), 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); +} + /* * This code is called after the linker has linked itself and * fixed it's own GOT. It is safe to make references to externs @@ -1917,13 +1896,9 @@ sanitize: si->dynamic = (unsigned *)-1; si->refcount = 1; - /* Use LD_LIBRARY_PATH if we aren't setuid/setgid */ - if (ldpath_env) - parse_library_path(ldpath_env, ":"); - - if (ldpreload_env) { - parse_preloads(ldpreload_env, " :"); - } + // Use LD_LIBRARY_PATH and LD_PRELOAD (but only if we aren't setuid/setgid). + parse_LD_LIBRARY_PATH(ldpath_env); + parse_LD_PRELOAD(ldpreload_env); if(soinfo_link_image(si, 0)) { char errmsg[] = "CANNOT LINK EXECUTABLE\n"; @@ -2047,7 +2022,7 @@ get_elf_exec_load_bias(const Elf32_Ehdr* elf) * relocations, any attempt to reference an extern variable, extern * function, or other GOT reference will generate a segfault. */ -unsigned __linker_init(unsigned **elfdata) { +extern "C" unsigned __linker_init(unsigned **elfdata) { unsigned linker_addr = find_linker_base(elfdata); Elf32_Ehdr *elf_hdr = (Elf32_Ehdr *) linker_addr; Elf32_Phdr *phdr = diff --git a/linker/linker.h b/linker/linker.h index cb2eab65b..ce811dfae 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -34,6 +34,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + #undef PAGE_MASK #undef PAGE_SIZE #define PAGE_SIZE 4096 @@ -106,7 +110,7 @@ typedef struct soinfo soinfo; struct soinfo { - const char name[SOINFO_NAME_LEN]; + char name[SOINFO_NAME_LEN]; const Elf32_Phdr *phdr; int phnum; unsigned entry; @@ -244,4 +248,8 @@ _Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr pc, int *pcount); int dl_iterate_phdr(int (*cb)(struct dl_phdr_info *, size_t, void *), void *); #endif +#ifdef __cplusplus +}; +#endif + #endif diff --git a/linker/linker_environ.h b/linker/linker_environ.h index 98ad1de27..d5f75a1f8 100644 --- a/linker/linker_environ.h +++ b/linker/linker_environ.h @@ -28,6 +28,10 @@ #ifndef LINKER_ENVIRON_H #define LINKER_ENVIRON_H +#ifdef __cplusplus +extern "C" { +#endif + /* Call this function before anything else. 'vecs' must be the pointer * to the environment block in the ELF data block. The function returns * the start of the aux vectors after the env block. @@ -47,8 +51,12 @@ extern void linker_env_unset(const char* name); * after this function. */ extern const char* linker_env_get(const char* name); -/* Remove unsecure environment variables. This should be used when +/* Remove insecure environment variables. This should be used when * running setuid programs. */ extern void linker_env_secure(void); +#ifdef __cplusplus +}; +#endif + #endif /* LINKER_ENVIRON_H */ diff --git a/linker/linker_format.c b/linker/linker_format.c index d30574020..f60e2593d 100644 --- a/linker/linker_format.c +++ b/linker/linker_format.c @@ -312,7 +312,7 @@ static int log_vprint(int prio, const char *tag, const char *fmt, va_list args) #else /* !CUSTOM_LOG_VPRINT */ -extern int __libc_android_log_vprint(int prio, const char* tag, const char* format, va_list ap); +extern "C" int __libc_android_log_vprint(int prio, const char* tag, const char* format, va_list ap); #endif /* !CUSTOM_LOG_VPRINT */ diff --git a/linker/linker_format.h b/linker/linker_format.h index 6ae2badbf..3766b628a 100644 --- a/linker/linker_format.h +++ b/linker/linker_format.h @@ -31,6 +31,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + /* Formatting routines for the dynamic linker's debug traces */ /* We want to avoid dragging the whole C library fprintf() */ /* implementation into the dynamic linker since this creates */ @@ -38,4 +42,8 @@ int format_buffer(char *buffer, size_t bufsize, const char *format, ...); +#ifdef __cplusplus +}; +#endif + #endif /* _LINKER_FORMAT_H */ diff --git a/linker/linker_phdr.c b/linker/linker_phdr.c index c9f194b0a..beb756fef 100644 --- a/linker/linker_phdr.c +++ b/linker/linker_phdr.c @@ -180,24 +180,25 @@ void phdr_table_unload(void* phdr_mmap, Elf32_Addr phdr_memsize) * This returns 0 if there are no loadable segments. */ Elf32_Addr phdr_table_get_load_size(const Elf32_Phdr* phdr_table, - int phdr_count) + size_t phdr_count) { - int nn; - Elf32_Addr min_vaddr = 0xFFFFFFFFU; Elf32_Addr max_vaddr = 0x00000000U; - for (nn = 0; nn < phdr_count; nn++) { - const Elf32_Phdr* phdr = &phdr_table[nn]; + for (size_t i = 0; i < phdr_count; ++i) { + const Elf32_Phdr* phdr = &phdr_table[i]; - if (phdr->p_type != PT_LOAD) + if (phdr->p_type != PT_LOAD) { continue; + } - if (phdr->p_vaddr < min_vaddr) + if (phdr->p_vaddr < min_vaddr) { min_vaddr = phdr->p_vaddr; + } - if (phdr->p_vaddr + phdr->p_memsz > max_vaddr) + if (phdr->p_vaddr + phdr->p_memsz > max_vaddr) { max_vaddr = phdr->p_vaddr + phdr->p_memsz; + } } if (min_vaddr > max_vaddr) { @@ -217,8 +218,6 @@ Elf32_Addr phdr_table_get_load_size(const Elf32_Phdr* phdr_table, * Input: * phdr_table -> program header table * phdr_count -> number of entries in the tables - * required_base -> for prelinked libraries, mandatory load address - * of the first loadable segment. 0 otherwise. * Output: * load_start -> first page of reserved address space range * load_size -> size in bytes of reserved address space range @@ -229,26 +228,19 @@ Elf32_Addr phdr_table_get_load_size(const Elf32_Phdr* phdr_table, */ int phdr_table_reserve_memory(const Elf32_Phdr* phdr_table, - int phdr_count, - Elf32_Addr required_base, - void** load_start, - Elf32_Addr* load_size, - Elf32_Addr* load_bias) + size_t phdr_count, + void** load_start, + Elf32_Addr* load_size, + Elf32_Addr* load_bias) { Elf32_Addr size = phdr_table_get_load_size(phdr_table, phdr_count); - void* start; - int nn, mmap_flags; - if (size == 0) { errno = EINVAL; return -1; } - mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS; - if (required_base != 0) - mmap_flags |= MAP_FIXED; - - start = mmap((void*)required_base, size, PROT_NONE, mmap_flags, -1, 0); + int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS; + void* start = mmap(NULL, size, PROT_NONE, mmap_flags, -1, 0); if (start == MAP_FAILED) { return -1; } @@ -257,8 +249,8 @@ phdr_table_reserve_memory(const Elf32_Phdr* phdr_table, *load_size = size; *load_bias = 0; - for (nn = 0; nn < phdr_count; nn++) { - const Elf32_Phdr* phdr = &phdr_table[nn]; + for (size_t i = 0; i < phdr_count; ++i) { + const Elf32_Phdr* phdr = &phdr_table[i]; if (phdr->p_type == PT_LOAD) { *load_bias = (Elf32_Addr)start - PAGE_START(phdr->p_vaddr); break; @@ -274,8 +266,6 @@ phdr_table_reserve_memory(const Elf32_Phdr* phdr_table, * Input: * phdr_table -> program header table * phdr_count -> number of entries in the table - * load_start -> start address of reserved memory range. - * load_size -> size of reserved memory range. * load_bias -> load offset. * fd -> input file descriptor. * @@ -285,8 +275,6 @@ phdr_table_reserve_memory(const Elf32_Phdr* phdr_table, int phdr_table_load_segments(const Elf32_Phdr* phdr_table, int phdr_count, - void* load_start, - Elf32_Addr load_size, Elf32_Addr load_bias, int fd) { diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h index d542e463e..753c7e76d 100644 --- a/linker/linker_phdr.h +++ b/linker/linker_phdr.h @@ -37,6 +37,10 @@ #include "linker.h" +#ifdef __cplusplus +extern "C" { +#endif + /* See linker_phdr.c for all usage documentation */ int @@ -52,21 +56,18 @@ phdr_table_unload(void* phdr_mmap, Elf32_Addr phdr_memsize); Elf32_Addr phdr_table_get_load_size(const Elf32_Phdr* phdr_table, - int phdr_count); + size_t phdr_count); int phdr_table_reserve_memory(const Elf32_Phdr* phdr_table, - int phdr_count, - Elf32_Addr required_base, - void** load_start, - Elf32_Addr* load_size, - Elf32_Addr* load_bias); + size_t phdr_count, + void** load_start, + Elf32_Addr* load_size, + Elf32_Addr* load_bias); int phdr_table_load_segments(const Elf32_Phdr* phdr_table, int phdr_count, - void* load_start, - Elf32_Addr load_size, Elf32_Addr load_bias, int fd); @@ -109,4 +110,8 @@ phdr_table_get_dynamic_section(const Elf32_Phdr* phdr_table, int phdr_count, Elf32_Addr load_bias); +#ifdef __cplusplus +}; +#endif + #endif /* LINKER_PHDR_H */