Ruby  2.0.0p247(2013-06-27revision41674)
addr2line.c
Go to the documentation of this file.
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