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 */