aboutsummaryrefslogtreecommitdiffstats
path: root/linker/linker_format.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linker/linker_format.cpp')
-rw-r--r--linker/linker_format.cpp704
1 files changed, 0 insertions, 704 deletions
diff --git a/linker/linker_format.cpp b/linker/linker_format.cpp
deleted file mode 100644
index e631ca1c1..000000000
--- a/linker/linker_format.cpp
+++ /dev/null
@@ -1,704 +0,0 @@
-/*
- * 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.
- */
-
-// Temporarily disable _FORTIFY_SOURCE to get this code to
-// compile under GCC 4.7
-#undef _FORTIFY_SOURCE
-
-#include "linker_format.h"
-
-#include <assert.h>
-#include <errno.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <string.h>
-#include <unistd.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
- ***/
-
-struct Out {
- void *opaque;
- void (*send)(void *opaque, const char *data, int len);
-};
-
-static void out_send(Out *o, const char *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
- ***/
-
-struct BufOut {
- Out out[1];
- char *buffer;
- char *pos;
- char *end;
- int total;
-};
-
-static void buf_out_send(void *opaque, const char *data, int len) {
- BufOut *bo = reinterpret_cast<BufOut*>(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);
-}
-
-/* The pthread implementation uses snprintf(). If we define it here, we
- * avoid pulling the stdio vfprintf() implementation into the linker
- * saving about 19KB of machine code.
- */
-int
-snprintf(char* buff, size_t bufsize, const char* format, ...)
-{
- va_list args;
- int ret;
- va_start(args, format);
- ret = vsnprintf(buff, bufsize, format, args);
- va_end(args);
- return ret;
-}
-
-#if !LINKER_DEBUG_TO_LOG
-
-/*** File descriptor output
- ***/
-
-struct FdOut {
- Out out[1];
- int fd;
- int total;
-};
-
-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 "C" 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 */
-
-/*** 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;
-}
-
-// Writes number 'value' in base 'base' into buffer 'buf' of size 'buf_size' bytes.
-// Assumes that buf_size > 0.
-static void format_number(char* buf, size_t buf_size, uint64_t value, int base, bool caps) {
- char* p = buf;
- char* end = buf + buf_size - 1;
-
- // Generate digit string in reverse order.
- while (value) {
- unsigned d = value % base;
- value /= base;
- if (p != end) {
- char ch;
- if (d < 10) {
- ch = '0' + d;
- } else {
- ch = (caps ? 'A' : 'a') + (d - 10);
- }
- *p++ = ch;
- }
- }
-
- // Special case for 0.
- if (p == buf) {
- if (p != end) {
- *p++ = '0';
- }
- }
- *p = '\0';
-
- // Reverse digit string in-place.
- size_t length = p - buf;
- for (size_t i = 0, j = length - 1; i < j; ++i, --j) {
- char ch = buf[i];
- buf[i] = buf[j];
- buf[j] = ch;
- }
-}
-
-/* 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)
-{
- // TODO: this is incorrect for MIN_INT.
- if (isSigned && (int64_t)value < 0) {
- buffer[0] = '-';
- buffer += 1;
- buffsize -= 1;
- value = (uint64_t)(-(int64_t)value);
- }
-
- format_number(buffer, buffsize, value, base, false);
-}
-
-// Assumes buf_size > 2.
-static void format_hex(char* buf, size_t buf_size, uint64_t value, bool caps) {
- format_number(buf, buf_size, value, 16, caps);
-}
-
-
-/* Perform formatted output to an output target 'o' */
-static void
-out_vformat(Out *o, const char *format, va_list args)
-{
- int nn = 0;
-
- for (;;) {
- int 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 */
-
- 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;
- 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 = (uintptr_t) va_arg(args, void*);
- buffer[0] = '0';
- buffer[1] = 'x';
- format_hex(buffer + 2, sizeof buffer-2, value, false);
- 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 (sign != '\0' || prec != -1) {
- __assert(__FILE__, __LINE__, "sign/precision unsupported");
- }
-
- 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", 0x1000000000LL);
- 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);
- utest_expect("005:5:05", "%03d:%d:%02d", 5, 5, 5);
- utest_expect("5,0x0", "%d,%p", 5, NULL);
- utest_expect("68719476736,6,7,8", "%lld,%d,%d,%d", 0x1000000000LL, 6, 7, 8);
- return gFails != 0;
-}
-
-#endif /* UNIT_TESTS */