blob: e631ca1c1f975fe94434fb3ccbb4e564344d7daf [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
Nick Kralevich83697b82012-11-15 15:21:43 -080029// Temporarily disable _FORTIFY_SOURCE to get this code to
30// compile under GCC 4.7
31#undef _FORTIFY_SOURCE
32
David 'Digit' Turner5c734642010-01-20 12:36:51 -080033#include "linker_format.h"
Elliott Hugheseababde2012-12-20 18:59:05 -080034
35#include <assert.h>
36#include <errno.h>
37#include <stdarg.h>
38#include <stddef.h>
39#include <stdint.h>
40#include <string.h>
41#include <unistd.h>
42
David 'Digit' Turner5c734642010-01-20 12:36:51 -080043#include "linker_debug.h"
44
45/* define UNIT_TESTS to build this file as a single executable that runs
46 * the formatter's unit tests
47 */
48#define xxUNIT_TESTS
49
50/*** Generic output sink
51 ***/
52
Elliott Hughes18a206c2012-10-29 17:37:13 -070053struct Out {
54 void *opaque;
55 void (*send)(void *opaque, const char *data, int len);
56};
David 'Digit' Turner5c734642010-01-20 12:36:51 -080057
Elliott Hughes18a206c2012-10-29 17:37:13 -070058static void out_send(Out *o, const char *data, size_t len) {
David 'Digit' Turner5c734642010-01-20 12:36:51 -080059 o->send(o->opaque, data, (int)len);
60}
61
62static void
63out_send_repeat(Out *o, char ch, int count)
64{
65 char pad[8];
66 const int padSize = (int)sizeof(pad);
67
68 memset(pad, ch, sizeof(pad));
69 while (count > 0) {
70 int avail = count;
71 if (avail > padSize) {
72 avail = padSize;
73 }
74 o->send(o->opaque, pad, avail);
75 count -= avail;
76 }
77}
78
79/* forward declaration */
Elliott Hughes18a206c2012-10-29 17:37:13 -070080static void out_vformat(Out* o, const char* format, va_list args);
David 'Digit' Turner5c734642010-01-20 12:36:51 -080081
82/*** Bounded buffer output
83 ***/
84
Elliott Hughes18a206c2012-10-29 17:37:13 -070085struct BufOut {
86 Out out[1];
87 char *buffer;
88 char *pos;
89 char *end;
90 int total;
91};
David 'Digit' Turner5c734642010-01-20 12:36:51 -080092
Elliott Hughes18a206c2012-10-29 17:37:13 -070093static void buf_out_send(void *opaque, const char *data, int len) {
94 BufOut *bo = reinterpret_cast<BufOut*>(opaque);
David 'Digit' Turner5c734642010-01-20 12:36:51 -080095
Elliott Hughes18a206c2012-10-29 17:37:13 -070096 if (len < 0) {
David 'Digit' Turner5c734642010-01-20 12:36:51 -080097 len = strlen(data);
Elliott Hughes18a206c2012-10-29 17:37:13 -070098 }
David 'Digit' Turner5c734642010-01-20 12:36:51 -080099
100 bo->total += len;
101
102 while (len > 0) {
103 int avail = bo->end - bo->pos;
104 if (avail == 0)
105 break;
106 if (avail > len)
107 avail = len;
108 memcpy(bo->pos, data, avail);
109 bo->pos += avail;
110 bo->pos[0] = '\0';
111 len -= avail;
112 }
113}
114
115static Out*
116buf_out_init(BufOut *bo, char *buffer, size_t size)
117{
118 if (size == 0)
119 return NULL;
120
121 bo->out->opaque = bo;
122 bo->out->send = buf_out_send;
123 bo->buffer = buffer;
124 bo->end = buffer + size - 1;
125 bo->pos = bo->buffer;
126 bo->pos[0] = '\0';
127 bo->total = 0;
128
129 return bo->out;
130}
131
132static int
133buf_out_length(BufOut *bo)
134{
135 return bo->total;
136}
137
138static int
139vformat_buffer(char *buff, size_t buffsize, const char *format, va_list args)
140{
141 BufOut bo;
142 Out *out;
143
144 out = buf_out_init(&bo, buff, buffsize);
145 if (out == NULL)
146 return 0;
147
148 out_vformat(out, format, args);
149
150 return buf_out_length(&bo);
151}
152
153int
154format_buffer(char *buff, size_t buffsize, const char *format, ...)
155{
156 va_list args;
157 int ret;
158
159 va_start(args, format);
160 ret = vformat_buffer(buff, buffsize, format, args);
161 va_end(args);
162
163 return ret;
164}
165
166/* The __stack_chk_fail() function calls __libc_android_log_print()
167 * which calls vsnprintf().
168 *
169 * We define our version of the function here to avoid dragging
170 * about 25 KB of C library routines related to formatting.
171 */
172int
173vsnprintf(char *buff, size_t bufsize, const char *format, va_list args)
174{
175 return format_buffer(buff, bufsize, format, args);
176}
177
David 'Digit' Turner166b7db2012-06-19 02:02:32 +0200178/* The pthread implementation uses snprintf(). If we define it here, we
179 * avoid pulling the stdio vfprintf() implementation into the linker
180 * saving about 19KB of machine code.
181 */
182int
183snprintf(char* buff, size_t bufsize, const char* format, ...)
184{
185 va_list args;
186 int ret;
187 va_start(args, format);
188 ret = vsnprintf(buff, bufsize, format, args);
189 va_end(args);
190 return ret;
191}
192
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800193#if !LINKER_DEBUG_TO_LOG
194
195/*** File descriptor output
196 ***/
197
Elliott Hughes18a206c2012-10-29 17:37:13 -0700198struct FdOut {
199 Out out[1];
200 int fd;
201 int total;
202};
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800203
204static void
205fd_out_send(void *opaque, const char *data, int len)
206{
207 FdOut *fdo = opaque;
208
209 if (len < 0)
210 len = strlen(data);
211
212 while (len > 0) {
213 int ret = write(fdo->fd, data, len);
214 if (ret < 0) {
215 if (errno == EINTR)
216 continue;
217 break;
218 }
219 data += ret;
220 len -= ret;
221 fdo->total += ret;
222 }
223}
224
225static Out*
226fd_out_init(FdOut *fdo, int fd)
227{
228 fdo->out->opaque = fdo;
229 fdo->out->send = fd_out_send;
230 fdo->fd = fd;
231 fdo->total = 0;
232
233 return fdo->out;
234}
235
236static int
237fd_out_length(FdOut *fdo)
238{
239 return fdo->total;
240}
241
242
243int
244format_fd(int fd, const char *format, ...)
245{
246 FdOut fdo;
247 Out* out;
248 va_list args;
249
250 out = fd_out_init(&fdo, fd);
251 if (out == NULL)
252 return 0;
253
254 va_start(args, format);
255 out_vformat(out, format, args);
256 va_end(args);
257
258 return fd_out_length(&fdo);
259}
260
261#else /* LINKER_DEBUG_TO_LOG */
262
263/*** Log output
264 ***/
265
266/* We need our own version of __libc_android_log_vprint, otherwise
267 * the log output is completely broken. Probably due to the fact
268 * that the C library is not initialized yet.
269 *
270 * You can test that by setting CUSTOM_LOG_VPRINT to 0
271 */
272#define CUSTOM_LOG_VPRINT 1
273
274#if CUSTOM_LOG_VPRINT
275
276#include <unistd.h>
277#include <fcntl.h>
278#include <sys/uio.h>
279
280static int log_vprint(int prio, const char *tag, const char *fmt, va_list args)
281{
282 char buf[1024];
283 int result;
284 static int log_fd = -1;
285
286 result = vformat_buffer(buf, sizeof buf, fmt, args);
287
288 if (log_fd < 0) {
289 log_fd = open("/dev/log/main", O_WRONLY);
290 if (log_fd < 0)
291 return result;
292 }
293
294 {
295 ssize_t ret;
296 struct iovec vec[3];
297
298 vec[0].iov_base = (unsigned char *) &prio;
299 vec[0].iov_len = 1;
300 vec[1].iov_base = (void *) tag;
301 vec[1].iov_len = strlen(tag) + 1;
302 vec[2].iov_base = (void *) buf;
303 vec[2].iov_len = strlen(buf) + 1;
304
305 do {
306 ret = writev(log_fd, vec, 3);
307 } while ((ret < 0) && (errno == EINTR));
308 }
309 return result;
310}
311
312#define __libc_android_log_vprint log_vprint
313
314#else /* !CUSTOM_LOG_VPRINT */
315
Elliott Hughes46882792012-08-03 16:49:39 -0700316extern "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 -0800317
318#endif /* !CUSTOM_LOG_VPRINT */
319
320int
321format_log(int prio, const char *tag, const char *format, ...)
322{
323 int ret;
324 va_list args;
325 va_start(args, format);
326 ret = __libc_android_log_vprint(prio, tag, format, args);
327 va_end(args);
328 return ret;
329}
330
331#endif /* LINKER_DEBUG_TO_LOG */
332
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800333/*** formatted output implementation
334 ***/
335
336/* Parse a decimal string from 'format + *ppos',
337 * return the value, and writes the new position past
338 * the decimal string in '*ppos' on exit.
339 *
340 * NOTE: Does *not* handle a sign prefix.
341 */
342static unsigned
343parse_decimal(const char *format, int *ppos)
344{
345 const char* p = format + *ppos;
346 unsigned result = 0;
347
348 for (;;) {
349 int ch = *p;
350 unsigned d = (unsigned)(ch - '0');
351
352 if (d >= 10U)
353 break;
354
355 result = result*10 + d;
356 p++;
357 }
358 *ppos = p - format;
359 return result;
360}
361
Elliott Hugheseababde2012-12-20 18:59:05 -0800362// Writes number 'value' in base 'base' into buffer 'buf' of size 'buf_size' bytes.
363// Assumes that buf_size > 0.
364static void format_number(char* buf, size_t buf_size, uint64_t value, int base, bool caps) {
365 char* p = buf;
366 char* end = buf + buf_size - 1;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800367
Elliott Hugheseababde2012-12-20 18:59:05 -0800368 // Generate digit string in reverse order.
369 while (value) {
370 unsigned d = value % base;
371 value /= base;
372 if (p != end) {
373 char ch;
374 if (d < 10) {
375 ch = '0' + d;
376 } else {
377 ch = (caps ? 'A' : 'a') + (d - 10);
378 }
379 *p++ = ch;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800380 }
Elliott Hugheseababde2012-12-20 18:59:05 -0800381 }
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800382
Elliott Hugheseababde2012-12-20 18:59:05 -0800383 // Special case for 0.
384 if (p == buf) {
385 if (p != end) {
386 *p++ = '0';
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800387 }
Elliott Hugheseababde2012-12-20 18:59:05 -0800388 }
389 *p = '\0';
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800390
Elliott Hugheseababde2012-12-20 18:59:05 -0800391 // Reverse digit string in-place.
392 size_t length = p - buf;
393 for (size_t i = 0, j = length - 1; i < j; ++i, --j) {
394 char ch = buf[i];
395 buf[i] = buf[j];
396 buf[j] = ch;
397 }
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800398}
399
400/* Write an integer (octal or decimal) into a buffer, assumes buffsize > 2 */
401static void
402format_integer(char *buffer, size_t buffsize, uint64_t value, int base, int isSigned)
403{
Elliott Hugheseababde2012-12-20 18:59:05 -0800404 // TODO: this is incorrect for MIN_INT.
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800405 if (isSigned && (int64_t)value < 0) {
406 buffer[0] = '-';
407 buffer += 1;
408 buffsize -= 1;
409 value = (uint64_t)(-(int64_t)value);
410 }
411
Elliott Hugheseababde2012-12-20 18:59:05 -0800412 format_number(buffer, buffsize, value, base, false);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800413}
414
Elliott Hugheseababde2012-12-20 18:59:05 -0800415// Assumes buf_size > 2.
416static void format_hex(char* buf, size_t buf_size, uint64_t value, bool caps) {
417 format_number(buf, buf_size, value, 16, caps);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800418}
419
420
421/* Perform formatted output to an output target 'o' */
422static void
423out_vformat(Out *o, const char *format, va_list args)
424{
Andy McFaddenec92af82011-07-29 12:46:34 -0700425 int nn = 0;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800426
427 for (;;) {
Andy McFaddenec92af82011-07-29 12:46:34 -0700428 int mm;
429 int padZero = 0;
430 int padLeft = 0;
431 char sign = '\0';
432 int width = -1;
433 int prec = -1;
434 size_t bytelen = sizeof(int);
435 const char* str;
436 int slen;
437 char buffer[32]; /* temporary buffer used to format numbers */
438
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800439 char c;
440
441 /* first, find all characters that are not 0 or '%' */
442 /* then send them to the output directly */
443 mm = nn;
444 do {
445 c = format[mm];
446 if (c == '\0' || c == '%')
447 break;
448 mm++;
449 } while (1);
450
451 if (mm > nn) {
452 out_send(o, format+nn, mm-nn);
453 nn = mm;
454 }
455
456 /* is this it ? then exit */
457 if (c == '\0')
458 break;
459
460 /* nope, we are at a '%' modifier */
461 nn++; // skip it
462
463 /* parse flags */
464 for (;;) {
465 c = format[nn++];
466 if (c == '\0') { /* single trailing '%' ? */
467 c = '%';
468 out_send(o, &c, 1);
469 return;
470 }
471 else if (c == '0') {
472 padZero = 1;
473 continue;
474 }
475 else if (c == '-') {
476 padLeft = 1;
477 continue;
478 }
479 else if (c == ' ' || c == '+') {
480 sign = c;
481 continue;
482 }
483 break;
484 }
485
486 /* parse field width */
487 if ((c >= '0' && c <= '9')) {
488 nn --;
489 width = (int)parse_decimal(format, &nn);
490 c = format[nn++];
491 }
492
493 /* parse precision */
494 if (c == '.') {
495 prec = (int)parse_decimal(format, &nn);
496 c = format[nn++];
497 }
498
499 /* length modifier */
500 switch (c) {
501 case 'h':
502 bytelen = sizeof(short);
503 if (format[nn] == 'h') {
504 bytelen = sizeof(char);
505 nn += 1;
506 }
507 c = format[nn++];
508 break;
509 case 'l':
510 bytelen = sizeof(long);
511 if (format[nn] == 'l') {
512 bytelen = sizeof(long long);
513 nn += 1;
514 }
515 c = format[nn++];
516 break;
517 case 'z':
518 bytelen = sizeof(size_t);
519 c = format[nn++];
520 break;
521 case 't':
522 bytelen = sizeof(ptrdiff_t);
523 c = format[nn++];
524 break;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800525 default:
526 ;
527 }
528
529 /* conversion specifier */
530 if (c == 's') {
531 /* string */
532 str = va_arg(args, const char*);
533 } else if (c == 'c') {
534 /* character */
535 /* NOTE: char is promoted to int when passed through the stack */
536 buffer[0] = (char) va_arg(args, int);
537 buffer[1] = '\0';
538 str = buffer;
539 } else if (c == 'p') {
Andy McFaddenec92af82011-07-29 12:46:34 -0700540 uint64_t value = (uintptr_t) va_arg(args, void*);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800541 buffer[0] = '0';
542 buffer[1] = 'x';
Elliott Hugheseababde2012-12-20 18:59:05 -0800543 format_hex(buffer + 2, sizeof buffer-2, value, false);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800544 str = buffer;
545 } else {
546 /* integers - first read value from stack */
547 uint64_t value;
548 int isSigned = (c == 'd' || c == 'i' || c == 'o');
549
550 /* NOTE: int8_t and int16_t are promoted to int when passed
551 * through the stack
552 */
553 switch (bytelen) {
554 case 1: value = (uint8_t) va_arg(args, int); break;
555 case 2: value = (uint16_t) va_arg(args, int); break;
556 case 4: value = va_arg(args, uint32_t); break;
557 case 8: value = va_arg(args, uint64_t); break;
558 default: return; /* should not happen */
559 }
560
561 /* sign extension, if needed */
562 if (isSigned) {
563 int shift = 64 - 8*bytelen;
564 value = (uint64_t)(((int64_t)(value << shift)) >> shift);
565 }
566
567 /* format the number properly into our buffer */
568 switch (c) {
569 case 'i': case 'd':
570 format_integer(buffer, sizeof buffer, value, 10, isSigned);
571 break;
572 case 'o':
573 format_integer(buffer, sizeof buffer, value, 8, isSigned);
574 break;
575 case 'x': case 'X':
576 format_hex(buffer, sizeof buffer, value, (c == 'X'));
577 break;
578 default:
579 buffer[0] = '\0';
580 }
581 /* then point to it */
582 str = buffer;
583 }
584
585 /* if we are here, 'str' points to the content that must be
586 * outputted. handle padding and alignment now */
587
588 slen = strlen(str);
589
Elliott Hughes18a206c2012-10-29 17:37:13 -0700590 if (sign != '\0' || prec != -1) {
591 __assert(__FILE__, __LINE__, "sign/precision unsupported");
592 }
593
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800594 if (slen < width && !padLeft) {
595 char padChar = padZero ? '0' : ' ';
596 out_send_repeat(o, padChar, width - slen);
597 }
598
599 out_send(o, str, slen);
600
601 if (slen < width && padLeft) {
602 char padChar = padZero ? '0' : ' ';
603 out_send_repeat(o, padChar, width - slen);
604 }
605 }
606}
607
608
609#ifdef UNIT_TESTS
610
611#include <stdio.h>
612
613static int gFails = 0;
614
615#define MARGIN 40
616
617#define UTEST_CHECK(condition,message) \
618 printf("Checking %-*s: ", MARGIN, message); fflush(stdout); \
619 if (!(condition)) { \
620 printf("KO\n"); \
621 gFails += 1; \
622 } else { \
623 printf("ok\n"); \
624 }
625
626static void
627utest_BufOut(void)
628{
629 char buffer[16];
630 BufOut bo[1];
631 Out* out;
632 int ret;
633
634 buffer[0] = '1';
635 out = buf_out_init(bo, buffer, sizeof buffer);
636 UTEST_CHECK(buffer[0] == '\0', "buf_out_init clears initial byte");
637 out_send(out, "abc", 3);
638 UTEST_CHECK(!memcmp(buffer, "abc", 4), "out_send() works with BufOut");
639 out_send_repeat(out, 'X', 4);
640 UTEST_CHECK(!memcmp(buffer, "abcXXXX", 8), "out_send_repeat() works with BufOut");
641 buffer[sizeof buffer-1] = 'x';
642 out_send_repeat(out, 'Y', 2*sizeof(buffer));
643 UTEST_CHECK(buffer[sizeof buffer-1] == '\0', "overflows always zero-terminates");
644
645 out = buf_out_init(bo, buffer, sizeof buffer);
646 out_send_repeat(out, 'X', 2*sizeof(buffer));
647 ret = buf_out_length(bo);
648 UTEST_CHECK(ret == 2*sizeof(buffer), "correct size returned on overflow");
649}
650
651static void
652utest_expect(const char* result, const char* format, ...)
653{
654 va_list args;
655 BufOut bo[1];
656 char buffer[256];
657 Out* out = buf_out_init(bo, buffer, sizeof buffer);
658
659 printf("Checking %-*s: ", MARGIN, format); fflush(stdout);
660 va_start(args, format);
661 out_vformat(out, format, args);
662 va_end(args);
663
664 if (strcmp(result, buffer)) {
665 printf("KO. got '%s' expecting '%s'\n", buffer, result);
666 gFails += 1;
667 } else {
668 printf("ok. got '%s'\n", result);
669 }
670}
671
672int main(void)
673{
674 utest_BufOut();
675 utest_expect("", "");
676 utest_expect("a", "a");
677 utest_expect("01234", "01234", "");
678 utest_expect("01234", "%s", "01234");
679 utest_expect("aabbcc", "aa%scc", "bb");
680 utest_expect("a", "%c", 'a');
681 utest_expect("1234", "%d", 1234);
682 utest_expect("-8123", "%d", -8123);
683 utest_expect("16", "%hd", 0x7fff0010);
684 utest_expect("16", "%hhd", 0x7fffff10);
Andy McFaddenec92af82011-07-29 12:46:34 -0700685 utest_expect("68719476736", "%lld", 0x1000000000LL);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800686 utest_expect("70000", "%ld", 70000);
687 utest_expect("0xb0001234", "%p", (void*)0xb0001234);
688 utest_expect("12ab", "%x", 0x12ab);
689 utest_expect("12AB", "%X", 0x12ab);
690 utest_expect("00123456", "%08x", 0x123456);
691 utest_expect("01234", "0%d", 1234);
692 utest_expect(" 1234", "%5d", 1234);
693 utest_expect("01234", "%05d", 1234);
694 utest_expect(" 1234", "%8d", 1234);
695 utest_expect("1234 ", "%-8d", 1234);
696 utest_expect("abcdef ", "%-11s", "abcdef");
697 utest_expect("something:1234", "%s:%d", "something", 1234);
Andy McFaddenec92af82011-07-29 12:46:34 -0700698 utest_expect("005:5:05", "%03d:%d:%02d", 5, 5, 5);
699 utest_expect("5,0x0", "%d,%p", 5, NULL);
700 utest_expect("68719476736,6,7,8", "%lld,%d,%d,%d", 0x1000000000LL, 6, 7, 8);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800701 return gFails != 0;
702}
703
704#endif /* UNIT_TESTS */