blob: 60b759bf3353ac913e14956340a28ef190a938df [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_TO_LOG
188
189/*** File descriptor output
190 ***/
191
Elliott Hughes18a206c2012-10-29 17:37:13 -0700192struct FdOut {
193 Out out[1];
194 int fd;
195 int total;
196};
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800197
198static void
199fd_out_send(void *opaque, const char *data, int len)
200{
201 FdOut *fdo = opaque;
202
203 if (len < 0)
204 len = strlen(data);
205
206 while (len > 0) {
207 int ret = write(fdo->fd, data, len);
208 if (ret < 0) {
209 if (errno == EINTR)
210 continue;
211 break;
212 }
213 data += ret;
214 len -= ret;
215 fdo->total += ret;
216 }
217}
218
219static Out*
220fd_out_init(FdOut *fdo, int fd)
221{
222 fdo->out->opaque = fdo;
223 fdo->out->send = fd_out_send;
224 fdo->fd = fd;
225 fdo->total = 0;
226
227 return fdo->out;
228}
229
230static int
231fd_out_length(FdOut *fdo)
232{
233 return fdo->total;
234}
235
236
237int
238format_fd(int fd, const char *format, ...)
239{
240 FdOut fdo;
241 Out* out;
242 va_list args;
243
244 out = fd_out_init(&fdo, fd);
245 if (out == NULL)
246 return 0;
247
248 va_start(args, format);
249 out_vformat(out, format, args);
250 va_end(args);
251
252 return fd_out_length(&fdo);
253}
254
255#else /* LINKER_DEBUG_TO_LOG */
256
257/*** Log output
258 ***/
259
260/* We need our own version of __libc_android_log_vprint, otherwise
261 * the log output is completely broken. Probably due to the fact
262 * that the C library is not initialized yet.
263 *
264 * You can test that by setting CUSTOM_LOG_VPRINT to 0
265 */
266#define CUSTOM_LOG_VPRINT 1
267
268#if CUSTOM_LOG_VPRINT
269
270#include <unistd.h>
271#include <fcntl.h>
272#include <sys/uio.h>
273
274static int log_vprint(int prio, const char *tag, const char *fmt, va_list args)
275{
276 char buf[1024];
277 int result;
278 static int log_fd = -1;
279
280 result = vformat_buffer(buf, sizeof buf, fmt, args);
281
282 if (log_fd < 0) {
283 log_fd = open("/dev/log/main", O_WRONLY);
284 if (log_fd < 0)
285 return result;
286 }
287
288 {
289 ssize_t ret;
290 struct iovec vec[3];
291
292 vec[0].iov_base = (unsigned char *) &prio;
293 vec[0].iov_len = 1;
294 vec[1].iov_base = (void *) tag;
295 vec[1].iov_len = strlen(tag) + 1;
296 vec[2].iov_base = (void *) buf;
297 vec[2].iov_len = strlen(buf) + 1;
298
299 do {
300 ret = writev(log_fd, vec, 3);
301 } while ((ret < 0) && (errno == EINTR));
302 }
303 return result;
304}
305
306#define __libc_android_log_vprint log_vprint
307
308#else /* !CUSTOM_LOG_VPRINT */
309
Elliott Hughes46882792012-08-03 16:49:39 -0700310extern "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 -0800311
312#endif /* !CUSTOM_LOG_VPRINT */
313
314int
315format_log(int prio, const char *tag, const char *format, ...)
316{
317 int ret;
318 va_list args;
319 va_start(args, format);
320 ret = __libc_android_log_vprint(prio, tag, format, args);
321 va_end(args);
322 return ret;
323}
324
325#endif /* LINKER_DEBUG_TO_LOG */
326
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800327/*** formatted output implementation
328 ***/
329
330/* Parse a decimal string from 'format + *ppos',
331 * return the value, and writes the new position past
332 * the decimal string in '*ppos' on exit.
333 *
334 * NOTE: Does *not* handle a sign prefix.
335 */
336static unsigned
337parse_decimal(const char *format, int *ppos)
338{
339 const char* p = format + *ppos;
340 unsigned result = 0;
341
342 for (;;) {
343 int ch = *p;
344 unsigned d = (unsigned)(ch - '0');
345
346 if (d >= 10U)
347 break;
348
349 result = result*10 + d;
350 p++;
351 }
352 *ppos = p - format;
353 return result;
354}
355
356/* write an octal/decimal/number into a bounded buffer.
357 * assumes that bufsize > 0, and 'digits' is a string of
358 * digits of at least 'base' values.
359 */
360static void
361format_number(char *buffer, size_t bufsize, uint64_t value, int base, const char *digits)
362{
363 char *pos = buffer;
364 char *end = buffer + bufsize - 1;
365
366 /* generate digit string in reverse order */
367 while (value) {
368 unsigned d = value % base;
369 value /= base;
370 if (pos < end) {
371 *pos++ = digits[d];
372 }
373 }
374
375 /* special case for 0 */
376 if (pos == buffer) {
377 if (pos < end) {
378 *pos++ = '0';
379 }
380 }
381 pos[0] = '\0';
382
383 /* now reverse digit string in-place */
384 end = pos - 1;
385 pos = buffer;
386 while (pos < end) {
387 int ch = pos[0];
388 pos[0] = end[0];
389 end[0] = (char) ch;
390 pos++;
391 end--;
392 }
393}
394
395/* Write an integer (octal or decimal) into a buffer, assumes buffsize > 2 */
396static void
397format_integer(char *buffer, size_t buffsize, uint64_t value, int base, int isSigned)
398{
399 if (isSigned && (int64_t)value < 0) {
400 buffer[0] = '-';
401 buffer += 1;
402 buffsize -= 1;
403 value = (uint64_t)(-(int64_t)value);
404 }
405
406 format_number(buffer, buffsize, value, base, "0123456789");
407}
408
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800409/* Write an hexadecimal into a buffer, isCap is true for capital alphas.
410 * Assumes bufsize > 2 */
411static void
412format_hex(char *buffer, size_t buffsize, uint64_t value, int isCap)
413{
414 const char *digits = isCap ? "0123456789ABCDEF" : "0123456789abcdef";
415
416 format_number(buffer, buffsize, value, 16, digits);
417}
418
419
420/* Perform formatted output to an output target 'o' */
421static void
422out_vformat(Out *o, const char *format, va_list args)
423{
Andy McFaddenec92af82011-07-29 12:46:34 -0700424 int nn = 0;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800425
426 for (;;) {
Andy McFaddenec92af82011-07-29 12:46:34 -0700427 int mm;
428 int padZero = 0;
429 int padLeft = 0;
430 char sign = '\0';
431 int width = -1;
432 int prec = -1;
433 size_t bytelen = sizeof(int);
434 const char* str;
435 int slen;
436 char buffer[32]; /* temporary buffer used to format numbers */
437
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800438 char c;
439
440 /* first, find all characters that are not 0 or '%' */
441 /* then send them to the output directly */
442 mm = nn;
443 do {
444 c = format[mm];
445 if (c == '\0' || c == '%')
446 break;
447 mm++;
448 } while (1);
449
450 if (mm > nn) {
451 out_send(o, format+nn, mm-nn);
452 nn = mm;
453 }
454
455 /* is this it ? then exit */
456 if (c == '\0')
457 break;
458
459 /* nope, we are at a '%' modifier */
460 nn++; // skip it
461
462 /* parse flags */
463 for (;;) {
464 c = format[nn++];
465 if (c == '\0') { /* single trailing '%' ? */
466 c = '%';
467 out_send(o, &c, 1);
468 return;
469 }
470 else if (c == '0') {
471 padZero = 1;
472 continue;
473 }
474 else if (c == '-') {
475 padLeft = 1;
476 continue;
477 }
478 else if (c == ' ' || c == '+') {
479 sign = c;
480 continue;
481 }
482 break;
483 }
484
485 /* parse field width */
486 if ((c >= '0' && c <= '9')) {
487 nn --;
488 width = (int)parse_decimal(format, &nn);
489 c = format[nn++];
490 }
491
492 /* parse precision */
493 if (c == '.') {
494 prec = (int)parse_decimal(format, &nn);
495 c = format[nn++];
496 }
497
498 /* length modifier */
499 switch (c) {
500 case 'h':
501 bytelen = sizeof(short);
502 if (format[nn] == 'h') {
503 bytelen = sizeof(char);
504 nn += 1;
505 }
506 c = format[nn++];
507 break;
508 case 'l':
509 bytelen = sizeof(long);
510 if (format[nn] == 'l') {
511 bytelen = sizeof(long long);
512 nn += 1;
513 }
514 c = format[nn++];
515 break;
516 case 'z':
517 bytelen = sizeof(size_t);
518 c = format[nn++];
519 break;
520 case 't':
521 bytelen = sizeof(ptrdiff_t);
522 c = format[nn++];
523 break;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800524 default:
525 ;
526 }
527
528 /* conversion specifier */
529 if (c == 's') {
530 /* string */
531 str = va_arg(args, const char*);
532 } else if (c == 'c') {
533 /* character */
534 /* NOTE: char is promoted to int when passed through the stack */
535 buffer[0] = (char) va_arg(args, int);
536 buffer[1] = '\0';
537 str = buffer;
538 } else if (c == 'p') {
Andy McFaddenec92af82011-07-29 12:46:34 -0700539 uint64_t value = (uintptr_t) va_arg(args, void*);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800540 buffer[0] = '0';
541 buffer[1] = 'x';
542 format_hex(buffer + 2, sizeof buffer-2, value, 0);
543 str = buffer;
544 } else {
545 /* integers - first read value from stack */
546 uint64_t value;
547 int isSigned = (c == 'd' || c == 'i' || c == 'o');
548
549 /* NOTE: int8_t and int16_t are promoted to int when passed
550 * through the stack
551 */
552 switch (bytelen) {
553 case 1: value = (uint8_t) va_arg(args, int); break;
554 case 2: value = (uint16_t) va_arg(args, int); break;
555 case 4: value = va_arg(args, uint32_t); break;
556 case 8: value = va_arg(args, uint64_t); break;
557 default: return; /* should not happen */
558 }
559
560 /* sign extension, if needed */
561 if (isSigned) {
562 int shift = 64 - 8*bytelen;
563 value = (uint64_t)(((int64_t)(value << shift)) >> shift);
564 }
565
566 /* format the number properly into our buffer */
567 switch (c) {
568 case 'i': case 'd':
569 format_integer(buffer, sizeof buffer, value, 10, isSigned);
570 break;
571 case 'o':
572 format_integer(buffer, sizeof buffer, value, 8, isSigned);
573 break;
574 case 'x': case 'X':
575 format_hex(buffer, sizeof buffer, value, (c == 'X'));
576 break;
577 default:
578 buffer[0] = '\0';
579 }
580 /* then point to it */
581 str = buffer;
582 }
583
584 /* if we are here, 'str' points to the content that must be
585 * outputted. handle padding and alignment now */
586
587 slen = strlen(str);
588
Elliott Hughes18a206c2012-10-29 17:37:13 -0700589 if (sign != '\0' || prec != -1) {
590 __assert(__FILE__, __LINE__, "sign/precision unsupported");
591 }
592
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800593 if (slen < width && !padLeft) {
594 char padChar = padZero ? '0' : ' ';
595 out_send_repeat(o, padChar, width - slen);
596 }
597
598 out_send(o, str, slen);
599
600 if (slen < width && padLeft) {
601 char padChar = padZero ? '0' : ' ';
602 out_send_repeat(o, padChar, width - slen);
603 }
604 }
605}
606
607
608#ifdef UNIT_TESTS
609
610#include <stdio.h>
611
612static int gFails = 0;
613
614#define MARGIN 40
615
616#define UTEST_CHECK(condition,message) \
617 printf("Checking %-*s: ", MARGIN, message); fflush(stdout); \
618 if (!(condition)) { \
619 printf("KO\n"); \
620 gFails += 1; \
621 } else { \
622 printf("ok\n"); \
623 }
624
625static void
626utest_BufOut(void)
627{
628 char buffer[16];
629 BufOut bo[1];
630 Out* out;
631 int ret;
632
633 buffer[0] = '1';
634 out = buf_out_init(bo, buffer, sizeof buffer);
635 UTEST_CHECK(buffer[0] == '\0', "buf_out_init clears initial byte");
636 out_send(out, "abc", 3);
637 UTEST_CHECK(!memcmp(buffer, "abc", 4), "out_send() works with BufOut");
638 out_send_repeat(out, 'X', 4);
639 UTEST_CHECK(!memcmp(buffer, "abcXXXX", 8), "out_send_repeat() works with BufOut");
640 buffer[sizeof buffer-1] = 'x';
641 out_send_repeat(out, 'Y', 2*sizeof(buffer));
642 UTEST_CHECK(buffer[sizeof buffer-1] == '\0', "overflows always zero-terminates");
643
644 out = buf_out_init(bo, buffer, sizeof buffer);
645 out_send_repeat(out, 'X', 2*sizeof(buffer));
646 ret = buf_out_length(bo);
647 UTEST_CHECK(ret == 2*sizeof(buffer), "correct size returned on overflow");
648}
649
650static void
651utest_expect(const char* result, const char* format, ...)
652{
653 va_list args;
654 BufOut bo[1];
655 char buffer[256];
656 Out* out = buf_out_init(bo, buffer, sizeof buffer);
657
658 printf("Checking %-*s: ", MARGIN, format); fflush(stdout);
659 va_start(args, format);
660 out_vformat(out, format, args);
661 va_end(args);
662
663 if (strcmp(result, buffer)) {
664 printf("KO. got '%s' expecting '%s'\n", buffer, result);
665 gFails += 1;
666 } else {
667 printf("ok. got '%s'\n", result);
668 }
669}
670
671int main(void)
672{
673 utest_BufOut();
674 utest_expect("", "");
675 utest_expect("a", "a");
676 utest_expect("01234", "01234", "");
677 utest_expect("01234", "%s", "01234");
678 utest_expect("aabbcc", "aa%scc", "bb");
679 utest_expect("a", "%c", 'a');
680 utest_expect("1234", "%d", 1234);
681 utest_expect("-8123", "%d", -8123);
682 utest_expect("16", "%hd", 0x7fff0010);
683 utest_expect("16", "%hhd", 0x7fffff10);
Andy McFaddenec92af82011-07-29 12:46:34 -0700684 utest_expect("68719476736", "%lld", 0x1000000000LL);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800685 utest_expect("70000", "%ld", 70000);
686 utest_expect("0xb0001234", "%p", (void*)0xb0001234);
687 utest_expect("12ab", "%x", 0x12ab);
688 utest_expect("12AB", "%X", 0x12ab);
689 utest_expect("00123456", "%08x", 0x123456);
690 utest_expect("01234", "0%d", 1234);
691 utest_expect(" 1234", "%5d", 1234);
692 utest_expect("01234", "%05d", 1234);
693 utest_expect(" 1234", "%8d", 1234);
694 utest_expect("1234 ", "%-8d", 1234);
695 utest_expect("abcdef ", "%-11s", "abcdef");
696 utest_expect("something:1234", "%s:%d", "something", 1234);
Andy McFaddenec92af82011-07-29 12:46:34 -0700697 utest_expect("005:5:05", "%03d:%d:%02d", 5, 5, 5);
698 utest_expect("5,0x0", "%d,%p", 5, NULL);
699 utest_expect("68719476736,6,7,8", "%lld,%d,%d,%d", 0x1000000000LL, 6, 7, 8);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800700 return gFails != 0;
701}
702
703#endif /* UNIT_TESTS */