am 78129204: Merge "Clean up the linker a bit, remove prelinking support."

* commit '7812920487070d392984f94c9f80006dad8c198a':
  Clean up the linker a bit, remove prelinking support.
This commit is contained in:
Elliott Hughes 2012-08-08 15:29:27 -07:00 committed by Android Git Automerger
commit f3ffb8fa6e
11 changed files with 321 additions and 360 deletions

View File

@ -59,6 +59,10 @@ enum {
ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */ 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_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_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); 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_int(int32_t tag, int value);
void __libc_android_log_event_uid(int32_t tag); void __libc_android_log_event_uid(int32_t tag);
#ifdef __cplusplus
};
#endif
#endif /* _ANDROID_BIONIC_LOGD_H */ #endif /* _ANDROID_BIONIC_LOGD_H */

View File

@ -3,13 +3,13 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \ LOCAL_SRC_FILES:= \
arch/$(TARGET_ARCH)/begin.S \ arch/$(TARGET_ARCH)/begin.S \
linker.c \ debugger.c \
dlfcn.c \
linker.cpp \
linker_environ.c \ linker_environ.c \
linker_format.c \ linker_format.c \
linker_phdr.c \ linker_phdr.c \
rt.c \ rt.c
dlfcn.c \
debugger.c
LOCAL_LDFLAGS := -shared LOCAL_LDFLAGS := -shared
@ -23,15 +23,14 @@ LOCAL_CFLAGS += -fno-stack-protector \
# #
LOCAL_CFLAGS += -DLINKER_DEBUG=0 LOCAL_CFLAGS += -DLINKER_DEBUG=0
# we need to access the Bionic private header <bionic_tls.h> # We need to access Bionic private headers in the linker...
# in the linker; duplicate the HAVE_ARM_TLS_REGISTER definition LOCAL_CFLAGS += -I$(LOCAL_PATH)/../libc/
# from the libc build
# ...one of which is <private/bionic_tls.h>, for which we
# need HAVE_ARM_TLS_REGISTER.
ifeq ($(TARGET_ARCH)-$(ARCH_ARM_HAVE_TLS_REGISTER),arm-true) ifeq ($(TARGET_ARCH)-$(ARCH_ARM_HAVE_TLS_REGISTER),arm-true)
LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER
endif endif
LOCAL_CFLAGS += \
-I$(LOCAL_PATH)/../libc/private \
-I$(LOCAL_PATH)/../libc/arch-$(TARGET_ARCH)/bionic
ifeq ($(TARGET_ARCH),arm) ifeq ($(TARGET_ARCH),arm)
LOCAL_CFLAGS += -DANDROID_ARM_LINKER LOCAL_CFLAGS += -DANDROID_ARM_LINKER

View File

@ -8,44 +8,6 @@ This document provides several notes related to the design of the Android
dynamic linker. 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-<arch>.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: Initialization and Termination functions:
----------------------------------------- -----------------------------------------

View File

@ -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 * Catches fatal signals so we can ask debuggerd to ptrace us before
* we crash. * 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]; char msgbuf[128];
unsigned tid; unsigned tid;

View File

