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