summaryrefslogtreecommitdiffstats
path: root/linker
diff options
context:
space:
mode:
Diffstat (limited to 'linker')
-rw-r--r--linker/Android.mk18
-rw-r--r--linker/README.TXT29
-rw-r--r--linker/debugger.c17
-rw-r--r--linker/dlfcn.c3
-rw-r--r--linker/linker.c156
-rw-r--r--linker/linker_debug.h36
-rw-r--r--linker/linker_format.c703
-rw-r--r--linker/linker_format.h41
8 files changed, 929 insertions, 74 deletions
diff --git a/linker/Android.mk b/linker/Android.mk
index 6c26eb3..4647c8f 100644
--- a/linker/Android.mk
+++ b/linker/Android.mk
@@ -4,6 +4,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
arch/$(TARGET_ARCH)/begin.S \
linker.c \
+ linker_format.c \
rt.c \
dlfcn.c \
debugger.c \
@@ -13,7 +14,12 @@ ifeq ($(TARGET_ARCH),sh)
# SH-4A series virtual address range from 0x00000000 to 0x7FFFFFFF.
LINKER_TEXT_BASE := 0x70000100
else
-LINKER_TEXT_BASE := 0xB0000100
+# This is aligned to 4K page boundary so that both GNU ld and gold work. Gold
+# actually produces a correct binary with starting address 0xB0000100 but the
+# extra objcopy step to rename symbols causes the resulting binary to be misaligned
+# and unloadable. Increasing the alignment adds an extra 3840 bytes in padding
+# but switching to gold saves about 1M of space.
+LINKER_TEXT_BASE := 0xB0001000
endif
# The maximum size set aside for the linker, from
@@ -26,8 +32,16 @@ LOCAL_CFLAGS += -DPRELINK
LOCAL_CFLAGS += -DLINKER_TEXT_BASE=$(LINKER_TEXT_BASE)
LOCAL_CFLAGS += -DLINKER_AREA_SIZE=$(LINKER_AREA_SIZE)
+# Set LINKER_DEBUG to either 1 or 0
+#
+LOCAL_CFLAGS += -DLINKER_DEBUG=0
+
# we need to access the Bionic private header <bionic_tls.h>
-# in the linker
+# in the linker; duplicate the HAVE_ARM_TLS_REGISTER definition
+# from the libc build
+ifeq ($(TARGET_ARCH)-$(ARCH_ARM_HAVE_TLS_REGISTER),arm-true)
+ LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER
+endif
LOCAL_CFLAGS += -I$(LOCAL_PATH)/../libc/private
ifeq ($(TARGET_ARCH),arm)
diff --git a/linker/README.TXT b/linker/README.TXT
index 4fff14e..052a65b 100644
--- a/linker/README.TXT
+++ b/linker/README.TXT
@@ -112,3 +112,32 @@ On x86, the lists of constructors and destructors are placed in special
sections named ".ctors" and ".dtors", and the DT_INIT / DT_FINI functions
are in charge of calling them explicitely.
+
+Debugging:
+----------
+
+It is possible to enable debug output in the dynamic linker. To do so,
+follow these steps:
+
+1/ Modify the line in Android.mk that says:
+
+ LOCAL_CFLAGS += -DLINKER_DEBUG=0
+
+ Into the following:
+
+ LOCAL_CFLAGS += -DLINKER_DEBUG=1
+
+2/ Force-rebuild the dynamic linker:
+
+ cd bionic/linker
+ mm -B
+
+3/ Rebuild a new system image.
+
+You can increase the verbosity of debug traces by defining the DEBUG
+environment variable to a numeric value from 0 to 2. This will only
+affect new processes being launched.
+
+By default, traces are sent to logcat, with the "linker" tag. You can
+change this to go to stdout instead by setting the definition of
+LINKER_DEBUG_TO_LOG to 0 in "linker_debug.h"
diff --git a/linker/debugger.c b/linker/debugger.c
index 5bb065c..1bd3cc8 100644
--- a/linker/debugger.c
+++ b/linker/debugger.c
@@ -32,6 +32,7 @@
#include <ctype.h>
#include <signal.h>
#include <sys/mman.h>
+#include <errno.h>
#include "linker.h"
@@ -40,6 +41,11 @@
void notify_gdb_of_libraries();
+#define RETRY_ON_EINTR(ret,cond) \
+ do { \
+ ret = (cond); \
+ } while (ret < 0 && errno == EINTR)
+
void debugger_signal_handler(int n)
{
unsigned tid;
@@ -58,10 +64,15 @@ void debugger_signal_handler(int n)
* is paranoid and will verify that we are giving a tid
* that's actually in our process
*/
- write(s, &tid, sizeof(unsigned));
+ int ret;
- read(s, &tid, 1);
- notify_gdb_of_libraries();
+ RETRY_ON_EINTR(ret, write(s, &tid, sizeof(unsigned)));
+ if (ret == sizeof(unsigned)) {
+ /* if the write failed, there is no point to read on
+ * the file descriptor. */
+ RETRY_ON_EINTR(ret, read(s, &tid, 1));
+ notify_gdb_of_libraries();
+ }
close(s);
}
diff --git a/linker/dlfcn.c b/linker/dlfcn.c
index 30f5f4c..a36b42c 100644
--- a/linker/dlfcn.c
+++ b/linker/dlfcn.c
@@ -17,6 +17,7 @@
#include <pthread.h>
#include <stdio.h>
#include "linker.h"
+#include "linker_format.h"
/* This file hijacks the symbols stubbed out in libdl.so. */
@@ -45,7 +46,7 @@ static pthread_mutex_t dl_lock = PTHREAD_MUTEX_INITIALIZER;
static void set_dlerror(int err)
{
- snprintf(dl_err_buf, sizeof(dl_err_buf), "%s: %s", dl_errors[err],
+ format_buffer(dl_err_buf, sizeof(dl_err_buf), "%s: %s", dl_errors[err],
linker_get_error());
dl_err_str = (const char *)&dl_err_buf[0];
};
diff --git a/linker/linker.c b/linker/linker.c
index 451e96c..a1f4fff 100644
--- a/linker/linker.c
+++ b/linker/linker.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2008, 2009 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -48,6 +48,7 @@
#include "linker.h"
#include "linker_debug.h"
+#include "linker_format.h"
#include "ba.h"
@@ -68,7 +69,6 @@
*
* open issues / todo:
*
- * - should we do anything special for STB_WEAK symbols?
* - are we doing everything we should for ARM_COPY relocations?
* - cleaner error reporting
* - after linking, set as much stuff as possible to READONLY
@@ -92,15 +92,15 @@ static soinfo *somain; /* main process, always the one after libdl_info */
#endif
-/* Set up for the buddy allocator managing the prelinked libraries. */
-static struct ba_bits ba_prelink_bitmap[(LIBLAST - LIBBASE) / LIBINC];
-static struct ba ba_prelink = {
+/* Set up for the buddy allocator managing the non-prelinked libraries. */
+static struct ba_bits ba_nonprelink_bitmap[(LIBLAST - LIBBASE) / LIBINC];
+static struct ba ba_nonprelink = {
.base = LIBBASE,
.size = LIBLAST - LIBBASE,
.min_alloc = LIBINC,
/* max_order will be determined automatically */
- .bitmap = ba_prelink_bitmap,
- .num_entries = sizeof(ba_prelink_bitmap)/sizeof(ba_prelink_bitmap[0]),
+ .bitmap = ba_nonprelink_bitmap,
+ .num_entries = sizeof(ba_nonprelink_bitmap)/sizeof(ba_nonprelink_bitmap[0]),
};
static inline int validate_soinfo(soinfo *si)
@@ -143,7 +143,7 @@ static char tmp_err_buf[768];
static char __linker_dl_err_buf[768];
#define DL_ERR(fmt, x...) \
do { \
- snprintf(__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); \
ERROR(fmt "\n", ##x); \
} while(0)
@@ -258,7 +258,7 @@ static soinfo *alloc_info(const char *name)
if(strlen(name) >= SOINFO_NAME_LEN) {
DL_ERR("%5d library name %s too long", pid, name);
- return 0;
+ return NULL;
}
/* The freelist is populated when we call free_info(), which in turn is
@@ -406,20 +406,20 @@ static Elf32_Sym *_elf_lookup(soinfo *si, unsigned hash, const char *name)
s = symtab + n;
if(strcmp(strtab + s->st_name, name)) continue;
- /* only concern ourselves with global symbols */
+ /* only concern ourselves with global and weak symbol definitions */
switch(ELF32_ST_BIND(s->st_info)){
case STB_GLOBAL:
+ case STB_WEAK:
/* no section == undefined */
if(s->st_shndx == 0) continue;
- case STB_WEAK:
TRACE_TYPE(LOOKUP, "%5d FOUND %s in %s (%08x) %d\n", pid,
name, si->name, s->st_value, s->st_size);
return s;
}
}
- return 0;
+ return NULL;
}
static unsigned elfhash(const char *_name)
@@ -437,25 +437,23 @@ static unsigned elfhash(const char *_name)
}
static Elf32_Sym *
-_do_lookup_in_so(soinfo *si, const char *name, unsigned *elf_hash)
-{
- if (*elf_hash == 0)
- *elf_hash = elfhash(name);
- return _elf_lookup (si, *elf_hash, name);
-}
-
-static Elf32_Sym *
_do_lookup(soinfo *si, const char *name, unsigned *base)
{
- unsigned elf_hash = 0;
+ unsigned elf_hash = elfhash(name);
Elf32_Sym *s;
unsigned *d;
soinfo *lsi = si;
/* Look for symbols in the local scope first (the object who is
* searching). This happens with C++ templates on i386 for some
- * reason. */
- s = _do_lookup_in_so(si, name, &elf_hash);
+ * reason.
+ *
+ * Notes on weak symbols:
+ * The ELF specs are ambigious about treatment of weak definitions in
+ * dynamic linking. Some systems return the first definition found
+ * and some the first non-weak definition. This is system dependent.
+ * Here we return the first definition found for simplicity. */
+ s = _elf_lookup(si, elf_hash, name);
if(s != NULL)
goto done;
@@ -465,12 +463,12 @@ _do_lookup(soinfo *si, const char *name, unsigned *base)
if (!validate_soinfo(lsi)) {
DL_ERR("%5d bad DT_NEEDED pointer in %s",
pid, si->name);
- return 0;
+ return NULL;
}
DEBUG("%5d %s: looking up %s in %s\n",
pid, si->name, name, lsi->name);
- s = _do_lookup_in_so(lsi, name, &elf_hash);
+ s = _elf_lookup(lsi, elf_hash, name);
if ((s != NULL) && (s->st_shndx != SHN_UNDEF))
goto done;
}
@@ -485,7 +483,7 @@ _do_lookup(soinfo *si, const char *name, unsigned *base)
lsi = somain;
DEBUG("%5d %s: looking up %s in executable %s\n",
pid, si->name, name, lsi->name);
- s = _do_lookup_in_so(lsi, name, &elf_hash);
+ s = _elf_lookup(lsi, elf_hash, name);
}
#endif
@@ -498,7 +496,7 @@ done:
return s;
}
- return 0;
+ return NULL;
}
/* This is used by dl_sym(). It performs symbol lookup only within the
@@ -506,15 +504,14 @@ done:
*/
Elf32_Sym *lookup_in_library(soinfo *si, const char *name)
{
- unsigned unused = 0;
- return _do_lookup_in_so(si, name, &unused);
+ return _elf_lookup(si, elfhash(name), name);
}
/* This is used by dl_sym(). It performs a global symbol lookup.
*/
Elf32_Sym *lookup(const char *name, soinfo **found, soinfo *start)
{
- unsigned elf_hash = 0;
+ unsigned elf_hash = elfhash(name);
Elf32_Sym *s = NULL;
soinfo *si;
@@ -526,7 +523,7 @@ Elf32_Sym *lookup(const char *name, soinfo **found, soinfo *start)
{
if(si->flags & FLAG_ERROR)
continue;
- s = _do_lookup_in_so(si, name, &elf_hash);
+ s = _elf_lookup(si, elf_hash, name);
if (s != NULL) {
*found = si;
break;
@@ -539,7 +536,7 @@ Elf32_Sym *lookup(const char *name, soinfo **found, soinfo *start)
return s;
}
- return 0;
+ return NULL;
}
soinfo *find_containing_library(void *addr)
@@ -626,7 +623,7 @@ static int open_library(const char *name)
return fd;
for (path = ldpaths; *path; path++) {
- n = snprintf(buf, sizeof(buf), "%s/%s", *path, name);
+ n = format_buffer(buf, sizeof(buf), "%s/%s", *path, name);
if (n < 0 || n >= (int)sizeof(buf)) {
WARN("Ignoring very long library path: %s/%s\n", *path, name);
continue;
@@ -635,7 +632,7 @@ static int open_library(const char *name)
return fd;
}
for (path = sopaths; *path; path++) {
- n = snprintf(buf, sizeof(buf), "%s/%s", *path, name);
+ n = format_buffer(buf, sizeof(buf), "%s/%s", *path, name);
if (n < 0 || n >= (int)sizeof(buf)) {
WARN("Ignoring very long library path: %s/%s\n", *path, name);
continue;
@@ -834,14 +831,14 @@ alloc_mem_region(soinfo *si)
for it from the buddy allocator, which manages the area between
LIBBASE and LIBLAST.
*/
- si->ba_index = ba_allocate(&ba_prelink, si->size);
+ si->ba_index = ba_allocate(&ba_nonprelink, si->size);
if(si->ba_index >= 0) {
- si->base = ba_start_addr(&ba_prelink, si->ba_index);
+ si->base = ba_start_addr(&ba_nonprelink, si->ba_index);
PRINT("%5d mapping library '%s' at %08x (index %d) " \
"through buddy allocator.\n",
pid, si->name, si->base, si->ba_index);
if (reserve_mem_region(si) < 0) {
- ba_free(&ba_prelink, si->ba_index);
+ ba_free(&ba_nonprelink, si->ba_index);
si->ba_index = -1;
si->base = 0;
goto err;
@@ -1137,7 +1134,7 @@ load_library(const char *name)
/* Now actually load the library's segments into right places in memory */
if (load_segments(fd, &__header[0], si) < 0) {
if (si->ba_index >= 0) {
- ba_free(&ba_prelink, si->ba_index);
+ ba_free(&ba_nonprelink, si->ba_index);
si->ba_index = -1;
}
goto fail;
@@ -1240,7 +1237,7 @@ unsigned unload_library(soinfo *si)
PRINT("%5d releasing library '%s' address space at %08x "\
"through buddy allocator.\n",
pid, si->name, si->base);
- ba_free(&ba_prelink, si->ba_index);
+ ba_free(&ba_nonprelink, si->ba_index);
}
notify_gdb_of_unload(si);
free_info(si);
@@ -1279,10 +1276,64 @@ static int reloc_library(soinfo *si, Elf32_Rel *rel, unsigned count)
if(sym != 0) {
sym_name = (char *)(strtab + symtab[sym].st_name);
s = _do_lookup(si, sym_name, &base);
- if(s == 0) {
- DL_ERR("%5d cannot locate '%s'...", pid, sym_name);
- return -1;
- }
+ if(s == NULL) {
+ /* We only allow an undefined symbol if this is a weak
+ reference.. */
+ s = &symtab[sym];
+ if (ELF32_ST_BIND(s->st_info) != STB_WEAK) {
+ DL_ERR("%5d cannot locate '%s'...\n", pid, sym_name);
+ return -1;
+ }
+
+ /* IHI0044C AAELF 4.5.1.1:
+
+ Libraries are not searched to resolve weak references.
+ It is not an error for a weak reference to remain
+ unsatisfied.
+
+ During linking, the value of an undefined weak reference is:
+ - Zero if the relocation type is absolute
+ - The address of the place if the relocation is pc-relative
+ - The address of nominial base address if the relocation
+ type is base-relative.
+ */
+
+ switch (type) {
+#if defined(ANDROID_ARM_LINKER)
+ case R_ARM_JUMP_SLOT:
+ case R_ARM_GLOB_DAT:
+ case R_ARM_ABS32:
+ case R_ARM_RELATIVE: /* Don't care. */
+ case R_ARM_NONE: /* Don't care. */
+#elif defined(ANDROID_X86_LINKER)
+ case R_386_JUMP_SLOT:
+ case R_386_GLOB_DAT:
+ case R_386_32:
+ case R_386_RELATIVE: /* Dont' care. */
+#endif /* ANDROID_*_LINKER */
+ /* sym_addr was initialized to be zero above or relocation
+ code below does not care about value of sym_addr.
+ No need to do anything. */
+ break;
+
+#if defined(ANDROID_X86_LINKER)
+ case R_386_PC32:
+ sym_addr = reloc;
+ break;
+#endif /* ANDROID_X86_LINKER */
+
+#if defined(ANDROID_ARM_LINKER)
+ case R_ARM_COPY:
+ /* Fall through. Can't really copy if weak symbol is
+ 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));
+ return -1;
+ }
+ } else {
+ /* We got a definition. */
#if 0
if((base == 0) && (si->base != 0)){
/* linking from libraries to main image is bad */
@@ -1291,20 +1342,11 @@ static int reloc_library(soinfo *si, Elf32_Rel *rel, unsigned count)
return -1;
}
#endif
- // st_shndx==SHN_UNDEF means an undefined symbol.
- // st_value should be 0 then, except that the low bit of st_value is
- // used to indicate whether the symbol points to an ARM or thumb function,
- // and should be ignored in the following check.
- if ((s->st_shndx == SHN_UNDEF) && ((s->st_value & ~1) != 0)) {
- DL_ERR("%5d In '%s', symbol=%s shndx=%d && value=0x%08x. We do not "
- "handle this yet", pid, si->name, sym_name, s->st_shndx,
- s->st_value);
- return -1;
- }
- sym_addr = (unsigned)(s->st_value + base);
+ sym_addr = (unsigned)(s->st_value + base);
+ }
COUNT_RELOC(RELOC_SYMBOL);
} else {
- s = 0;
+ s = NULL;
}
/* TODO: This is ugly. Split up the relocations by arch into
@@ -1954,7 +1996,7 @@ static int link_image(soinfo *si, unsigned wr_offset)
return 0;
fail:
- ERROR("failed to link %s\n", si->name);
+ DL_ERR("failed to link %s\n", si->name);
si->flags |= FLAG_ERROR;
return -1;
}
@@ -2084,7 +2126,7 @@ unsigned __linker_init(unsigned **elfdata)
vecs += 2;
}
- ba_init(&ba_prelink);
+ ba_init(&ba_nonprelink);
si->base = 0;
si->dynamic = (unsigned *)-1;
diff --git a/linker/linker_debug.h b/linker/linker_debug.h
index 3f4fc4c..3f08303 100644
--- a/linker/linker_debug.h
+++ b/linker/linker_debug.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2008-2010 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -31,12 +31,17 @@
#include <stdio.h>
-/* WARNING: For linker debugging only.. Be careful not to leave any of
- * this on when submitting back to repository */
-#define LINKER_DEBUG 0
-#define TRACE_DEBUG 0
-#define DO_TRACE_LOOKUP 0
-#define DO_TRACE_RELO 0
+#ifndef LINKER_DEBUG
+#error LINKER_DEBUG should be defined to either 1 or 0 in Android.mk
+#endif
+
+/* set LINKER_DEBUG_TO_LOG to 1 to send the logs to logcat,
+ * or 0 to use stdout instead.
+ */
+#define LINKER_DEBUG_TO_LOG 1
+#define TRACE_DEBUG 1
+#define DO_TRACE_LOOKUP 1
+#define DO_TRACE_RELO 1
#define TIMING 0
#define STATS 0
#define COUNT_PAGES 0
@@ -59,12 +64,21 @@
* corruption when the linker uses printf().
*/
#if LINKER_DEBUG
+#include "linker_format.h"
extern int debug_verbosity;
-#warning "*** LINKER IS USING printf(); DO NOT CHECK THIS IN ***"
-#define _PRINTVF(v,f,x...) \
- do { \
- (debug_verbosity > (v)) && (printf(x), ((f) && fflush(stdout))); \
+#if LINKER_DEBUG_TO_LOG
+extern int format_log(int, const char *, const char *, ...);
+#define _PRINTVF(v,f,x...) \
+ do { \
+ if (debug_verbosity > (v)) format_log(5-(v),"linker",x); \
+ } while (0)
+#else /* !LINKER_DEBUG_TO_LOG */
+extern int format_fd(int, const char *, ...);
+#define _PRINTVF(v,f,x...) \
+ do { \
+ if (debug_verbosity > (v)) format_fd(1, x); \
} while (0)
+#endif /* !LINKER_DEBUG_TO_LOG */
#else /* !LINKER_DEBUG */
#define _PRINTVF(v,f,x...) do {} while(0)
#endif /* LINKER_DEBUG */
diff --git a/linker/linker_format.c b/linker/linker_format.c
new file mode 100644
index 0000000..4d00bd9
--- /dev/null
+++ b/linker/linker_format.c
@@ -0,0 +1,703 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stddef.h>
+#include "linker_format.h"
+#include "linker_debug.h"
+
+/* define UNIT_TESTS to build this file as a single executable that runs
+ * the formatter's unit tests
+ */
+#define xxUNIT_TESTS
+
+/*** Generic output sink
+ ***/
+
+typedef struct {
+ void *opaque;
+ void (*send)(void *opaque, const char *data, int len);
+} Out;
+
+static void
+out_send(Out *o, const void *data, size_t len)
+{
+ o->send(o->opaque, data, (int)len);
+}
+
+static void
+out_send_repeat(Out *o, char ch, int count)
+{
+ char pad[8];
+ const int padSize = (int)sizeof(pad);
+
+ memset(pad, ch, sizeof(pad));
+ while (count > 0) {
+ int avail = count;
+ if (avail > padSize) {
+ avail = padSize;
+ }
+ o->send(o->opaque, pad, avail);
+ count -= avail;
+ }
+}
+
+/* forward declaration */
+static void
+out_vformat(Out *o, const char *format, va_list args);
+
+/*** Bounded buffer output
+ ***/
+
+typedef struct {
+ Out out[1];
+ char *buffer;
+ char *pos;
+ char *end;
+ int total;
+} BufOut;
+
+static void
+buf_out_send(void *opaque, const char *data, int len)
+{
+ BufOut *bo = opaque;
+
+ if (len < 0)
+ len = strlen(data);
+
+ bo->total += len;
+
+ while (len > 0) {
+ int avail = bo->end - bo->pos;
+ if (avail == 0)
+ break;
+ if (avail > len)
+ avail = len;
+ memcpy(bo->pos, data, avail);
+ bo->pos += avail;
+ bo->pos[0] = '\0';
+ len -= avail;
+ }
+}
+
+static Out*
+buf_out_init(BufOut *bo, char *buffer, size_t size)
+{
+ if (size == 0)
+ return NULL;
+
+ bo->out->opaque = bo;
+ bo->out->send = buf_out_send;
+ bo->buffer = buffer;
+ bo->end = buffer + size - 1;
+ bo->pos = bo->buffer;
+ bo->pos[0] = '\0';
+ bo->total = 0;
+
+ return bo->out;
+}
+
+static int
+buf_out_length(BufOut *bo)
+{
+ return bo->total;
+}
+
+static int
+vformat_buffer(char *buff, size_t buffsize, const char *format, va_list args)
+{
+ BufOut bo;
+ Out *out;
+
+ out = buf_out_init(&bo, buff, buffsize);
+ if (out == NULL)
+ return 0;
+
+ out_vformat(out, format, args);
+
+ return buf_out_length(&bo);
+}
+
+int
+format_buffer(char *buff, size_t buffsize, const char *format, ...)
+{
+ va_list args;
+ int ret;
+
+ va_start(args, format);
+ ret = vformat_buffer(buff, buffsize, format, args);
+ va_end(args);
+
+ return ret;
+}
+
+/* The __stack_chk_fail() function calls __libc_android_log_print()
+ * which calls vsnprintf().
+ *
+ * We define our version of the function here to avoid dragging
+ * about 25 KB of C library routines related to formatting.
+ */
+int
+vsnprintf(char *buff, size_t bufsize, const char *format, va_list args)
+{
+ return format_buffer(buff, bufsize, format, args);
+}
+
+#if LINKER_DEBUG
+
+#if !LINKER_DEBUG_TO_LOG
+
+/*** File descriptor output
+ ***/
+
+typedef struct {
+ Out out[1];
+ int fd;
+ int total;
+} FdOut;
+
+static void
+fd_out_send(void *opaque, const char *data, int len)
+{
+ FdOut *fdo = opaque;
+
+ if (len < 0)
+ len = strlen(data);
+
+ while (len > 0) {
+ int ret = write(fdo->fd, data, len);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+ data += ret;
+ len -= ret;
+ fdo->total += ret;
+ }
+}
+
+static Out*
+fd_out_init(FdOut *fdo, int fd)
+{
+ fdo->out->opaque = fdo;
+ fdo->out->send = fd_out_send;
+ fdo->fd = fd;
+ fdo->total = 0;
+
+ return fdo->out;
+}
+
+static int
+fd_out_length(FdOut *fdo)
+{
+ return fdo->total;
+}
+
+
+int
+format_fd(int fd, const char *format, ...)
+{
+ FdOut fdo;
+ Out* out;
+ va_list args;
+
+ out = fd_out_init(&fdo, fd);
+ if (out == NULL)
+ return 0;
+
+ va_start(args, format);
+ out_vformat(out, format, args);
+ va_end(args);
+
+ return fd_out_length(&fdo);
+}
+
+#else /* LINKER_DEBUG_TO_LOG */
+
+/*** Log output
+ ***/
+
+/* We need our own version of __libc_android_log_vprint, otherwise
+ * the log output is completely broken. Probably due to the fact
+ * that the C library is not initialized yet.
+ *
+ * You can test that by setting CUSTOM_LOG_VPRINT to 0
+ */
+#define CUSTOM_LOG_VPRINT 1
+
+#if CUSTOM_LOG_VPRINT
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/uio.h>
+
+static int log_vprint(int prio, const char *tag, const char *fmt, va_list args)
+{
+ char buf[1024];
+ int result;
+ static int log_fd = -1;
+
+ result = vformat_buffer(buf, sizeof buf, fmt, args);
+
+ if (log_fd < 0) {
+ log_fd = open("/dev/log/main", O_WRONLY);
+ if (log_fd < 0)
+ return result;
+ }
+
+ {
+ ssize_t ret;
+ struct iovec vec[3];
+
+ vec[0].iov_base = (unsigned char *) &prio;
+ vec[0].iov_len = 1;
+ vec[1].iov_base = (void *) tag;
+ vec[1].iov_len = strlen(tag) + 1;
+ vec[2].iov_base = (void *) buf;
+ vec[2].iov_len = strlen(buf) + 1;
+
+ do {
+ ret = writev(log_fd, vec, 3);
+ } while ((ret < 0) && (errno == EINTR));
+ }
+ return result;
+}
+
+#define __libc_android_log_vprint log_vprint
+
+#else /* !CUSTOM_LOG_VPRINT */
+
+extern int __libc_android_log_vprint(int prio, const char* tag, const char* format, va_list ap);
+
+#endif /* !CUSTOM_LOG_VPRINT */
+
+int
+format_log(int prio, const char *tag, const char *format, ...)
+{
+ int ret;
+ va_list args;
+ va_start(args, format);
+ ret = __libc_android_log_vprint(prio, tag, format, args);
+ va_end(args);
+ return ret;
+}
+
+#endif /* LINKER_DEBUG_TO_LOG */
+
+#endif /* LINKER_DEBUG */
+
+/*** formatted output implementation
+ ***/
+
+/* Parse a decimal string from 'format + *ppos',
+ * return the value, and writes the new position past
+ * the decimal string in '*ppos' on exit.
+ *
+ * NOTE: Does *not* handle a sign prefix.
+ */
+static unsigned
+parse_decimal(const char *format, int *ppos)
+{
+ const char* p = format + *ppos;
+ unsigned result = 0;
+
+ for (;;) {
+ int ch = *p;
+ unsigned d = (unsigned)(ch - '0');
+
+ if (d >= 10U)
+ break;
+
+ result = result*10 + d;
+ p++;
+ }
+ *ppos = p - format;
+ return result;
+}
+
+/* write an octal/decimal/number into a bounded buffer.
+ * assumes that bufsize > 0, and 'digits' is a string of
+ * digits of at least 'base' values.
+ */
+static void
+format_number(char *buffer, size_t bufsize, uint64_t value, int base, const char *digits)
+{
+ char *pos = buffer;
+ char *end = buffer + bufsize - 1;
+
+ /* generate digit string in reverse order */
+ while (value) {
+ unsigned d = value % base;
+ value /= base;
+ if (pos < end) {
+ *pos++ = digits[d];
+ }
+ }
+
+ /* special case for 0 */
+ if (pos == buffer) {
+ if (pos < end) {
+ *pos++ = '0';
+ }
+ }
+ pos[0] = '\0';
+
+ /* now reverse digit string in-place */
+ end = pos - 1;
+ pos = buffer;
+ while (pos < end) {
+ int ch = pos[0];
+ pos[0] = end[0];
+ end[0] = (char) ch;
+ pos++;
+ end--;
+ }
+}
+
+/* Write an integer (octal or decimal) into a buffer, assumes buffsize > 2 */
+static void
+format_integer(char *buffer, size_t buffsize, uint64_t value, int base, int isSigned)
+{
+ if (isSigned && (int64_t)value < 0) {
+ buffer[0] = '-';
+ buffer += 1;
+ buffsize -= 1;
+ value = (uint64_t)(-(int64_t)value);
+ }
+
+ format_number(buffer, buffsize, value, base, "0123456789");
+}
+
+/* Write an octal into a buffer, assumes buffsize > 2 */
+static void
+format_octal(char *buffer, size_t buffsize, uint64_t value, int isSigned)
+{
+ format_integer(buffer, buffsize, value, 8, isSigned);
+}
+
+/* Write a decimal into a buffer, assumes buffsize > 2 */
+static void
+format_decimal(char *buffer, size_t buffsize, uint64_t value, int isSigned)
+{
+ format_integer(buffer, buffsize, value, 10, isSigned);
+}
+
+/* Write an hexadecimal into a buffer, isCap is true for capital alphas.
+ * Assumes bufsize > 2 */
+static void
+format_hex(char *buffer, size_t buffsize, uint64_t value, int isCap)
+{
+ const char *digits = isCap ? "0123456789ABCDEF" : "0123456789abcdef";
+
+ format_number(buffer, buffsize, value, 16, digits);
+}
+
+
+/* Perform formatted output to an output target 'o' */
+static void
+out_vformat(Out *o, const char *format, va_list args)
+{
+ int nn = 0, mm;
+ int padZero = 0;
+ int padLeft = 0;
+ char sign = '\0';
+ int width = -1;
+ int prec = -1;
+ size_t bytelen = sizeof(int);
+ const char* str;
+ int slen;
+ char buffer[32]; /* temporary buffer used to format numbers */
+
+ for (;;) {
+ char c;
+
+ /* first, find all characters that are not 0 or '%' */
+ /* then send them to the output directly */
+ mm = nn;
+ do {
+ c = format[mm];
+ if (c == '\0' || c == '%')
+ break;
+ mm++;
+ } while (1);
+
+ if (mm > nn) {
+ out_send(o, format+nn, mm-nn);
+ nn = mm;
+ }
+
+ /* is this it ? then exit */
+ if (c == '\0')
+ break;
+
+ /* nope, we are at a '%' modifier */
+ nn++; // skip it
+
+ /* parse flags */
+ for (;;) {
+ c = format[nn++];
+ if (c == '\0') { /* single trailing '%' ? */
+ c = '%';
+ out_send(o, &c, 1);
+ return;
+ }
+ else if (c == '0') {
+ padZero = 1;
+ continue;
+ }
+ else if (c == '-') {
+ padLeft = 1;
+ continue;
+ }
+ else if (c == ' ' || c == '+') {
+ sign = c;
+ continue;
+ }
+ break;
+ }
+
+ /* parse field width */
+ if ((c >= '0' && c <= '9')) {
+ nn --;
+ width = (int)parse_decimal(format, &nn);
+ c = format[nn++];
+ }
+
+ /* parse precision */
+ if (c == '.') {
+ prec = (int)parse_decimal(format, &nn);
+ c = format[nn++];
+ }
+
+ /* length modifier */
+ switch (c) {
+ case 'h':
+ bytelen = sizeof(short);
+ if (format[nn] == 'h') {
+ bytelen = sizeof(char);
+ nn += 1;
+ }
+ c = format[nn++];
+ break;
+ case 'l':
+ bytelen = sizeof(long);
+ if (format[nn] == 'l') {
+ bytelen = sizeof(long long);
+ nn += 1;
+ }
+ c = format[nn++];
+ break;
+ case 'z':
+ bytelen = sizeof(size_t);
+ c = format[nn++];
+ break;
+ case 't':
+ bytelen = sizeof(ptrdiff_t);
+ c = format[nn++];
+ break;
+ case 'p':
+ bytelen = sizeof(void*);
+ c = format[nn++];
+ default:
+ ;
+ }
+
+ /* conversion specifier */
+ if (c == 's') {
+ /* string */
+ str = va_arg(args, const char*);
+ } else if (c == 'c') {
+ /* character */
+ /* NOTE: char is promoted to int when passed through the stack */
+ buffer[0] = (char) va_arg(args, int);
+ buffer[1] = '\0';
+ str = buffer;
+ } else if (c == 'p') {
+ uint64_t value = (uint64_t)(ptrdiff_t) va_arg(args, void*);
+ buffer[0] = '0';
+ buffer[1] = 'x';
+ format_hex(buffer + 2, sizeof buffer-2, value, 0);
+ str = buffer;
+ } else {
+ /* integers - first read value from stack */
+ uint64_t value;
+ int isSigned = (c == 'd' || c == 'i' || c == 'o');
+
+ /* NOTE: int8_t and int16_t are promoted to int when passed
+ * through the stack
+ */
+ switch (bytelen) {
+ case 1: value = (uint8_t) va_arg(args, int); break;
+ case 2: value = (uint16_t) va_arg(args, int); break;
+ case 4: value = va_arg(args, uint32_t); break;
+ case 8: value = va_arg(args, uint64_t); break;
+ default: return; /* should not happen */
+ }
+
+ /* sign extension, if needed */
+ if (isSigned) {
+ int shift = 64 - 8*bytelen;
+ value = (uint64_t)(((int64_t)(value << shift)) >> shift);
+ }
+
+ /* format the number properly into our buffer */
+ switch (c) {
+ case 'i': case 'd':
+ format_integer(buffer, sizeof buffer, value, 10, isSigned);
+ break;
+ case 'o':
+ format_integer(buffer, sizeof buffer, value, 8, isSigned);
+ break;
+ case 'x': case 'X':
+ format_hex(buffer, sizeof buffer, value, (c == 'X'));
+ break;
+ default:
+ buffer[0] = '\0';
+ }
+ /* then point to it */
+ str = buffer;
+ }
+
+ /* if we are here, 'str' points to the content that must be
+ * outputted. handle padding and alignment now */
+
+ slen = strlen(str);
+
+ if (slen < width && !padLeft) {
+ char padChar = padZero ? '0' : ' ';
+ out_send_repeat(o, padChar, width - slen);
+ }
+
+ out_send(o, str, slen);
+
+ if (slen < width && padLeft) {
+ char padChar = padZero ? '0' : ' ';
+ out_send_repeat(o, padChar, width - slen);
+ }
+ }
+}
+
+
+#ifdef UNIT_TESTS
+
+#include <stdio.h>
+
+static int gFails = 0;
+
+#define MARGIN 40
+
+#define UTEST_CHECK(condition,message) \
+ printf("Checking %-*s: ", MARGIN, message); fflush(stdout); \
+ if (!(condition)) { \
+ printf("KO\n"); \
+ gFails += 1; \
+ } else { \
+ printf("ok\n"); \
+ }
+
+static void
+utest_BufOut(void)
+{
+ char buffer[16];
+ BufOut bo[1];
+ Out* out;
+ int ret;
+
+ buffer[0] = '1';
+ out = buf_out_init(bo, buffer, sizeof buffer);
+ UTEST_CHECK(buffer[0] == '\0', "buf_out_init clears initial byte");
+ out_send(out, "abc", 3);
+ UTEST_CHECK(!memcmp(buffer, "abc", 4), "out_send() works with BufOut");
+ out_send_repeat(out, 'X', 4);
+ UTEST_CHECK(!memcmp(buffer, "abcXXXX", 8), "out_send_repeat() works with BufOut");
+ buffer[sizeof buffer-1] = 'x';
+ out_send_repeat(out, 'Y', 2*sizeof(buffer));
+ UTEST_CHECK(buffer[sizeof buffer-1] == '\0', "overflows always zero-terminates");
+
+ out = buf_out_init(bo, buffer, sizeof buffer);
+ out_send_repeat(out, 'X', 2*sizeof(buffer));
+ ret = buf_out_length(bo);
+ UTEST_CHECK(ret == 2*sizeof(buffer), "correct size returned on overflow");
+}
+
+static void
+utest_expect(const char* result, const char* format, ...)
+{
+ va_list args;
+ BufOut bo[1];
+ char buffer[256];
+ Out* out = buf_out_init(bo, buffer, sizeof buffer);
+
+ printf("Checking %-*s: ", MARGIN, format); fflush(stdout);
+ va_start(args, format);
+ out_vformat(out, format, args);
+ va_end(args);
+
+ if (strcmp(result, buffer)) {
+ printf("KO. got '%s' expecting '%s'\n", buffer, result);
+ gFails += 1;
+ } else {
+ printf("ok. got '%s'\n", result);
+ }
+}
+
+int main(void)
+{
+ utest_BufOut();
+ utest_expect("", "");
+ utest_expect("a", "a");
+ utest_expect("01234", "01234", "");
+ utest_expect("01234", "%s", "01234");
+ utest_expect("aabbcc", "aa%scc", "bb");
+ utest_expect("a", "%c", 'a');
+ utest_expect("1234", "%d", 1234);
+ utest_expect("-8123", "%d", -8123);
+ utest_expect("16", "%hd", 0x7fff0010);
+ utest_expect("16", "%hhd", 0x7fffff10);
+ utest_expect("68719476736", "%lld", 0x1000000000);
+ utest_expect("70000", "%ld", 70000);
+ utest_expect("0xb0001234", "%p", (void*)0xb0001234);
+ utest_expect("12ab", "%x", 0x12ab);
+ utest_expect("12AB", "%X", 0x12ab);
+ utest_expect("00123456", "%08x", 0x123456);
+ utest_expect("01234", "0%d", 1234);
+ utest_expect(" 1234", "%5d", 1234);
+ utest_expect("01234", "%05d", 1234);
+ utest_expect(" 1234", "%8d", 1234);
+ utest_expect("1234 ", "%-8d", 1234);
+ utest_expect("abcdef ", "%-11s", "abcdef");
+ utest_expect("something:1234", "%s:%d", "something", 1234);
+ return gFails != 0;
+}
+
+#endif /* UNIT_TESTS */
diff --git a/linker/linker_format.h b/linker/linker_format.h
new file mode 100644
index 0000000..6ae2bad
--- /dev/null
+++ b/linker/linker_format.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#ifndef _LINKER_FORMAT_H
+#define _LINKER_FORMAT_H
+
+#include <stdarg.h>
+#include <stddef.h>
+
+/* 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 */
+/* issues (it uses malloc()/free()) and increases code size */
+
+int format_buffer(char *buffer, size_t bufsize, const char *format, ...);
+
+#endif /* _LINKER_FORMAT_H */