diff options
author | David 'Digit' Turner <digit@google.com> | 2010-01-20 12:36:51 -0800 |
---|---|---|
committer | David 'Digit' Turner <digit@google.com> | 2010-02-11 10:46:48 -0800 |
commit | 5c734644eebf8d01be1e86cbe20a111a5c5a2738 (patch) | |
tree | 5e442dc0157ec487f51a65f85f87fe2bd66fd0cf | |
parent | e100f52f4ad1b70762bbcc5ad9828bd258917ee5 (diff) | |
download | bionic-5c734644eebf8d01be1e86cbe20a111a5c5a2738.zip bionic-5c734644eebf8d01be1e86cbe20a111a5c5a2738.tar.gz bionic-5c734644eebf8d01be1e86cbe20a111a5c5a2738.tar.bz2 |
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.
-rw-r--r-- | linker/Android.mk | 5 | ||||
-rw-r--r-- | linker/README.TXT | 29 | ||||
-rw-r--r-- | linker/dlfcn.c | 3 | ||||
-rw-r--r-- | linker/linker.c | 7 | ||||
-rw-r--r-- | linker/linker_debug.h | 36 | ||||
-rw-r--r-- | linker/linker_format.c | 703 | ||||
-rw-r--r-- | linker/linker_format.h | 41 |
7 files changed, 809 insertions, 15 deletions
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 @@ include $(CLEAR_VARS) 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 += -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; 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 @@ 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/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 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 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 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) @@ -584,7 +585,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; @@ -593,7 +594,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; 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 */ |