blob: cc70a03455a89853322722f9249bb056d5e84682 [file] [log] [blame]
David 'Digit' Turner5c734642010-01-20 12:36:51 -08001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
Elliott Hughes18a206c2012-10-29 17:37:13 -070029#include <assert.h>
David 'Digit' Turner5c734642010-01-20 12:36:51 -080030#include <stdarg.h>
31#include <string.h>
32#include <errno.h>
33#include <unistd.h>
34#include <stdint.h>
35#include <stddef.h>
36#include "linker_format.h"
37#include "linker_debug.h"
38
39/* define UNIT_TESTS to build this file as a single executable that runs
40 * the formatter's unit tests
41 */
42#define xxUNIT_TESTS
43
44/*** Generic output sink
45 ***/
46
Elliott Hughes18a206c2012-10-29 17:37:13 -070047struct Out {
48 void *opaque;
49 void (*send)(void *opaque, const char *data, int len);
50};
David 'Digit' Turner5c734642010-01-20 12:36:51 -080051
Elliott Hughes18a206c2012-10-29 17:37:13 -070052static void out_send(Out *o, const char *data, size_t len) {
David 'Digit' Turner5c734642010-01-20 12:36:51 -080053 o->send(o->opaque, data, (int)len);
54}
55
56static void
57out_send_repeat(Out *o, char ch, int count)
58{
59 char pad[8];
60 const int padSize = (int)sizeof(pad);
61
62 memset(pad, ch, sizeof(pad));
63 while (count > 0) {
64 int avail = count;
65 if (avail > padSize) {
66 avail = padSize;
67 }
68 o->send(o->opaque, pad, avail);
69 count -= avail;
70 }
71}
72
73/* forward declaration */
Elliott Hughes18a206c2012-10-29 17:37:13 -070074static void out_vformat(Out* o, const char* format, va_list args);
David 'Digit' Turner5c734642010-01-20 12:36:51 -080075
76/*** Bounded buffer output
77 ***/
78
Elliott Hughes18a206c2012-10-29 17:37:13 -070079struct BufOut {
80 Out out[1];
81 char *buffer;
82 char *pos;
83 char *end;
84 int total;
85};
David 'Digit' Turner5c734642010-01-20 12:36:51 -080086
Elliott Hughes18a206c2012-10-29 17:37:13 -070087static void buf_out_send(void *opaque, const char *data, int len) {
88 BufOut *bo = reinterpret_cast<BufOut*>(opaque);
David 'Digit' Turner5c734642010-01-20 12:36:51 -080089
Elliott Hughes18a206c2012-10-29 17:37:13 -070090 if (len < 0) {
David 'Digit' Turner5c734642010-01-20 12:36:51 -080091 len = strlen(data);
Elliott Hughes18a206c2012-10-29 17:37:13 -070092 }
David 'Digit' Turner5c734642010-01-20 12:36:51 -080093
94 bo->total += len;
95
96 while (len > 0) {
97 int avail = bo->end - bo->pos;
98 if (avail == 0)
99 break;
100 if (avail > len)
101 avail = len;
102 memcpy(bo->pos, data, avail);
103 bo->pos += avail;
104 bo->pos[0] = '\0';
105 len -= avail;
106 }
107}
108
109static Out*
110buf_out_init(BufOut *bo, char *buffer, size_t size)
111{
112 if (size == 0)
113 return NULL;
114
115 bo->out->opaque = bo;
116 bo->out->send = buf_out_send;
117 bo->buffer = buffer;
118 bo->end = buffer + size - 1;
119 bo->pos = bo->buffer;
120 bo->pos[0] = '\0';
121 bo->total = 0;
122
123 return bo->out;
124}
125
126static int
127buf_out_length(BufOut *bo)
128{
129 return bo->total;
130}
131
132static int
133vformat_buffer(char *buff, size_t buffsize, const char *format, va_list args)
134{
135 BufOut bo;
136 Out *out;
137
138 out = buf_out_init(&bo, buff, buffsize);
139 if (out == NULL)
140 return 0;
141
142 out_vformat(out, format, args);
143
144 return buf_out_length(&bo);
145}
146
147int
148format_buffer(char *buff, size_t buffsize, const char *format, ...)
149{
150 va_list args;
151 int ret;
152
153 va_start(args, format);
154 ret = vformat_buffer(buff, buffsize, format, args);
155 va_end(args);
156
157 return ret;
158}
159
160/* The __stack_chk_fail() function calls __libc_android_log_print()
161 * which calls vsnprintf().
162 *
163 * We define our version of the function here to avoid dragging
164 * about 25 KB of C library routines related to formatting.
165 */
166int
167vsnprintf(char *buff, size_t bufsize, const char *format, va_list args)
168{
169 return format_buffer(buff, bufsize, format, args);
170}
171
David 'Digit' Turner166b7db2012-06-19 02:02:32 +0200172/* The pthread implementation uses snprintf(). If we define it here, we
173 * avoid pulling the stdio vfprintf() implementation into the linker
174 * saving about 19KB of machine code.
175 */
176int
177snprintf(char* buff, size_t bufsize, const char* format, ...)
178{
179 va_list args;
180 int ret;
181 va_start(args, format);
182 ret = vsnprintf(buff, bufsize, format, args);
183 va_end(args);
184 return ret;
185}
186
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800187#if LINKER_DEBUG
188
189#if !LINKER_DEBUG_TO_LOG
190
191/*** File descriptor output
192 ***/
193
Elliott Hughes18a206c2012-10-29 17:37:13 -0700194struct FdOut {
195 Out out[1];
196 int fd;
197 int total;
198};
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800199
200static void
201fd_out_send(void *opaque, const char *data, int len)
202{
203 FdOut *fdo = opaque;
204
205 if (len < 0)
206 len = strlen(data);
207
208 while (len > 0) {
209 int ret = write(fdo->fd, data, len);
210 if (ret < 0) {
211 if (errno == EINTR)
212 continue;
213 break;
214 }
215 data += ret;
216 len -= ret;
217 fdo->total += ret;
218 }
219}
220
221static Out*
222fd_out_init(FdOut *fdo, int fd)
223{
224 fdo->out->opaque = fdo;
225 fdo->out->send = fd_out_send;
226 fdo->fd = fd;
227 fdo->total = 0;
228
229 return fdo->out;
230}
231
232static int
233fd_out_length(FdOut *fdo)
234{
235 return fdo->total;
236}
237
238
239int
240format_fd(int fd, const char *format, ...)
241{
242 FdOut fdo;
243 Out* out;
244 va_list args;
245
246 out = fd_out_init(&fdo, fd);
247 if (out == NULL)
248 return 0;
249
250 va_start(args, format);
251 out_vformat(out, format, args);
252 va_end(args);
253
254 return fd_out_length(&fdo);
255}
256
257#else /* LINKER_DEBUG_TO_LOG */
258
259/*** Log output
260 ***/
261
262/* We need our own version of __libc_android_log_vprint, otherwise
263 * the log output is completely broken. Probably due to the fact
264 * that the C library is not initialized yet.
265 *
266 * You can test that by setting CUSTOM_LOG_VPRINT to 0
267 */
268#define CUSTOM_LOG_VPRINT 1
269
270#if CUSTOM_LOG_VPRINT
271
272#include <unistd.h>
273#include <fcntl.h>
274#include <sys/uio.h>
275
276static int log_vprint(int prio, const char *tag, const char *fmt, va_list args)
277{
278 char buf[1024];
279 int result;
280 static int log_fd = -1;
281
282 result = vformat_buffer(buf, sizeof buf, fmt, args);
283
284 if (log_fd < 0) {
285 log_fd = open("/dev/log/main", O_WRONLY);
286 if (log_fd < 0)
287 return result;
288 }
289
290 {
291 ssize_t ret;
292 struct iovec vec[3];
293
294 vec[0].iov_base = (unsigned char *) &prio;
295 vec[0].iov_len = 1;
296 vec[1].iov_base = (void *) tag;
297 vec[1].iov_len = strlen(tag) + 1;
298 vec[2].iov_base = (void *) buf;
299 vec[2].iov_len = strlen(buf) + 1;
300
301 do {
302 ret = writev(log_fd, vec, 3);
303 } while ((ret < 0) && (errno == EINTR));
304 }
305 return result;
306}
307
308#define __libc_android_log_vprint log_vprint
309
310#else /* !CUSTOM_LOG_VPRINT */
311
Elliott Hughes46882792012-08-03 16:49:39 -0700312extern "C" int __libc_android_log_vprint(int prio, const char* tag, const char* format, va_list ap);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800313
314#endif /* !CUSTOM_LOG_VPRINT */
315
316int
317format_log(int prio, const char *tag, const char *format, ...)
318{
319 int ret;
320 va_list args;
321 va_start(args, format);
322 ret = __libc_android_log_vprint(prio, tag, format, args);
323 va_end(args);
324 return ret;
325}
326
327#endif /* LINKER_DEBUG_TO_LOG */
328
329#endif /* LINKER_DEBUG */
330
331/*** formatted output implementation
332 ***/
333
334/* Parse a decimal string from 'format + *ppos',
335 * return the value, and writes the new position past
336 * the decimal string in '*ppos' on exit.
337 *
338 * NOTE: Does *not* handle a sign prefix.
339 */
340static unsigned
341parse_decimal(const char *format, int *ppos)
342{
343 const char* p = format + *ppos;
344 unsigned result = 0;
345
346 for (;;) {
347 int ch = *p;
348 unsigned d = (unsigned)(ch - '0');
349
350 if (d >= 10U)
351 break;
352
353 result = result*10 + d;
354 p++;
355 }
356 *ppos = p - format;
357 return result;
358}
359
360/* write an octal/decimal/number into a bounded buffer.
361 * assumes that bufsize > 0, and 'digits' is a string of
362 * digits of at least 'base' values.
363 */
364static void
365format_number(char *buffer, size_t bufsize, uint64_t value, int base, const char *digits)
366{
367 char *pos = buffer;
368 char *end = buffer + bufsize - 1;
369
370 /* generate digit string in reverse order */
371 while (value) {
372 unsigned d = value % base;
373 value /= base;
374 if (pos < end) {
375 *pos++ = digits[d];
376 }
377 }
378
379 /* special case for 0 */
380 if (pos == buffer) {
381 if (pos < end) {
382 *pos++ = '0';
383 }
384 }
385 pos[0] = '\0';
386
387 /* now reverse digit string in-place */
388 end = pos - 1;
389 pos = buffer;
390 while (pos < end) {
391 int ch = pos[0];
392 pos[0] = end[0];
393 end[0] = (char) ch;
394 pos++;
395 end--;
396 }
397}
398
399/* Write an integer (octal or decimal) into a buffer, assumes buffsize > 2 */
400static void
401format_integer(char *buffer, size_t buffsize, uint64_t value, int base, int isSigned)
402{
403 if (isSigned && (int64_t)value < 0) {
404 buffer[0] = '-';
405 buffer += 1;
406 buffsize -= 1;
407 value = (uint64_t)(-(int64_t)value);
408 }
409
410 format_number(buffer, buffsize, value, base, "0123456789");
411}
412
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800413/* Write an hexadecimal into a buffer, isCap is true for capital alphas.
414 * Assumes bufsize > 2 */
415static void
416format_hex(char *buffer, size_t buffsize, uint64_t value, int isCap)
417{
418 const char *digits = isCap ? "0123456789ABCDEF" : "0123456789abcdef";
419
420 format_number(buffer, buffsize, value, 16, digits);
421}
422
423
424/* Perform formatted output to an output target 'o' */
425static void
426out_vformat(Out *o, const char *format, va_list args)
427{
Andy McFaddenec92af82011-07-29 12:46:34 -0700428 int nn = 0;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800429
430 for (;;) {
Andy McFaddenec92af82011-07-29 12:46:34 -0700431 int mm;
432 int padZero = 0;
433 int padLeft = 0;
434 char sign = '\0';
435 int width = -1;
436 int prec = -1;
437 size_t bytelen = sizeof(int);
438 const char* str;
439 int slen;
440 char buffer[32]; /* temporary buffer used to format numbers */
441
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800442 char c;
443
444 /* first, find all characters that are not 0 or '%' */
445 /* then send them to the output directly */
446 mm = nn;
447 do {
448 c = format[mm];
449 if (c == '\0' || c == '%')
450 break;
451 mm++;
452 } while (1);
453
454 if (mm > nn) {
455 out_send(o, format+nn, mm-nn);
456 nn = mm;
457 }
458
459 /* is this it ? then exit */
460 if (c == '\0')
461 break;
462
463 /* nope, we are at a '%' modifier */
464 nn++; // skip it
465
466 /* parse flags */
467 for (;;) {
468 c = format[nn++];
469 if (c == '\0') { /* single trailing '%' ? */
470 c = '%';
471 out_send(o, &c, 1);
472 return;
473 }
474 else if (c == '0') {
475 padZero = 1;
476 continue;
477 }
478 else if (c == '-') {
479 padLeft = 1;
480 continue;
481 }
482 else if (c == ' ' || c == '+') {
483 sign = c;
484 continue;
485 }
486 break;
487 }
488
489 /* parse field width */
490 if ((c >= '0' && c <= '9')) {
491 nn --;
492 width = (int)parse_decimal(format, &nn);
493 c = format[nn++];
494 }
495
496 /* parse precision */
497 if (c == '.') {
498 prec = (int)parse_decimal(format, &nn);
499 c = format[nn++];
500 }
501
502 /* length modifier */
503 switch (c) {
504 case 'h':
505 bytelen = sizeof(short);
506 if (format[nn] == 'h') {
507 bytelen = sizeof(char);
508 nn += 1;
509 }
510 c = format[nn++];
511 break;
512 case 'l':
513 bytelen = sizeof(long);
514 if (format[nn] == 'l') {
515 bytelen = sizeof(long long);
516 nn += 1;
517 }
518 c = format[nn++];
519 break;
520 case 'z':
521 bytelen = sizeof(size_t);
522 c = format[nn++];
523 break;
524 case 't':
525 bytelen = sizeof(ptrdiff_t);
526 c = format[nn++];
527 break;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800528 default:
529 ;
530 }
531
532 /* conversion specifier */
533 if (c == 's') {
534 /* string */
535 str = va_arg(args, const char*);
536 } else if (c == 'c') {
537 /* character */
538 /* NOTE: char is promoted to int when passed through the stack */
539 buffer[0] = (char) va_arg(args, int);
540 buffer[1] = '\0';
541 str = buffer;
542 } else if (c == 'p') {
Andy McFaddenec92af82011-07-29 12:46:34 -0700543 uint64_t value = (uintptr_t) va_arg(args, void*);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800544 buffer[0] = '0';
545 buffer[1] = 'x';
546 format_hex(buffer + 2, sizeof buffer-2, value, 0);
547 str = buffer;
548 } else {
549 /* integers - first read value from stack */
550 uint64_t value;
551 int isSigned = (c == 'd' || c == 'i' || c == 'o');
552
553 /* NOTE: int8_t and int16_t are promoted to int when passed
554 * through the stack
555 */
556 switch (bytelen) {
557 case 1: value = (uint8_t) va_arg(args, int); break;
558 case 2: value = (uint16_t) va_arg(args, int); break;
559 case 4: value = va_arg(args, uint32_t); break;
560 case 8: value = va_arg(args, uint64_t); break;
561 default: return; /* should not happen */
562 }
563
564 /* sign extension, if needed */
565 if (isSigned) {
566 int shift = 64 - 8*bytelen;
567 value = (uint64_t)(((int64_t)(value << shift)) >> shift);
568 }
569
570 /* format the number properly into our buffer */
571 switch (c) {
572 case 'i': case 'd':
573 format_integer(buffer, sizeof buffer, value, 10, isSigned);
574 break;
575 case 'o':
576 format_integer(buffer, sizeof buffer, value, 8, isSigned);
577 break;
578 case 'x': case 'X':
579 format_hex(buffer, sizeof buffer, value, (c == 'X'));
580 break;
581 default:
582 buffer[0] = '\0';
583 }
584 /* then point to it */
585 str = buffer;
586 }
587
588 /* if we are here, 'str' points to the content that must be
589 * outputted. handle padding and alignment now */
590
591 slen = strlen(str);
592
Elliott Hughes18a206c2012-10-29 17:37:13 -0700593 if (sign != '\0' || prec != -1) {
594 __assert(__FILE__, __LINE__, "sign/precision unsupported");
595 }
596
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800597 if (slen < width && !padLeft) {
598 char padChar = padZero ? '0' : ' ';
599 out_send_repeat(o, padChar, width - slen);
600 }
601
602 out_send(o, str, slen);
603
604 if (slen < width && padLeft) {
605 char padChar = padZero ? '0' : ' ';
606 out_send_repeat(o, padChar, width - slen);
607 }
608 }
609}
610
611
612#ifdef UNIT_TESTS
613
614#include <stdio.h>
615
616static int gFails = 0;
617
618#define MARGIN 40
619
620#define UTEST_CHECK(condition,message) \
621 printf("Checking %-*s: ", MARGIN, message); fflush(stdout); \
622 if (!(condition)) { \
623 printf("KO\n"); \
624 gFails += 1; \
625 } else { \
626 printf("ok\n"); \
627 }
628
629static void
630utest_BufOut(void)
631{
632 char buffer[16];
633 BufOut bo[1];
634 Out* out;
635 int ret;
636
637 buffer[0] = '1';
638 out = buf_out_init(bo, buffer, sizeof buffer);
639 UTEST_CHECK(buffer[0] == '\0', "buf_out_init clears initial byte");
640 out_send(out, "abc", 3);
641 UTEST_CHECK(!memcmp(buffer, "abc", 4), "out_send() works with BufOut");
642 out_send_repeat(out, 'X', 4);
643 UTEST_CHECK(!memcmp(buffer, "abcXXXX", 8), "out_send_repeat() works with BufOut");
644 buffer[sizeof buffer-1] = 'x';
645 out_send_repeat(out, 'Y', 2*sizeof(buffer));
646 UTEST_CHECK(buffer[sizeof buffer-1] == '\0', "overflows always zero-terminates");
647
648 out = buf_out_init(bo, buffer, sizeof buffer);
649 out_send_repeat(out, 'X', 2*sizeof(buffer));
650 ret = buf_out_length(bo);
651 UTEST_CHECK(ret == 2*sizeof(buffer), "correct size returned on overflow");
652}
653
654static void
655utest_expect(const char* result, const char* format, ...)
656{
657 va_list args;
658 BufOut bo[1];
659 char buffer[256];
660 Out* out = buf_out_init(bo, buffer, sizeof buffer);
661
662 printf("Checking %-*s: ", MARGIN, format); fflush(stdout);
663 va_start(args, format);
664 out_vformat(out, format, args);
665 va_end(args);
666
667 if (strcmp(result, buffer)) {
668 printf("KO. got '%s' expecting '%s'\n", buffer, result);
669 gFails += 1;
670 } else {
671 printf("ok. got '%s'\n", result);
672 }
673}
674
675int main(void)
676{
677 utest_BufOut();
678 utest_expect("", "");
679 utest_expect("a", "a");
680 utest_expect("01234", "01234", "");
681 utest_expect("01234", "%s", "01234");
682 utest_expect("aabbcc", "aa%scc", "bb");
683 utest_expect("a", "%c", 'a');
684 utest_expect("1234", "%d", 1234);
685 utest_expect("-8123", "%d", -8123);
686 utest_expect("16", "%hd", 0x7fff0010);
687 utest_expect("16", "%hhd", 0x7fffff10);
Andy McFaddenec92af82011-07-29 12:46:34 -0700688 utest_expect("68719476736", "%lld", 0x1000000000LL);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800689 utest_expect("70000", "%ld", 70000);
690 utest_expect("0xb0001234", "%p", (void*)0xb0001234);
691 utest_expect("12ab", "%x", 0x12ab);
692 utest_expect("12AB", "%X", 0x12ab);
693 utest_expect("00123456", "%08x", 0x123456);
694 utest_expect("01234", "0%d", 1234);
695 utest_expect(" 1234", "%5d", 1234);
696 utest_expect("01234", "%05d", 1234);
697 utest_expect(" 1234", "%8d", 1234);
698 utest_expect("1234 ", "%-8d", 1234);
699 utest_expect("abcdef ", "%-11s", "abcdef");
700 utest_expect("something:1234", "%s:%d", "something", 1234);
Andy McFaddenec92af82011-07-29 12:46:34 -0700701 utest_expect("005:5:05", "%03d:%d:%02d", 5, 5, 5);
702 utest_expect("5,0x0", "%d,%p", 5, NULL);
703 utest_expect("68719476736,6,7,8", "%lld,%d,%d,%d", 0x1000000000LL, 6, 7, 8);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800704 return gFails != 0;
705}
706
707#endif /* UNIT_TESTS */