Ruby
2.0.0p247(2013-06-27revision41674)
|
00001 /********************************************************************** 00002 00003 addr2line.c - 00004 00005 $Author$ 00006 00007 Copyright (C) 2010 Shinichiro Hamaji 00008 00009 **********************************************************************/ 00010 00011 #include "ruby/config.h" 00012 #include "addr2line.h" 00013 00014 #include <stdio.h> 00015 #include <errno.h> 00016 00017 #ifdef USE_ELF 00018 00019 #ifdef __OpenBSD__ 00020 #include <elf_abi.h> 00021 #else 00022 #include <elf.h> 00023 #endif 00024 #include <fcntl.h> 00025 #include <limits.h> 00026 #include <stdio.h> 00027 #include <stdint.h> 00028 #include <stdlib.h> 00029 #include <string.h> 00030 #include <sys/mman.h> 00031 #include <sys/types.h> 00032 #include <sys/stat.h> 00033 #include <unistd.h> 00034 00035 /* Make alloca work the best possible way. */ 00036 #ifdef __GNUC__ 00037 # ifndef atarist 00038 # ifndef alloca 00039 # define alloca __builtin_alloca 00040 # endif 00041 # endif /* atarist */ 00042 #else 00043 # ifdef HAVE_ALLOCA_H 00044 # include <alloca.h> 00045 # else 00046 # ifdef _AIX 00047 #pragma alloca 00048 # else 00049 # ifndef alloca /* predefined by HP cc +Olibcalls */ 00050 void *alloca(); 00051 # endif 00052 # endif /* AIX */ 00053 # endif /* HAVE_ALLOCA_H */ 00054 #endif /* __GNUC__ */ 00055 00056 #ifdef HAVE_DL_ITERATE_PHDR 00057 # ifndef _GNU_SOURCE 00058 # define _GNU_SOURCE 00059 # endif 00060 # include <link.h> 00061 #endif 00062 00063 #define DW_LNS_copy 0x01 00064 #define DW_LNS_advance_pc 0x02 00065 #define DW_LNS_advance_line 0x03 00066 #define DW_LNS_set_file 0x04 00067 #define DW_LNS_set_column 0x05 00068 #define DW_LNS_negate_stmt 0x06 00069 #define DW_LNS_set_basic_block 0x07 00070 #define DW_LNS_const_add_pc 0x08 00071 #define DW_LNS_fixed_advance_pc 0x09 00072 #define DW_LNS_set_prologue_end 0x0a /* DWARF3 */ 00073 #define DW_LNS_set_epilogue_begin 0x0b /* DWARF3 */ 00074 #define DW_LNS_set_isa 0x0c /* DWARF3 */ 00075 00076 /* Line number extended opcode name. */ 00077 #define DW_LNE_end_sequence 0x01 00078 #define DW_LNE_set_address 0x02 00079 #define DW_LNE_define_file 0x03 00080 #define DW_LNE_set_discriminator 0x04 /* DWARF4 */ 00081 00082 #ifndef ElfW 00083 # if SIZEOF_VOIDP == 8 00084 # define ElfW(x) Elf64##_##x 00085 # else 00086 # define ElfW(x) Elf32##_##x 00087 # endif 00088 #endif 00089 #ifndef PATH_MAX 00090 #define PATH_MAX 4096 00091 #endif 00092 00093 int kprintf(const char *fmt, ...); 00094 00095 typedef struct { 00096 const char *dirname; 00097 const char *filename; 00098 int line; 00099 00100 int fd; 00101 void *mapped; 00102 size_t mapped_size; 00103 unsigned long base_addr; 00104 } line_info_t; 00105 00106 /* Avoid consuming stack as this module may be used from signal handler */ 00107 static char binary_filename[PATH_MAX]; 00108 00109 static unsigned long 00110 uleb128(char **p) 00111 { 00112 unsigned long r = 0; 00113 int s = 0; 00114 for (;;) { 00115 unsigned char b = *(unsigned char *)(*p)++; 00116 if (b < 0x80) { 00117 r += (unsigned long)b << s; 00118 break; 00119 } 00120 r += (b & 0x7f) << s; 00121 s += 7; 00122 } 00123 return r; 00124 } 00125 00126 static long 00127 sleb128(char **p) 00128 { 00129 long r = 0; 00130 int s = 0; 00131 for (;;) { 00132 unsigned char b = *(unsigned char *)(*p)++; 00133 if (b < 0x80) { 00134 if (b & 0x40) { 00135 r -= (0x80 - b) << s; 00136 } 00137 else { 00138 r += (b & 0x3f) << s; 00139 } 00140 break; 00141 } 00142 r += (b & 0x7f) << s; 00143 s += 7; 00144 } 00145 return r; 00146 } 00147 00148 static const char * 00149 get_nth_dirname(unsigned long dir, char *p) 00150 { 00151 if (!dir--) { 00152 return ""; 00153 } 00154 while (dir--) { 00155 while (*p) p++; 00156 p++; 00157 if (!*p) { 00158 kprintf("Unexpected directory number %lu in %s\n", 00159 dir, binary_filename); 00160 return ""; 00161 } 00162 } 00163 return p; 00164 } 00165 00166 static void 00167 fill_filename(int file, char *include_directories, char *filenames, 00168 line_info_t *line) 00169 { 00170 int i; 00171 char *p = filenames; 00172 char *filename; 00173 unsigned long dir; 00174 for (i = 1; i <= file; i++) { 00175 filename = p; 00176 if (!*p) { 00177 /* Need to output binary file name? */ 00178 kprintf("Unexpected file number %d in %s\n", 00179 file, binary_filename); 00180 return; 00181 } 00182 while (*p) p++; 00183 p++; 00184 dir = uleb128(&p); 00185 /* last modified. */ 00186 uleb128(&p); 00187 /* size of the file. */ 00188 uleb128(&p); 00189 00190 if (i == file) { 00191 line->filename = filename; 00192 line->dirname = get_nth_dirname(dir, include_directories); 00193 } 00194 } 00195 } 00196 00197 static int 00198 get_path_from_symbol(const char *symbol, const char **p, size_t *len) 00199 { 00200 if (symbol[0] == '0') { 00201 /* libexecinfo */ 00202 *p = strchr(symbol, '/'); 00203 if (*p == NULL) return 0; 00204 *len = strlen(*p); 00205 } 00206 else { 00207 /* glibc */ 00208 const char *q; 00209 *p = symbol; 00210 q = strchr(symbol, '('); 00211 if (q == NULL) return 0; 00212 *len = q - symbol; 00213 } 00214 return 1; 00215 } 00216 00217 static void 00218 fill_line(int num_traces, void **traces, 00219 unsigned long addr, int file, int line, 00220 char *include_directories, char *filenames, line_info_t *lines) 00221 { 00222 int i; 00223 for (i = 0; i < num_traces; i++) { 00224 unsigned long a = (unsigned long)traces[i] - lines[i].base_addr; 00225 /* We assume one line code doesn't result >100 bytes of native code. 00226 We may want more reliable way eventually... */ 00227 if (addr < a && a < addr + 100) { 00228 fill_filename(file, include_directories, filenames, &lines[i]); 00229 lines[i].line = line; 00230 } 00231 } 00232 } 00233 00234 static void 00235 parse_debug_line_cu(int num_traces, void **traces, 00236 char **debug_line, line_info_t *lines) 00237 { 00238 char *p, *cu_end, *cu_start, *include_directories, *filenames; 00239 unsigned long unit_length; 00240 int default_is_stmt, line_base; 00241 unsigned int header_length, minimum_instruction_length, line_range, 00242 opcode_base; 00243 /* unsigned char *standard_opcode_lengths; */ 00244 00245 /* The registers. */ 00246 unsigned long addr = 0; 00247 unsigned int file = 1; 00248 unsigned int line = 1; 00249 /* unsigned int column = 0; */ 00250 int is_stmt; 00251 /* int basic_block = 0; */ 00252 /* int end_sequence = 0; */ 00253 /* int prologue_end = 0; */ 00254 /* int epilogue_begin = 0; */ 00255 /* unsigned int isa = 0; */ 00256 00257 p = *debug_line; 00258 00259 unit_length = *(unsigned int *)p; 00260 p += sizeof(unsigned int); 00261 if (unit_length == 0xffffffff) { 00262 unit_length = *(unsigned long *)p; 00263 p += sizeof(unsigned long); 00264 } 00265 00266 cu_end = p + unit_length; 00267 00268 /*dwarf_version = *(unsigned short *)p;*/ 00269 p += 2; 00270 00271 header_length = *(unsigned int *)p; 00272 p += sizeof(unsigned int); 00273 00274 cu_start = p + header_length; 00275 00276 minimum_instruction_length = *(unsigned char *)p; 00277 p++; 00278 00279 is_stmt = default_is_stmt = *(unsigned char *)p; 00280 p++; 00281 00282 line_base = *(char *)p; 00283 p++; 00284 00285 line_range = *(unsigned char *)p; 00286 p++; 00287 00288 opcode_base = *(unsigned char *)p; 00289 p++; 00290 00291 /* standard_opcode_lengths = (unsigned char *)p - 1; */ 00292 p += opcode_base - 1; 00293 00294 include_directories = p; 00295 00296 /* skip include directories */ 00297 while (*p) { 00298 while (*p) p++; 00299 p++; 00300 } 00301 p++; 00302 00303 filenames = p; 00304 00305 p = cu_start; 00306 00307 #define FILL_LINE() \ 00308 do { \ 00309 fill_line(num_traces, traces, addr, file, line, \ 00310 include_directories, filenames, lines); \ 00311 /*basic_block = prologue_end = epilogue_begin = 0;*/ \ 00312 } while (0) 00313 00314 while (p < cu_end) { 00315 unsigned long a; 00316 unsigned char op = *p++; 00317 switch (op) { 00318 case DW_LNS_copy: 00319 FILL_LINE(); 00320 break; 00321 case DW_LNS_advance_pc: 00322 a = uleb128(&p); 00323 addr += a; 00324 break; 00325 case DW_LNS_advance_line: { 00326 long a = sleb128(&p); 00327 line += a; 00328 break; 00329 } 00330 case DW_LNS_set_file: 00331 file = (unsigned int)uleb128(&p); 00332 break; 00333 case DW_LNS_set_column: 00334 /*column = (unsigned int)*/(void)uleb128(&p); 00335 break; 00336 case DW_LNS_negate_stmt: 00337 is_stmt = !is_stmt; 00338 break; 00339 case DW_LNS_set_basic_block: 00340 /*basic_block = 1; */ 00341 break; 00342 case DW_LNS_const_add_pc: 00343 a = ((255 - opcode_base) / line_range) * 00344 minimum_instruction_length; 00345 addr += a; 00346 break; 00347 case DW_LNS_fixed_advance_pc: 00348 a = *(unsigned char *)p++; 00349 addr += a; 00350 break; 00351 case DW_LNS_set_prologue_end: 00352 /* prologue_end = 1; */ 00353 break; 00354 case DW_LNS_set_epilogue_begin: 00355 /* epilogue_begin = 1; */ 00356 break; 00357 case DW_LNS_set_isa: 00358 /* isa = (unsigned int)*/(void)uleb128(&p); 00359 break; 00360 case 0: 00361 a = *(unsigned char *)p++; 00362 op = *p++; 00363 switch (op) { 00364 case DW_LNE_end_sequence: 00365 /* end_sequence = 1; */ 00366 FILL_LINE(); 00367 addr = 0; 00368 file = 1; 00369 line = 1; 00370 /* column = 0; */ 00371 is_stmt = default_is_stmt; 00372 /* end_sequence = 0; */ 00373 /* isa = 0; */ 00374 break; 00375 case DW_LNE_set_address: 00376 addr = *(unsigned long *)p; 00377 p += sizeof(unsigned long); 00378 break; 00379 case DW_LNE_define_file: 00380 kprintf("Unsupported operation in %s\n", 00381 binary_filename); 00382 break; 00383 case DW_LNE_set_discriminator: 00384 /* TODO:currently ignore */ 00385 uleb128(&p); 00386 break; 00387 default: 00388 kprintf("Unknown extended opcode: %d in %s\n", 00389 op, binary_filename); 00390 } 00391 break; 00392 default: { 00393 unsigned long addr_incr; 00394 unsigned long line_incr; 00395 a = op - opcode_base; 00396 addr_incr = (a / line_range) * minimum_instruction_length; 00397 line_incr = line_base + (a % line_range); 00398 addr += (unsigned int)addr_incr; 00399 line += (unsigned int)line_incr; 00400 FILL_LINE(); 00401 } 00402 } 00403 } 00404 *debug_line = p; 00405 } 00406 00407 static void 00408 parse_debug_line(int num_traces, void **traces, 00409 char *debug_line, unsigned long size, line_info_t *lines) 00410 { 00411 char *debug_line_end = debug_line + size; 00412 while (debug_line < debug_line_end) { 00413 parse_debug_line_cu(num_traces, traces, &debug_line, lines); 00414 } 00415 if (debug_line != debug_line_end) { 00416 kprintf("Unexpected size of .debug_line in %s\n", 00417 binary_filename); 00418 } 00419 } 00420 00421 /* read file and fill lines */ 00422 static void 00423 fill_lines(int num_traces, void **traces, char **syms, int check_debuglink, 00424 line_info_t *current_line, line_info_t *lines); 00425 00426 static void 00427 follow_debuglink(char *debuglink, int num_traces, void **traces, char **syms, 00428 line_info_t *current_line, line_info_t *lines) 00429 { 00430 /* Ideally we should check 4 paths to follow gnu_debuglink, 00431 but we handle only one case for now as this format is used 00432 by some linux distributions. See GDB's info for detail. */ 00433 static const char global_debug_dir[] = "/usr/lib/debug"; 00434 char *p, *subdir; 00435 00436 p = strrchr(binary_filename, '/'); 00437 if (!p) { 00438 return; 00439 } 00440 p[1] = '\0'; 00441 00442 subdir = (char *)alloca(strlen(binary_filename) + 1); 00443 strcpy(subdir, binary_filename); 00444 strcpy(binary_filename, global_debug_dir); 00445 strncat(binary_filename, subdir, 00446 PATH_MAX - strlen(binary_filename) - 1); 00447 strncat(binary_filename, debuglink, 00448 PATH_MAX - strlen(binary_filename) - 1); 00449 00450 munmap(current_line->mapped, current_line->mapped_size); 00451 close(current_line->fd); 00452 fill_lines(num_traces, traces, syms, 0, current_line, lines); 00453 } 00454 00455 /* read file and fill lines */ 00456 static void 00457 fill_lines(int num_traces, void **traces, char **syms, int check_debuglink, 00458 line_info_t *current_line, line_info_t *lines) 00459 { 00460 int i; 00461 char *shstr; 00462 char *section_name; 00463 ElfW(Ehdr) *ehdr; 00464 ElfW(Shdr) *shdr, *shstr_shdr; 00465 ElfW(Shdr) *debug_line_shdr = NULL, *gnu_debuglink_shdr = NULL; 00466 int fd; 00467 off_t filesize; 00468 char *file; 00469 00470 fd = open(binary_filename, O_RDONLY); 00471 if (fd < 0) { 00472 return; 00473 } 00474 filesize = lseek(fd, 0, SEEK_END); 00475 if (filesize < 0) { 00476 int e = errno; 00477 close(fd); 00478 kprintf("lseek: %s\n", strerror(e)); 00479 return; 00480 } 00481 #if SIZEOF_OFF_T > SIZEOF_SIZE_T 00482 if (filesize > (off_t)SIZE_MAX) { 00483 close(fd); 00484 kprintf("Too large file %s\n", binary_filename); 00485 return; 00486 } 00487 #endif 00488 lseek(fd, 0, SEEK_SET); 00489 /* async-signal unsafe */ 00490 file = (char *)mmap(NULL, (size_t)filesize, PROT_READ, MAP_SHARED, fd, 0); 00491 if (file == MAP_FAILED) { 00492 int e = errno; 00493 close(fd); 00494 kprintf("mmap: %s\n", strerror(e)); 00495 return; 00496 } 00497 00498 ehdr = (ElfW(Ehdr) *)file; 00499 if (memcmp(ehdr->e_ident, "\177ELF", 4) != 0) { 00500 /* 00501 * Huh? Maybe filename was overridden by setproctitle() and 00502 * it match non-elf file. 00503 */ 00504 close(fd); 00505 return; 00506 } 00507 00508 current_line->fd = fd; 00509 current_line->mapped = file; 00510 current_line->mapped_size = (size_t)filesize; 00511 00512 for (i = 0; i < num_traces; i++) { 00513 const char *path; 00514 size_t len; 00515 if (get_path_from_symbol(syms[i], &path, &len) && 00516 !strncmp(path, binary_filename, len)) { 00517 lines[i].line = -1; 00518 } 00519 } 00520 00521 shdr = (ElfW(Shdr) *)(file + ehdr->e_shoff); 00522 00523 shstr_shdr = shdr + ehdr->e_shstrndx; 00524 shstr = file + shstr_shdr->sh_offset; 00525 00526 for (i = 0; i < ehdr->e_shnum; i++) { 00527 section_name = shstr + shdr[i].sh_name; 00528 if (!strcmp(section_name, ".debug_line")) { 00529 debug_line_shdr = shdr + i; 00530 break; 00531 } else if (!strcmp(section_name, ".gnu_debuglink")) { 00532 gnu_debuglink_shdr = shdr + i; 00533 } 00534 } 00535 00536 if (!debug_line_shdr) { 00537 /* This file doesn't have .debug_line section, 00538 let's check .gnu_debuglink section instead. */ 00539 if (gnu_debuglink_shdr && check_debuglink) { 00540 follow_debuglink(file + gnu_debuglink_shdr->sh_offset, 00541 num_traces, traces, syms, 00542 current_line, lines); 00543 } 00544 return; 00545 } 00546 00547 parse_debug_line(num_traces, traces, 00548 file + debug_line_shdr->sh_offset, 00549 debug_line_shdr->sh_size, 00550 lines); 00551 } 00552 00553 #ifdef HAVE_DL_ITERATE_PHDR 00554 00555 typedef struct { 00556 int num_traces; 00557 char **syms; 00558 line_info_t *lines; 00559 } fill_base_addr_state_t; 00560 00561 static int 00562 fill_base_addr(struct dl_phdr_info *info, size_t size, void *data) 00563 { 00564 int i; 00565 fill_base_addr_state_t *st = (fill_base_addr_state_t *)data; 00566 for (i = 0; i < st->num_traces; i++) { 00567 const char *path; 00568 size_t len; 00569 size_t name_len = strlen(info->dlpi_name); 00570 00571 if (get_path_from_symbol(st->syms[i], &path, &len) && 00572 (len == name_len || (len > name_len && path[len-name_len-1] == '/')) && 00573 !strncmp(path+len-name_len, info->dlpi_name, name_len)) { 00574 st->lines[i].base_addr = info->dlpi_addr; 00575 } 00576 } 00577 return 0; 00578 } 00579 00580 #endif /* HAVE_DL_ITERATE_PHDR */ 00581 00582 void 00583 rb_dump_backtrace_with_lines(int num_traces, void **trace, char **syms) 00584 { 00585 int i; 00586 /* async-signal unsafe */ 00587 line_info_t *lines = (line_info_t *)calloc(num_traces, 00588 sizeof(line_info_t)); 00589 00590 /* Note that line info of shared objects might not be shown 00591 if we don't have dl_iterate_phdr */ 00592 #ifdef HAVE_DL_ITERATE_PHDR 00593 fill_base_addr_state_t fill_base_addr_state; 00594 00595 fill_base_addr_state.num_traces = num_traces; 00596 fill_base_addr_state.syms = syms; 00597 fill_base_addr_state.lines = lines; 00598 /* maybe async-signal unsafe */ 00599 dl_iterate_phdr(fill_base_addr, &fill_base_addr_state); 00600 #endif /* HAVE_DL_ITERATE_PHDR */ 00601 00602 for (i = 0; i < num_traces; i++) { 00603 const char *path; 00604 size_t len; 00605 if (lines[i].line) { 00606 continue; 00607 } 00608 00609 if (!get_path_from_symbol(syms[i], &path, &len)) { 00610 continue; 00611 } 00612 00613 strncpy(binary_filename, path, len); 00614 binary_filename[len] = '\0'; 00615 00616 fill_lines(num_traces, trace, syms, 1, &lines[i], lines); 00617 } 00618 00619 for (i = 0; i < num_traces; i++) { 00620 line_info_t *line = &lines[i]; 00621 00622 if (line->line > 0) { 00623 if (line->filename) { 00624 if (line->dirname && line->dirname[0]) { 00625 kprintf("%s %s/%s:%d\n", syms[i], line->dirname, line->filename, line->line); 00626 } 00627 else { 00628 kprintf("%s %s:%d\n", syms[i], line->filename, line->line); 00629 } 00630 } else { 00631 kprintf("%s ???:%d\n", syms[i], line->line); 00632 } 00633 } else { 00634 kprintf("%s\n", syms[i]); 00635 } 00636 } 00637 00638 for (i = 0; i < num_traces; i++) { 00639 line_info_t *line = &lines[i]; 00640 if (line->fd) { 00641 munmap(line->mapped, line->mapped_size); 00642 close(line->fd); 00643 } 00644 } 00645 free(lines); 00646 } 00647 00648 /* From FreeBSD's lib/libstand/printf.c */ 00649 /*- 00650 * Copyright (c) 1986, 1988, 1991, 1993 00651 * The Regents of the University of California. All rights reserved. 00652 * (c) UNIX System Laboratories, Inc. 00653 * All or some portions of this file are derived from material licensed 00654 * to the University of California by American Telephone and Telegraph 00655 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 00656 * the permission of UNIX System Laboratories, Inc. 00657 * 00658 * Redistribution and use in source and binary forms, with or without 00659 * modification, are permitted provided that the following conditions 00660 * are met: 00661 * 1. Redistributions of source code must retain the above copyright 00662 * notice, this list of conditions and the following disclaimer. 00663 * 2. Redistributions in binary form must reproduce the above copyright 00664 * notice, this list of conditions and the following disclaimer in the 00665 * documentation and/or other materials provided with the distribution. 00666 * 4. Neither the name of the University nor the names of its contributors 00667 * may be used to endorse or promote products derived from this software 00668 * without specific prior written permission. 00669 * 00670 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 00671 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00672 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00673 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 00674 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 00675 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 00676 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00677 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00678 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 00679 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 00680 * SUCH DAMAGE. 00681 * 00682 * @(#)subr_prf.c 8.3 (Berkeley) 1/21/94 00683 */ 00684 00685 #include <stdarg.h> 00686 #define MAXNBUF (sizeof(intmax_t) * CHAR_BIT + 1) 00687 extern int rb_toupper(int c); 00688 #define toupper(c) rb_toupper(c) 00689 #define hex2ascii(hex) (hex2ascii_data[hex]) 00690 char const hex2ascii_data[] = "0123456789abcdefghijklmnopqrstuvwxyz"; 00691 static inline int imax(int a, int b) { return (a > b ? a : b); } 00692 static int kvprintf(char const *fmt, void (*func)(int), void *arg, int radix, va_list ap); 00693 00694 static void putce(int c) 00695 { 00696 char s[1]; 00697 ssize_t ret; 00698 00699 s[0] = (char)c; 00700 ret = write(2, s, 1); 00701 (void)ret; 00702 } 00703 00704 int 00705 kprintf(const char *fmt, ...) 00706 { 00707 va_list ap; 00708 int retval; 00709 00710 va_start(ap, fmt); 00711 retval = kvprintf(fmt, putce, NULL, 10, ap); 00712 va_end(ap); 00713 return retval; 00714 } 00715 00716 /* 00717 * Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse 00718 * order; return an optional length and a pointer to the last character 00719 * written in the buffer (i.e., the first character of the string). 00720 * The buffer pointed to by `nbuf' must have length >= MAXNBUF. 00721 */ 00722 static char * 00723 ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper) 00724 { 00725 char *p, c; 00726 00727 p = nbuf; 00728 *p = '\0'; 00729 do { 00730 c = hex2ascii(num % base); 00731 *++p = upper ? toupper(c) : c; 00732 } while (num /= base); 00733 if (lenp) 00734 *lenp = p - nbuf; 00735 return (p); 00736 } 00737 00738 /* 00739 * Scaled down version of printf(3). 00740 * 00741 * Two additional formats: 00742 * 00743 * The format %b is supported to decode error registers. 00744 * Its usage is: 00745 * 00746 * printf("reg=%b\n", regval, "<base><arg>*"); 00747 * 00748 * where <base> is the output base expressed as a control character, e.g. 00749 * \10 gives octal; \20 gives hex. Each arg is a sequence of characters, 00750 * the first of which gives the bit number to be inspected (origin 1), and 00751 * the next characters (up to a control character, i.e. a character <= 32), 00752 * give the name of the register. Thus: 00753 * 00754 * kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n"); 00755 * 00756 * would produce output: 00757 * 00758 * reg=3<BITTWO,BITONE> 00759 * 00760 * XXX: %D -- Hexdump, takes pointer and separator string: 00761 * ("%6D", ptr, ":") -> XX:XX:XX:XX:XX:XX 00762 * ("%*D", len, ptr, " " -> XX XX XX XX ... 00763 */ 00764 static int 00765 kvprintf(char const *fmt, void (*func)(int), void *arg, int radix, va_list ap) 00766 { 00767 #define PCHAR(c) {int cc=(c); if (func) (*func)(cc); else *d++ = cc; retval++; } 00768 char nbuf[MAXNBUF]; 00769 char *d; 00770 const char *p, *percent, *q; 00771 unsigned char *up; 00772 int ch, n; 00773 uintmax_t num; 00774 int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot; 00775 int cflag, hflag, jflag, tflag, zflag; 00776 int dwidth, upper; 00777 char padc; 00778 int stop = 0, retval = 0; 00779 00780 num = 0; 00781 if (!func) 00782 d = (char *) arg; 00783 else 00784 d = NULL; 00785 00786 if (fmt == NULL) 00787 fmt = "(fmt null)\n"; 00788 00789 if (radix < 2 || radix > 36) 00790 radix = 10; 00791 00792 for (;;) { 00793 padc = ' '; 00794 width = 0; 00795 while ((ch = (unsigned char)*fmt++) != '%' || stop) { 00796 if (ch == '\0') 00797 return (retval); 00798 PCHAR(ch); 00799 } 00800 percent = fmt - 1; 00801 qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0; 00802 sign = 0; dot = 0; dwidth = 0; upper = 0; 00803 cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0; 00804 reswitch: switch (ch = (unsigned char)*fmt++) { 00805 case '.': 00806 dot = 1; 00807 goto reswitch; 00808 case '#': 00809 sharpflag = 1; 00810 goto reswitch; 00811 case '+': 00812 sign = 1; 00813 goto reswitch; 00814 case '-': 00815 ladjust = 1; 00816 goto reswitch; 00817 case '%': 00818 PCHAR(ch); 00819 break; 00820 case '*': 00821 if (!dot) { 00822 width = va_arg(ap, int); 00823 if (width < 0) { 00824 ladjust = !ladjust; 00825 width = -width; 00826 } 00827 } else { 00828 dwidth = va_arg(ap, int); 00829 } 00830 goto reswitch; 00831 case '0': 00832 if (!dot) { 00833 padc = '0'; 00834 goto reswitch; 00835 } 00836 case '1': case '2': case '3': case '4': 00837 case '5': case '6': case '7': case '8': case '9': 00838 for (n = 0;; ++fmt) { 00839 n = n * 10 + ch - '0'; 00840 ch = *fmt; 00841 if (ch < '0' || ch > '9') 00842 break; 00843 } 00844 if (dot) 00845 dwidth = n; 00846 else 00847 width = n; 00848 goto reswitch; 00849 case 'b': 00850 num = (unsigned int)va_arg(ap, int); 00851 p = va_arg(ap, char *); 00852 for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;) 00853 PCHAR(*q--); 00854 00855 if (num == 0) 00856 break; 00857 00858 for (tmp = 0; *p;) { 00859 n = *p++; 00860 if (num & (1 << (n - 1))) { 00861 PCHAR(tmp ? ',' : '<'); 00862 for (; (n = *p) > ' '; ++p) 00863 PCHAR(n); 00864 tmp = 1; 00865 } else 00866 for (; *p > ' '; ++p) 00867 continue; 00868 } 00869 if (tmp) 00870 PCHAR('>'); 00871 break; 00872 case 'c': 00873 PCHAR(va_arg(ap, int)); 00874 break; 00875 case 'D': 00876 up = va_arg(ap, unsigned char *); 00877 p = va_arg(ap, char *); 00878 if (!width) 00879 width = 16; 00880 while(width--) { 00881 PCHAR(hex2ascii(*up >> 4)); 00882 PCHAR(hex2ascii(*up & 0x0f)); 00883 up++; 00884 if (width) 00885 for (q=p;*q;q++) 00886 PCHAR(*q); 00887 } 00888 break; 00889 case 'd': 00890 case 'i': 00891 base = 10; 00892 sign = 1; 00893 goto handle_sign; 00894 case 'h': 00895 if (hflag) { 00896 hflag = 0; 00897 cflag = 1; 00898 } else 00899 hflag = 1; 00900 goto reswitch; 00901 case 'j': 00902 jflag = 1; 00903 goto reswitch; 00904 case 'l': 00905 if (lflag) { 00906 lflag = 0; 00907 qflag = 1; 00908 } else 00909 lflag = 1; 00910 goto reswitch; 00911 case 'n': 00912 if (jflag) 00913 *(va_arg(ap, intmax_t *)) = retval; 00914 else if (qflag) 00915 *(va_arg(ap, int64_t *)) = retval; 00916 else if (lflag) 00917 *(va_arg(ap, long *)) = retval; 00918 else if (zflag) 00919 *(va_arg(ap, size_t *)) = retval; 00920 else if (hflag) 00921 *(va_arg(ap, short *)) = retval; 00922 else if (cflag) 00923 *(va_arg(ap, char *)) = retval; 00924 else 00925 *(va_arg(ap, int *)) = retval; 00926 break; 00927 case 'o': 00928 base = 8; 00929 goto handle_nosign; 00930 case 'p': 00931 base = 16; 00932 sharpflag = (width == 0); 00933 sign = 0; 00934 num = (uintptr_t)va_arg(ap, void *); 00935 goto number; 00936 case 'q': 00937 qflag = 1; 00938 goto reswitch; 00939 case 'r': 00940 base = radix; 00941 if (sign) 00942 goto handle_sign; 00943 goto handle_nosign; 00944 case 's': 00945 p = va_arg(ap, char *); 00946 if (p == NULL) 00947 p = "(null)"; 00948 if (!dot) 00949 n = strlen (p); 00950 else 00951 for (n = 0; n < dwidth && p[n]; n++) 00952 continue; 00953 00954 width -= n; 00955 00956 if (!ladjust && width > 0) 00957 while (width--) 00958 PCHAR(padc); 00959 while (n--) 00960 PCHAR(*p++); 00961 if (ladjust && width > 0) 00962 while (width--) 00963 PCHAR(padc); 00964 break; 00965 case 't': 00966 tflag = 1; 00967 goto reswitch; 00968 case 'u': 00969 base = 10; 00970 goto handle_nosign; 00971 case 'X': 00972 upper = 1; 00973 case 'x': 00974 base = 16; 00975 goto handle_nosign; 00976 case 'y': 00977 base = 16; 00978 sign = 1; 00979 goto handle_sign; 00980 case 'z': 00981 zflag = 1; 00982 goto reswitch; 00983 handle_nosign: 00984 sign = 0; 00985 if (jflag) 00986 num = va_arg(ap, uintmax_t); 00987 else if (qflag) 00988 num = va_arg(ap, uint64_t); 00989 else if (tflag) 00990 num = va_arg(ap, ptrdiff_t); 00991 else if (lflag) 00992 num = va_arg(ap, unsigned long); 00993 else if (zflag) 00994 num = va_arg(ap, size_t); 00995 else if (hflag) 00996 num = (unsigned short)va_arg(ap, int); 00997 else if (cflag) 00998 num = (unsigned char)va_arg(ap, int); 00999 else 01000 num = va_arg(ap, unsigned int); 01001 goto number; 01002 handle_sign: 01003 if (jflag) 01004 num = va_arg(ap, intmax_t); 01005 else if (qflag) 01006 num = va_arg(ap, int64_t); 01007 else if (tflag) 01008 num = va_arg(ap, ptrdiff_t); 01009 else if (lflag) 01010 num = va_arg(ap, long); 01011 else if (zflag) 01012 num = va_arg(ap, ssize_t); 01013 else if (hflag) 01014 num = (short)va_arg(ap, int); 01015 else if (cflag) 01016 num = (char)va_arg(ap, int); 01017 else 01018 num = va_arg(ap, int); 01019 number: 01020 if (sign && (intmax_t)num < 0) { 01021 neg = 1; 01022 num = -(intmax_t)num; 01023 } 01024 p = ksprintn(nbuf, num, base, &n, upper); 01025 tmp = 0; 01026 if (sharpflag && num != 0) { 01027 if (base == 8) 01028 tmp++; 01029 else if (base == 16) 01030 tmp += 2; 01031 } 01032 if (neg) 01033 tmp++; 01034 01035 if (!ladjust && padc == '0') 01036 dwidth = width - tmp; 01037 width -= tmp + imax(dwidth, n); 01038 dwidth -= n; 01039 if (!ladjust) 01040 while (width-- > 0) 01041 PCHAR(' '); 01042 if (neg) 01043 PCHAR('-'); 01044 if (sharpflag && num != 0) { 01045 if (base == 8) { 01046 PCHAR('0'); 01047 } else if (base == 16) { 01048 PCHAR('0'); 01049 PCHAR('x'); 01050 } 01051 } 01052 while (dwidth-- > 0) 01053 PCHAR('0'); 01054 01055 while (*p) 01056 PCHAR(*p--); 01057 01058 if (ladjust) 01059 while (width-- > 0) 01060 PCHAR(' '); 01061 01062 break; 01063 default: 01064 while (percent < fmt) 01065 PCHAR(*percent++); 01066 /* 01067 * Since we ignore an formatting argument it is no 01068 * longer safe to obey the remaining formatting 01069 * arguments as the arguments will no longer match 01070 * the format specs. 01071 */ 01072 stop = 1; 01073 break; 01074 } 01075 } 01076 #undef PCHAR 01077 } 01078 #else /* defined(USE_ELF) */ 01079 #error not supported 01080 #endif 01081