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 */
};
#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 */

View file

@ -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 <bionic_tls.h>
# 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 <private/bionic_tls.h>, 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

View file

@ -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-<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:
-----------------------------------------

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

View file

@ -26,25 +26,23 @@
* SUCH DAMAGE.
*/
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/auxvec.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.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/mman.h>
#include <sys/stat.h>
#include <unistd.h>
/* special private C library header - see Android.mk */
#include <bionic_tls.h>
// Private C library headers.
#include <private/bionic_tls.h>
#include <private/logd.h>
#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 =

View file

@ -34,6 +34,10 @@
#include <elf.h>
#include <sys/exec_elf.h>
#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

View file

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

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

View file

@ -31,6 +31,10 @@
#include <stdarg.h>
#include <stddef.h>
#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 */

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.
*/
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)
{

View file

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