@ -26,25 +26,23 @@
* SUCH DAMAGE. * SUCH DAMAGE.
*/ */
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/auxvec.h> #include <linux/auxvec.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <dlfcn.h>
#include <sys/stat.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/atomics.h> #include <sys/atomics.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
/* special private C library header - see Android.mk */ // Private C library headers.
#include <bionic_tls.h> #include <private/bionic_tls.h>
#include <private/logd.h>
#include "linker.h" #include "linker.h"
#include "linker_debug.h" #include "linker_debug.h"
@ -79,8 +77,8 @@
* - linker hardcodes PAGE_SIZE and PAGE_MASK because the kernel * - linker hardcodes PAGE_SIZE and PAGE_MASK because the kernel
* headers provide versions that are negative... * headers provide versions that are negative...
* - allocate space for soinfo structs dynamically instead of * - 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); static int soinfo_link_image(soinfo *si, unsigned wr_offset);
@ -126,24 +124,30 @@ struct _link_stats linker_stats;
unsigned bitmask[4096]; unsigned bitmask[4096];
#endif #endif
#define HOODLUM(name, ret, ...) \ // You shouldn't try to call memory-allocating functions in the dynamic linker.
ret name __VA_ARGS__ \ // Guard against the most obvious ones.
{ \ #define DISALLOW_ALLOCATION(return_type, name, ...) \
char errstr[] = "ERROR: " #name " called from the dynamic linker!\n"; \ return_type name __VA_ARGS__ \
write(2, errstr, sizeof(errstr)); \ { \
abort(); \ 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)); #define UNUSED __attribute__((unused))
HOODLUM(free, void, (void *ptr)); DISALLOW_ALLOCATION(void*, malloc, (size_t u UNUSED));
HOODLUM(realloc, void *, (void *ptr, size_t size)); DISALLOW_ALLOCATION(void, free, (void* u UNUSED));
HOODLUM(calloc, void *, (size_t cnt, size_t size)); 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 tmp_err_buf[768];
static char __linker_dl_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...) \ #define DL_ERR(fmt, x...) \
do { \ do { \
format_buffer(__linker_dl_err_buf, sizeof(__linker_dl_err_buf), \ 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); \ ERROR(fmt "\n", ##x); \
} while(0) } 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 * This function is an empty stub where GDB locates a breakpoint to get notified
* about linker activity. * 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, static struct r_debug _r_debug = {1, NULL, &rtld_db_dlactivity,
RT_CONSISTENT, 0}; RT_CONSISTENT, 0};
@ -243,7 +247,7 @@ void notify_gdb_of_unload(soinfo * info)
pthread_mutex_unlock(&_r_debug_lock); pthread_mutex_unlock(&_r_debug_lock);
} }
void notify_gdb_of_libraries() extern "C" void notify_gdb_of_libraries()
{ {
_r_debug.r_state = RT_ADD; _r_debug.r_state = RT_ADD;
rtld_db_dlactivity(); rtld_db_dlactivity();
@ -253,10 +257,8 @@ void notify_gdb_of_libraries()
static soinfo *soinfo_alloc(const char *name) static soinfo *soinfo_alloc(const char *name)
{ {
soinfo *si; if (strlen(name) >= SOINFO_NAME_LEN) {
DL_ERR("library name \"%s\" too long", name);
if(strlen(name) >= SOINFO_NAME_LEN) {
DL_ERR("%5d library name %s too long", pid, name);
return NULL; return NULL;
} }
@ -264,15 +266,15 @@ static soinfo *soinfo_alloc(const char *name)
done only by dlclose(), which is not likely to be used. done only by dlclose(), which is not likely to be used.
*/ */
if (!freelist) { if (!freelist) {
if(socount == SO_MAX) { if (socount == SO_MAX) {
DL_ERR("%5d too many libraries when loading %s", pid, name); DL_ERR("too many libraries when loading \"%s\"", name);
return NULL; return NULL;
} }
freelist = sopool + socount++; freelist = sopool + socount++;
freelist->next = NULL; freelist->next = NULL;
} }
si = freelist; soinfo* si = freelist;
freelist = freelist->next; freelist = freelist->next;
/* Make sure we get a clean block of soinfo */ /* Make sure we get a clean block of soinfo */
@ -287,8 +289,12 @@ static soinfo *soinfo_alloc(const char *name)
return si; return si;
} }
static void soinfo_free(soinfo *si) static void soinfo_free(soinfo* si)
{ {
if (si == NULL) {
return;
}
soinfo *prev = NULL, *trav; soinfo *prev = NULL, *trav;
TRACE("%5d name %s: freeing soinfo @ %p\n", pid, si->name, si); 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) { if (trav == NULL) {
/* si was not ni solist */ /* 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; return;
} }
@ -315,17 +321,16 @@ static void soinfo_free(soinfo *si)
const char *addr_to_name(unsigned addr) const char *addr_to_name(unsigned addr)
{ {
soinfo *si; for (soinfo* si = solist; si != 0; si = si->next) {
if ((addr >= si->base) && (addr < (si->base + si->size))) {
for(si = solist; si != 0; si = si->next){
if((addr >= si->base) && (addr < (si->base + si->size))) {
return si->name; return si->name;
} }
} }
return ""; return "";
} }
#ifdef ANDROID_ARM_LINKER
/* For a given PC, find the .so that it belongs to. /* For a given PC, find the .so that it belongs to.
* Returns the base address of the .ARM.exidx section * Returns the base address of the .ARM.exidx section
* for that .so, and the number of 8-byte entries * 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. * 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) _Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr pc, int *pcount)
{ {
soinfo *si; soinfo *si;
@ -350,7 +354,9 @@ _Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr pc, int *pcount)
*pcount = 0; *pcount = 0;
return NULL; return NULL;
} }
#elif defined(ANDROID_X86_LINKER) || defined(ANDROID_MIPS_LINKER) #elif defined(ANDROID_X86_LINKER) || defined(ANDROID_MIPS_LINKER)
/* Here, we only have to provide a callback to iterate across all the /* Here, we only have to provide a callback to iterate across all the
* loaded libraries. gcc_eh does the rest. */ * loaded libraries. gcc_eh does the rest. */
int int
@ -372,6 +378,7 @@ dl_iterate_phdr(int (*cb)(struct dl_phdr_info *info, size_t size, void *data),
} }
return rv; return rv;
} }
#endif #endif
static Elf32_Sym *soinfo_elf_lookup(soinfo *si, unsigned hash, const char *name) 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){ if(d[0] == DT_NEEDED){
lsi = (soinfo *)d[1]; lsi = (soinfo *)d[1];
if (!validate_soinfo(lsi)) { if (!validate_soinfo(lsi)) {
DL_ERR("%5d bad DT_NEEDED pointer in %s", DL_ERR("bad DT_NEEDED pointer in \"%s\"", lsi->name);
pid, lsi->name);
return NULL; return NULL;
} }
@ -638,35 +644,33 @@ static int open_library(const char *name)
return -1; return -1;
} }
typedef struct { // Returns 'true' if the library is prelinked or on failure so we error out
long mmap_addr; // either way. We no longer support prelinking.
char tag[4]; /* 'P', 'R', 'E', ' ' */ static bool is_prelinked(int fd, const char* name)
} 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)
{ {
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) { if (sz < 0) {
DL_ERR("lseek() failed!"); DL_ERR("lseek failed: %s", strerror(errno));
return 0; return true;
} }
prelink_info_t info; struct prelink_info_t info;
int rc = TEMP_FAILURE_RETRY(read(fd, &info, sizeof(info))); int rc = TEMP_FAILURE_RETRY(read(fd, &info, sizeof(info)));
if (rc != sizeof(info)) { if (rc != sizeof(info)) {
WARN("Could not read prelink_info_t structure for `%s`\n", name); DL_ERR("could not read prelink_info_t structure for \"%s\":", name, strerror(errno));
return 0; return true;
} }
if (memcmp(info.tag, "PRE ", 4)) { if (memcmp(info.tag, "PRE ", 4) == 0) {
WARN("`%s` is not a prelinked library\n", name); DL_ERR("prelinked libraries no longer supported: %s", name);
return 0; return true;
} }
return false;
return (unsigned long)info.mmap_addr;
} }
/* verify_elf_header /* verify_elf_header
@ -697,101 +701,106 @@ verify_elf_header(const Elf32_Ehdr* hdr)
return 0; return 0;
} }
struct scoped_fd {
~scoped_fd() {
if (fd != -1) {
close(fd);
}
}
int fd;
};
static soinfo * struct soinfo_ptr {
load_library(const char *name) 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); // Open the file.
int ret, cnt; scoped_fd fd;
unsigned ext_sz; fd.fd = open_library(name);
unsigned req_base; if (fd.fd == -1) {
const char *bname; DL_ERR("library \"%s\" not found", name);
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);
return NULL; return NULL;
} }
/* Read the ELF header first */ // Read the ELF header.
ret = TEMP_FAILURE_RETRY(read(fd, (void*)header, sizeof(header))); Elf32_Ehdr header[1];
int ret = TEMP_FAILURE_RETRY(read(fd.fd, (void*)header, sizeof(header)));
if (ret < 0) { if (ret < 0) {
DL_ERR("%5d can't read file %s: %s", pid, name, strerror(errno)); DL_ERR("can't read file \"%s\": %s", name, strerror(errno));
goto fail; return NULL;
} }
if (ret != (int)sizeof(header)) { if (ret != (int)sizeof(header)) {
DL_ERR("%5d too small to be an ELF executable: %s", pid, name); DL_ERR("too small to be an ELF executable: %s", name);
goto fail; return NULL;
} }
if (verify_elf_header(header) < 0) { if (verify_elf_header(header) < 0) {
DL_ERR("%5d not a valid ELF executable: %s", pid, name); DL_ERR("not a valid ELF executable: %s", name);
goto fail; return NULL;
} }
/* Then read the program header table */ // Read the program header table.
ret = phdr_table_load(fd, header->e_phoff, header->e_phnum, const Elf32_Phdr* phdr_table;
&phdr_mmap, &phdr_size, &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) { if (ret < 0) {
DL_ERR("%5d can't load program header table: %s: %s", pid, DL_ERR("can't load program header table: %s: %s", name, strerror(errno));
name, strerror(errno)); return NULL;
goto fail;
} }
phdr_count = header->e_phnum; size_t phdr_count = header->e_phnum;
/* Get the load extents and the prelinked load address, if any */ // Get the load extents.
ext_sz = phdr_table_get_load_size(phdr_table, phdr_count); 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) { if (ext_sz == 0) {
DL_ERR("%5d no loadable segments in file: %s", pid, name); DL_ERR("no loadable segments in file: %s", name);
goto fail; return NULL;
} }
req_base = (unsigned) is_prelinked(fd, name); // We no longer support pre-linked libraries.
if (req_base == (unsigned)-1) { if (is_prelinked(fd.fd, name)) {
DL_ERR("%5d can't read end of library: %s: %s", pid, name, return NULL;
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);
} }
TRACE("[ %5d - '%s' (%s) wants base=0x%08x sz=0x%08x ]\n", pid, name, // Reserve address space for all loadable segments.
(req_base ? "prelinked" : "not pre-linked"), req_base, ext_sz); void* load_start = NULL;
Elf32_Addr load_size = 0;
/* Now configure the soinfo struct where we'll store all of our data Elf32_Addr load_bias = 0;
* 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 */
ret = phdr_table_reserve_memory(phdr_table, ret = phdr_table_reserve_memory(phdr_table,
phdr_count, phdr_count,
req_base,
&load_start, &load_start,
&load_size, &load_size,
&load_bias); &load_bias);
if (ret < 0) { if (ret < 0) {
DL_ERR("%5d Can't reserve %d bytes from 0x%08x in address space for %s: %s", DL_ERR("can't reserve %d bytes in address space for \"%s\": %s",
pid, ext_sz, req_base, name, strerror(errno)); ext_sz, name, strerror(errno));
goto fail; return NULL;
} }
TRACE("[ %5d allocated memory for %s @ %p (0x%08x) ]\n", 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 */ /* Map all the segments in our address space with default protections */
ret = phdr_table_load_segments(phdr_table, ret = phdr_table_load_segments(phdr_table,
phdr_count, phdr_count,
load_start,
load_size,
load_bias, load_bias,
fd); fd.fd);
if (ret < 0) { if (ret < 0) {
DL_ERR("%5d Can't map loadable segments for %s: %s", DL_ERR("can't map loadable segments for \"%s\": %s",
pid, name, strerror(errno)); name, strerror(errno));
goto fail; return NULL;
} }
/* Unprotect the segments, i.e. make them writable, to allow /* Unprotect the segments, i.e. make them writable, to allow
@ -819,36 +826,30 @@ load_library(const char *name)
phdr_count, phdr_count,
load_bias); load_bias);
if (ret < 0) { if (ret < 0) {
DL_ERR("%5d Can't unprotect loadable segments for %s: %s", DL_ERR("can't unprotect loadable segments for \"%s\": %s",
pid, name, strerror(errno)); name, strerror(errno));
goto fail; return NULL;
} }
si->base = (Elf32_Addr) load_start; soinfo_ptr si(name);
si->size = load_size; if (si.ptr == NULL) {
si->load_bias = load_bias; return NULL;
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;
} }
phdr_table_unload(phdr_mmap, phdr_size); si.ptr->base = (Elf32_Addr) load_start;
close(fd); si.ptr->size = load_size;
return si; si.ptr->load_bias = load_bias;
si.ptr->flags = 0;
fail: si.ptr->entry = 0;
if (si) soinfo_free(si); si.ptr->dynamic = (unsigned *)-1;
if (phdr_mmap != NULL) { si.ptr->phnum = phdr_count;
phdr_table_unload(phdr_mmap, phdr_size); 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 * static soinfo *
@ -891,11 +892,11 @@ soinfo *find_library(const char *name)
for(si = solist; si != 0; si = si->next){ for(si = solist; si != 0; si = si->next){
if(!strcmp(bname, si->name)) { if(!strcmp(bname, si->name)) {
if(si->flags & FLAG_ERROR) { 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; return NULL;
} }
if(si->flags & FLAG_LINKED) return si; 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; return NULL;
} }
} }
@ -908,8 +909,7 @@ soinfo *find_library(const char *name)
} }
/* TODO: /* TODO:
* notify gdb of unload * find a way to decrement libbase
* for non-prelinked libraries, find a way to decrement libbase
*/ */
static void call_destructors(soinfo *si); static void call_destructors(soinfo *si);
unsigned soinfo_unload(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, if (phdr_table_unprotect_gnu_relro(si->phdr, si->phnum,
si->load_bias) < 0) { 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)", "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) { for(d = si->dynamic; *d; d += 2) {
@ -945,8 +945,8 @@ unsigned soinfo_unload(soinfo *si)
soinfo_unload(lsi); soinfo_unload(lsi);
} }
else else
DL_ERR("%5d %s: could not unload dependent library", DL_ERR("\"%s\": could not unload dependent library",
pid, si->name); si->name);
} }
} }
@ -972,12 +972,10 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count)
Elf32_Sym *symtab = si->symtab; Elf32_Sym *symtab = si->symtab;
const char *strtab = si->strtab; const char *strtab = si->strtab;
Elf32_Sym *s; Elf32_Sym *s;
unsigned base;
Elf32_Addr offset; Elf32_Addr offset;
Elf32_Rel *start = rel; 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 type = ELF32_R_TYPE(rel->r_info);
unsigned sym = ELF32_R_SYM(rel->r_info); unsigned sym = ELF32_R_SYM(rel->r_info);
unsigned reloc = (unsigned)(rel->r_offset + si->load_bias); 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.. */ reference.. */
s = &symtab[sym]; s = &symtab[sym];
if (ELF32_ST_BIND(s->st_info) != STB_WEAK) { 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; return -1;
} }
@ -1043,8 +1041,8 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count)
not found in run-time. */ not found in run-time. */
#endif /* ANDROID_ARM_LINKER */ #endif /* ANDROID_ARM_LINKER */
default: default:
DL_ERR("%5d unknown weak reloc type %d @ %p (%d)\n", DL_ERR("unknown weak reloc type %d @ %p (%d)",
pid, type, rel, (int) (rel - start)); type, rel, (int) (rel - start));
return -1; return -1;
} }
} else { } else {
@ -1052,8 +1050,8 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count)
#if 0 #if 0
if((base == 0) && (si->base != 0)){ if((base == 0) && (si->base != 0)){
/* linking from libraries to main image is bad */ /* linking from libraries to main image is bad */
DL_ERR("%5d cannot locate '%s'...", DL_ERR("cannot locate \"%s\"...",
pid, strtab + symtab[sym].st_name); strtab + symtab[sym].st_name);
return -1; return -1;
} }
#endif #endif
@ -1140,8 +1138,8 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count)
#endif /* ANDROID_*_LINKER */ #endif /* ANDROID_*_LINKER */
COUNT_RELOC(RELOC_RELATIVE); COUNT_RELOC(RELOC_RELATIVE);
MARK(rel->r_offset); MARK(rel->r_offset);
if(sym){ if (sym) {
DL_ERR("%5d odd RELATIVE form...", pid); DL_ERR("odd RELATIVE form...", pid);
return -1; return -1;
} }
TRACE_TYPE(RELO, "%5d RELO RELATIVE %08x <- +%08x\n", pid, 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 */ #endif /* ANDROID_ARM_LINKER */
default: default:
DL_ERR("%5d unknown reloc type %d @ %p (%d)", DL_ERR("unknown reloc type %d @ %p (%d)",
pid, type, rel, (int) (rel - start)); type, rel, (int) (rel - start));
return -1; return -1;
} }
} }
@ -1240,7 +1238,7 @@ int mips_relocate_got(struct soinfo *si)
reference.. */ reference.. */
s = &symtab[g]; s = &symtab[g];
if (ELF32_ST_BIND(s->st_info) != STB_WEAK) { 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; return -1;
} }
*got = 0; *got = 0;
@ -1320,9 +1318,8 @@ void soinfo_call_constructors(soinfo *si)
TRACE("[ %5d Done calling preinit_array for '%s' ]\n", pid, si->name); TRACE("[ %5d Done calling preinit_array for '%s' ]\n", pid, si->name);
} else { } else {
if (si->preinit_array) { if (si->preinit_array) {
DL_ERR("%5d Shared library '%s' has a preinit_array table @ 0x%08x." DL_ERR("shared library \"%s\" has a preinit_array table @ 0x%08x. "
" This is INVALID.", pid, si->name, "This is INVALID.", si->name, (unsigned) si->preinit_array);
(unsigned)si->preinit_array);
} }
} }
@ -1332,8 +1329,7 @@ void soinfo_call_constructors(soinfo *si)
if(d[0] == DT_NEEDED){ if(d[0] == DT_NEEDED){
soinfo* lsi = (soinfo *)d[1]; soinfo* lsi = (soinfo *)d[1];
if (!validate_soinfo(lsi)) { if (!validate_soinfo(lsi)) {
DL_ERR("%5d bad DT_NEEDED pointer in %s", DL_ERR("bad DT_NEEDED pointer in \"%s\"", si->name);
pid, si->name);
} else { } else {
soinfo_call_constructors(lsi); soinfo_call_constructors(lsi);
} }
@ -1383,7 +1379,7 @@ static int nullify_closed_stdio (void)
dev_null = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)); dev_null = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
if (dev_null < 0) { if (dev_null < 0) {
DL_ERR("Cannot open /dev/null."); DL_ERR("cannot open /dev/null: %s", strerror(errno));
return -1; return -1;
} }
TRACE("[ %5d Opened /dev/null file-descriptor=%d]\n", pid, dev_null); 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. */ with /dev/null, dup /dev/null to it. */
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
/* If it is /dev/null already, we are done. */ /* If it is /dev/null already, we are done. */
if (i == dev_null) if (i == dev_null) {
continue; continue;
}
TRACE("[ %5d Nullifying stdio file descriptor %d]\n", pid, i); TRACE("[ %5d Nullifying stdio file descriptor %d]\n", pid, i);
/* The man page of fcntl does not say that fcntl(..,F_GETFL) status = TEMP_FAILURE_RETRY(fcntl(i, F_GETFL));
can be interrupted but we do this just to be safe. */
do {
status = fcntl(i, F_GETFL);
} while (status < 0 && errno == EINTR);
/* If file is openned, we are good. */ /* If file is opened, we are good. */
if (status >= 0) if (status != -1) {
continue; continue;
}
/* The only error we allow is that the file descriptor does not /* The only error we allow is that the file descriptor does not
exist, in which case we dup /dev/null to it. */ exist, in which case we dup /dev/null to it. */
if (errno != EBADF) { if (errno != EBADF) {
DL_ERR("nullify_stdio: unhandled error %s", strerror(errno)); DL_ERR("fcntl failed: %s", strerror(errno));
return_value = -1; return_value = -1;
continue; continue;
} }
@ -1417,12 +1411,9 @@ static int nullify_closed_stdio (void)
/* Try dupping /dev/null to this stdio file descriptor and /* Try dupping /dev/null to this stdio file descriptor and
repeat if there is a signal. Note that any errors in closing repeat if there is a signal. Note that any errors in closing
the stdio descriptor are lost. */ the stdio descriptor are lost. */
do { status = TEMP_FAILURE_RETRY(dup2(dev_null, i));
status = dup2(dev_null, i);
} while (status < 0 && errno == EINTR);
if (status < 0) { if (status < 0) {
DL_ERR("nullify_stdio: dup2 error %s", strerror(errno)); DL_ERR("dup2 failed: %s", strerror(errno));
return_value = -1; return_value = -1;
continue; 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 is not one of the stdio file descriptors, close it. */
if (dev_null > 2) { if (dev_null > 2) {
TRACE("[ %5d Closing /dev/null file-descriptor=%d]\n", pid, dev_null); TRACE("[ %5d Closing /dev/null file-descriptor=%d]\n", pid, dev_null);
do { status = TEMP_FAILURE_RETRY(close(dev_null));
status = close(dev_null); if (status == -1) {
} while (status < 0 && errno == EINTR); DL_ERR("close failed: %s", strerror(errno));
if (status < 0) {
DL_ERR("nullify_stdio: close error %s", strerror(errno));
return_value = -1; 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); si->dynamic = phdr_table_get_dynamic_section(phdr, phnum, base);
if (si->dynamic == NULL) { if (si->dynamic == NULL) {
if (!relocating_linker) { if (!relocating_linker) {
DL_ERR("%5d missing PT_DYNAMIC?!", pid); DL_ERR("missing PT_DYNAMIC?!");
} }
goto fail; goto fail;
} else { } 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 /* We can't call DL_ERR if the linker's relocations haven't
* been performed yet */ * been performed yet */
if (!relocating_linker) { if (!relocating_linker) {
DL_ERR("%5d Can't unprotect segments for %s: %s", DL_ERR("can't unprotect segments for \"%s\": %s",
pid, si->name, strerror(errno)); si->name, strerror(errno));
} }
goto fail; goto fail;
} }
@ -1537,7 +1525,7 @@ static int soinfo_link_image(soinfo *si, unsigned wr_offset)
#endif #endif
break; break;
case DT_RELA: case DT_RELA:
DL_ERR("%5d DT_RELA not supported", pid); DL_ERR("DT_RELA not supported");
goto fail; goto fail;
case DT_INIT: case DT_INIT:
si->init_func = (void (*)(void))(base + *d); 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); pid, si->base, si->strtab, si->symtab);
if((si->strtab == 0) || (si->symtab == 0)) { if((si->strtab == 0) || (si->symtab == 0)) {
DL_ERR("%5d missing essential tables", pid); DL_ERR("missing essential tables");
goto fail; goto fail;
} }
@ -1645,8 +1633,8 @@ static int soinfo_link_image(soinfo *si, unsigned wr_offset)
soinfo *lsi = find_library(ldpreload_names[i]); soinfo *lsi = find_library(ldpreload_names[i]);
if(lsi == 0) { if(lsi == 0) {
strlcpy(tmp_err_buf, linker_get_error(), sizeof(tmp_err_buf)); strlcpy(tmp_err_buf, linker_get_error(), sizeof(tmp_err_buf));
DL_ERR("%5d could not load needed library '%s' for '%s' (%s)", DL_ERR("could not load library \"%s\" needed by \"%s\"; caused by %s",
pid, ldpreload_names[i], si->name, tmp_err_buf); ldpreload_names[i], si->name, tmp_err_buf);
goto fail; goto fail;
} }
lsi->refcount++; lsi->refcount++;
@ -1660,8 +1648,8 @@ static int soinfo_link_image(soinfo *si, unsigned wr_offset)
soinfo *lsi = find_library(si->strtab + d[1]); soinfo *lsi = find_library(si->strtab + d[1]);
if(lsi == 0) { if(lsi == 0) {
strlcpy(tmp_err_buf, linker_get_error(), sizeof(tmp_err_buf)); strlcpy(tmp_err_buf, linker_get_error(), sizeof(tmp_err_buf));
DL_ERR("%5d could not load needed library '%s' for '%s' (%s)", DL_ERR("could not load library \"%s\" needed by \"%s\"; caused by %s",
pid, si->strtab + d[1], si->name, tmp_err_buf); si->strtab + d[1], si->name, tmp_err_buf);
goto fail; goto fail;
} }
/* Save the soinfo of the loaded DT_NEEDED library in the payload /* 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 /* All relocations are done, we can protect our segments back to
* read-only. */ * read-only. */
if (phdr_table_protect_segments(si->phdr, si->phnum, si->load_bias) < 0) { if (phdr_table_protect_segments(si->phdr, si->phnum, si->load_bias) < 0) {
DL_ERR("%5d Can't protect segments for %s: %s", DL_ERR("can't protect segments for \"%s\": %s",
pid, si->name, strerror(errno)); si->name, strerror(errno));
goto fail; goto fail;
} }
/* We can also turn on GNU RELRO protection */ /* We can also turn on GNU RELRO protection */
if (phdr_table_protect_gnu_relro(si->phdr, si->phnum, si->load_bias) < 0) { 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", DL_ERR("can't enable GNU RELRO protection for \"%s\": %s",
pid, si->name, strerror(errno)); si->name, strerror(errno));
goto fail; 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 ftp://ftp.freebsd.org/pub/FreeBSD/CERT/advisories/FreeBSD-SA-02:23.stdio.asc
*/ */
if (program_is_setuid) if (program_is_setuid) {
nullify_closed_stdio (); nullify_closed_stdio();
}
notify_gdb_of_load(si); notify_gdb_of_load(si);
return 0; return 0;
@ -1728,53 +1717,43 @@ fail:
return -1; 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; if (path == NULL) {
char *ldpaths_bufp = ldpaths_buf; return;
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;
} }
/* Forget the last path if we had to truncate; this occurs if the 2nd to size_t len = strlcpy(buf, path, buf_size);
* 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;
}
}
static void parse_preloads(const char *path, char *delim) size_t i = 0;
{ char* buf_p = buf;
size_t len; while (i < max_count && (array[i] = strsep(&buf_p, delimiters))) {
char *ldpreloads_bufp = ldpreloads_buf; if (*array[i] != '\0') {
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') {
++i; ++i;
} }
} }
/* Forget the last path if we had to truncate; this occurs if the 2nd to // 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). */ // last char isn't '\0' (i.e. wasn't originally a delimiter).
if (i > 0 && len >= sizeof(ldpreloads_buf) && if (i > 0 && len >= buf_size && buf[buf_size - 2] != '\0') {
ldpreloads_buf[sizeof(ldpreloads_buf) - 2] != '\0') { array[i - 1] = NULL;
ldpreload_names[i - 1] = NULL;
} else { } 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 * This code is called after the linker has linked itself and
* fixed it's own GOT. It is safe to make references to externs * fixed it's own GOT. It is safe to make references to externs
@ -1917,13 +1896,9 @@ sanitize:
si->dynamic = (unsigned *)-1; si->dynamic = (unsigned *)-1;
si->refcount = 1; si->refcount = 1;
/* Use LD_LIBRARY_PATH if we aren't setuid/setgid */ // Use LD_LIBRARY_PATH and LD_PRELOAD (but only if we aren't setuid/setgid).
if (ldpath_env) parse_LD_LIBRARY_PATH(ldpath_env);
parse_library_path(ldpath_env, ":"); parse_LD_PRELOAD(ldpreload_env);
if (ldpreload_env) {
parse_preloads(ldpreload_env, " :");
}
if(soinfo_link_image(si, 0)) { if(soinfo_link_image(si, 0)) {
char errmsg[] = "CANNOT LINK EXECUTABLE\n"; 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 * relocations, any attempt to reference an extern variable, extern
* function, or other GOT reference will generate a segfault. * 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); unsigned linker_addr = find_linker_base(elfdata);
Elf32_Ehdr *elf_hdr = (Elf32_Ehdr *) linker_addr; Elf32_Ehdr *elf_hdr = (Elf32_Ehdr *) linker_addr;
Elf32_Phdr *phdr = Elf32_Phdr *phdr =

View File

@ -34,6 +34,10 @@
#include <elf.h> #include <elf.h>
#include <sys/exec_elf.h> #include <sys/exec_elf.h>
#ifdef __cplusplus
extern "C" {
#endif
#undef PAGE_MASK #undef PAGE_MASK
#undef PAGE_SIZE #undef PAGE_SIZE
#define PAGE_SIZE 4096 #define PAGE_SIZE 4096
@ -106,7 +110,7 @@ typedef struct soinfo soinfo;
struct soinfo struct soinfo
{ {
const char name[SOINFO_NAME_LEN]; char name[SOINFO_NAME_LEN];
const Elf32_Phdr *phdr; const Elf32_Phdr *phdr;
int phnum; int phnum;
unsigned entry; 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 *); int dl_iterate_phdr(int (*cb)(struct dl_phdr_info *, size_t, void *), void *);
#endif #endif
#ifdef __cplusplus
};
#endif
#endif #endif

View File

@ -28,6 +28,10 @@
#ifndef LINKER_ENVIRON_H #ifndef LINKER_ENVIRON_H
#define LINKER_ENVIRON_H #define LINKER_ENVIRON_H
#ifdef __cplusplus
extern "C" {
#endif
/* Call this function before anything else. 'vecs' must be the pointer /* Call this function before anything else. 'vecs' must be the pointer
* to the environment block in the ELF data block. The function returns * to the environment block in the ELF data block. The function returns
* the start of the aux vectors after the env block. * 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. */ * after this function. */
extern const char* linker_env_get(const char* name); 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. */ * running setuid programs. */
extern void linker_env_secure(void); extern void linker_env_secure(void);
#ifdef __cplusplus
};
#endif
#endif /* LINKER_ENVIRON_H */ #endif /* LINKER_ENVIRON_H */

View File

@ -312,7 +312,7 @@ static int log_vprint(int prio, const char *tag, const char *fmt, va_list args)
#else /* !CUSTOM_LOG_VPRINT */ #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 */ #endif /* !CUSTOM_LOG_VPRINT */

View File

@ -31,6 +31,10 @@
#include <stdarg.h> #include <stdarg.h>
#include <stddef.h> #include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Formatting routines for the dynamic linker's debug traces */ /* Formatting routines for the dynamic linker's debug traces */
/* We want to avoid dragging the whole C library fprintf() */ /* We want to avoid dragging the whole C library fprintf() */
/* implementation into the dynamic linker since this creates */ /* implementation into the dynamic linker since this creates */
@ -38,4 +42,8 @@
int format_buffer(char *buffer, size_t bufsize, const char *format, ...); int format_buffer(char *buffer, size_t bufsize, const char *format, ...);
#ifdef __cplusplus
};
#endif
#endif /* _LINKER_FORMAT_H */ #endif /* _LINKER_FORMAT_H */

View File

@ -180,24 +180,25 @@ void phdr_table_unload(void* phdr_mmap, Elf32_Addr phdr_memsize)
* This returns 0 if there are no loadable segments. * This returns 0 if there are no loadable segments.
*/ */
Elf32_Addr phdr_table_get_load_size(const Elf32_Phdr* phdr_table, 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 min_vaddr = 0xFFFFFFFFU;
Elf32_Addr max_vaddr = 0x00000000U; Elf32_Addr max_vaddr = 0x00000000U;
for (nn = 0; nn < phdr_count; nn++) { for (size_t i = 0; i < phdr_count; ++i) {
const Elf32_Phdr* phdr = &phdr_table[nn]; const Elf32_Phdr* phdr = &phdr_table[i];
if (phdr->p_type != PT_LOAD) if (phdr->p_type != PT_LOAD) {
continue; continue;
}
if (phdr->p_vaddr < min_vaddr) if (phdr->p_vaddr < min_vaddr) {
min_vaddr = phdr->p_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; max_vaddr = phdr->p_vaddr + phdr->p_memsz;
}
} }
if (min_vaddr > max_vaddr) { if (min_vaddr > max_vaddr) {
@ -217,8 +218,6 @@ Elf32_Addr phdr_table_get_load_size(const Elf32_Phdr* phdr_table,
* Input: * Input:
* phdr_table -> program header table * phdr_table -> program header table
* phdr_count -> number of entries in the tables * phdr_count -> number of entries in the tables
* required_base -> for prelinked libraries, mandatory load address
* of the first loadable segment. 0 otherwise.
* Output: * Output:
* load_start -> first page of reserved address space range * load_start -> first page of reserved address space range
* load_size -> size in bytes 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 int
phdr_table_reserve_memory(const Elf32_Phdr* phdr_table, phdr_table_reserve_memory(const Elf32_Phdr* phdr_table,
int phdr_count, size_t phdr_count,
Elf32_Addr required_base, void** load_start,
void** load_start, Elf32_Addr* load_size,
Elf32_Addr* load_size, Elf32_Addr* load_bias)
Elf32_Addr* load_bias)
{ {
Elf32_Addr size = phdr_table_get_load_size(phdr_table, phdr_count); Elf32_Addr size = phdr_table_get_load_size(phdr_table, phdr_count);
void* start;
int nn, mmap_flags;
if (size == 0) { if (size == 0) {
errno = EINVAL; errno = EINVAL;
return -1; return -1;
} }
mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS; int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
if (required_base != 0) void* start = mmap(NULL, size, PROT_NONE, mmap_flags, -1, 0);
mmap_flags |= MAP_FIXED;
start = mmap((void*)required_base, size, PROT_NONE, mmap_flags, -1, 0);
if (start == MAP_FAILED) { if (start == MAP_FAILED) {
return -1; return -1;
} }
@ -257,8 +249,8 @@ phdr_table_reserve_memory(const Elf32_Phdr* phdr_table,
*load_size = size; *load_size = size;
*load_bias = 0; *load_bias = 0;
for (nn = 0; nn < phdr_count; nn++) { for (size_t i = 0; i < phdr_count; ++i) {
const Elf32_Phdr* phdr = &phdr_table[nn]; const Elf32_Phdr* phdr = &phdr_table[i];
if (phdr->p_type == PT_LOAD) { if (phdr->p_type == PT_LOAD) {
*load_bias = (Elf32_Addr)start - PAGE_START(phdr->p_vaddr); *load_bias = (Elf32_Addr)start - PAGE_START(phdr->p_vaddr);
break; break;
@ -274,8 +266,6 @@ phdr_table_reserve_memory(const Elf32_Phdr* phdr_table,
* Input: * Input:
* phdr_table -> program header table * phdr_table -> program header table
* phdr_count -> number of entries in the 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. * load_bias -> load offset.
* fd -> input file descriptor. * fd -> input file descriptor.
* *
@ -285,8 +275,6 @@ phdr_table_reserve_memory(const Elf32_Phdr* phdr_table,
int int
phdr_table_load_segments(const Elf32_Phdr* phdr_table, phdr_table_load_segments(const Elf32_Phdr* phdr_table,
int phdr_count, int phdr_count,
void* load_start,
Elf32_Addr load_size,
Elf32_Addr load_bias, Elf32_Addr load_bias,
int fd) int fd)
{ {

View File

@ -37,6 +37,10 @@
#include "linker.h" #include "linker.h"
#ifdef __cplusplus
extern "C" {
#endif
/* See linker_phdr.c for all usage documentation */ /* See linker_phdr.c for all usage documentation */
int int
@ -52,21 +56,18 @@ phdr_table_unload(void* phdr_mmap, Elf32_Addr phdr_memsize);
Elf32_Addr Elf32_Addr
phdr_table_get_load_size(const Elf32_Phdr* phdr_table, phdr_table_get_load_size(const Elf32_Phdr* phdr_table,
int phdr_count); size_t phdr_count);
int int
phdr_table_reserve_memory(const Elf32_Phdr* phdr_table, phdr_table_reserve_memory(const Elf32_Phdr* phdr_table,
int phdr_count, size_t phdr_count,
Elf32_Addr required_base, void** load_start,
void** load_start, Elf32_Addr* load_size,
Elf32_Addr* load_size, Elf32_Addr* load_bias);
Elf32_Addr* load_bias);
int int
phdr_table_load_segments(const Elf32_Phdr* phdr_table, phdr_table_load_segments(const Elf32_Phdr* phdr_table,
int phdr_count, int phdr_count,
void* load_start,
Elf32_Addr load_size,
Elf32_Addr load_bias, Elf32_Addr load_bias,
int fd); int fd);
@ -109,4 +110,8 @@ phdr_table_get_dynamic_section(const Elf32_Phdr* phdr_table,
int phdr_count, int phdr_count,
Elf32_Addr load_bias); Elf32_Addr load_bias);
#ifdef __cplusplus
};
#endif
#endif /* LINKER_PHDR_H */ #endif /* LINKER_PHDR_H */