Fix debug output in the dynamic linker.

This provides a mini-printf implementation that reduces the
size of the dynamic linker by 25 KB, by preventing the drag of
formatting-related routines from the C library.

Also allow traces to be sent to the log, instead of stdout.

NOTE: You now need to modify Android.mk to enable/disable debug
      output.
diff --git a/linker/Android.mk b/linker/Android.mk
index 19a68a0..4647c8f 100644
--- a/linker/Android.mk
+++ b/linker/Android.mk
@@ -4,6 +4,7 @@
 LOCAL_SRC_FILES:= \
 	arch/$(TARGET_ARCH)/begin.S \
 	linker.c \
+	linker_format.c \
 	rt.c \
 	dlfcn.c \
 	debugger.c \
@@ -31,6 +32,10 @@
 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; duplicate the HAVE_ARM_TLS_REGISTER definition
 # from the libc build
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 @@
 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/dlfcn.c b/linker/dlfcn.c
index 039926c..bc6b95a 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 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 40fdbab..7eb1ef9 100644
--- a/linker/linker.c
+++ b/linker/linker.c
@@ -48,6 +48,7 @@
 
 #include "linker.h"
 #include "linker_debug.h"
+#include "linker_format.h"
 
 #include "ba.h"
 
@@ -142,7 +143,7 @@
 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)
@@ -584,7 +585,7 @@
         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;
@@ -593,7 +594,7 @@
             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;
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 */