kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

addr2line.c (6366B)


      1 #include <kit/core.h>
      2 #include <kit/dwarf.h>
      3 #include <kit/object.h>
      4 #include <stdint.h>
      5 #include <stdlib.h>
      6 #include <string.h>
      7 
      8 #include "driver.h"
      9 #include "dwarfsym.h"
     10 
     11 #define A2L_TOOL "addr2line"
     12 
     13 typedef struct A2lOpts {
     14   const char* exe_path;
     15   int functions; /* -f */
     16   int pretty;    /* -p */
     17   int basenames; /* --basenames */
     18   int show_addr; /* -a */
     19 } A2lOpts;
     20 
     21 static void a2l_strip_basename(const char** path_ptr) {
     22   const char* s = *path_ptr;
     23   const char* slash = NULL;
     24   const char* p;
     25   if (!s) return;
     26   for (p = s; *p; ++p)
     27     if (*p == '/') slash = p;
     28   if (slash) *path_ptr = slash + 1;
     29 }
     30 
     31 static void a2l_translate(DriverDwarfSym* sym, uint64_t addr,
     32                           const A2lOpts* opts) {
     33   KitDwarfResolve loc;
     34 
     35   /* Share the DWARF open + func/line queries with `kit symbolize`; the two
     36    * tools differ only in how they format the result. */
     37   kit_dwarf_resolve(sym->dwarf, addr, opts->functions, &loc);
     38 
     39   if (opts->show_addr) driver_printf("0x%llx", (unsigned long long)addr);
     40 
     41   if (opts->pretty) {
     42     if (opts->show_addr) driver_printf(": ");
     43     if (loc.have_func)
     44       driver_printf("%.*s at ", (int)loc.func.len, loc.func.s);
     45     else if (opts->functions)
     46       driver_printf("?? at ");
     47     if (loc.have_line) {
     48       const char* f = loc.file.s;
     49       if (opts->basenames) a2l_strip_basename(&f);
     50       driver_printf("%s:%u", f, loc.line);
     51       if (loc.col) driver_printf(":%u", loc.col);
     52     } else {
     53       driver_printf("??:0");
     54     }
     55     driver_printf("\n");
     56     return;
     57   }
     58 
     59   /* default or -f, -a modes */
     60   if (opts->show_addr) driver_printf(": ");
     61 
     62   if (opts->functions) {
     63     if (loc.have_func)
     64       driver_printf("%.*s\n", (int)loc.func.len, loc.func.s);
     65     else
     66       driver_printf("??\n");
     67   }
     68 
     69   if (loc.have_line) {
     70     const char* f = loc.file.s;
     71     if (opts->basenames) a2l_strip_basename(&f);
     72     driver_printf("%s:%u", f, loc.line);
     73     if (loc.col) driver_printf(":%u", loc.col);
     74   } else {
     75     driver_printf("??:0");
     76   }
     77   driver_printf("\n");
     78 }
     79 
     80 void driver_help_addr2line(void) {
     81   driver_printf(
     82       "%.*s",
     83       KIT_SLICE_ARG(KIT_SLICE_LIT(
     84           "kit addr2line — translate addresses to file:line using debug "
     85           "info\n"
     86           "\n"
     87           "USAGE\n"
     88           "  kit addr2line [OPTIONS] -e FILE [ADDR...]\n"
     89           "\n"
     90           "DESCRIPTION\n"
     91           "  ADDR is hexadecimal, with or without a 0x prefix, and is a\n"
     92           "  link-time address in FILE. With no ADDR operands, read one hex\n"
     93           "  address token per line from stdin. The normal result is\n"
     94           "  FILE:LINE:COLUMN (plus the function with -f); an unresolved\n"
     95           "  address prints `??:0` and, with -f, function `??`.\n"
     96           "\n"
     97           "OPTIONS\n"
     98           "  -e FILE              object file with debug info (required)\n"
     99           "  -a, --addresses      print the address before each line\n"
    100           "  -f, --functions      print function names\n"
    101           "  -p, --pretty-print   compact single-line output\n"
    102           "  --basenames          strip directory from file paths\n"
    103           "  -h, --help           show this help\n"
    104           "\n"
    105           "EXAMPLES\n"
    106           "  SDK=$(kit cc -print-sysroot)\n"
    107           "  printf 'int main(void) { return 0; }\\n' > debug.c\n"
    108           "  kit cc -isysroot \"$SDK\" "
    109           "-g -no-pie debug.c -o debug-app\n"
    110           "  kit nm -n debug-app\n"
    111           "  ADDR=$(kit nm -n debug-app | awk '$3 == \"main\" || "
    112           "$3 == \"_main\" { print $1; exit }')\n"
    113           "  kit addr2line -a -f -p -e debug-app \"0x$ADDR\"\n"
    114           "  printf '%s\\n' \"$ADDR\" | kit addr2line -f -e debug-app\n"
    115           "\n"
    116           "EXIT CODES\n"
    117           "  0   success           1   debug/object or I/O error\n"
    118           "  2   bad usage\n")));
    119 }
    120 
    121 int driver_addr2line(int argc, char** argv) {
    122   DriverEnv env;
    123   A2lOpts opts;
    124   DriverDwarfSym sym;
    125   int i, rc = 1, opened = 0, options = 1;
    126   int stdin_addr_count = 0;
    127 
    128   if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) {
    129     driver_help_addr2line();
    130     return 0;
    131   }
    132 
    133   memset(&opts, 0, sizeof opts);
    134   driver_env_init(&env);
    135 
    136   for (i = 1; i < argc; ++i) {
    137     const char* a = argv[i];
    138     if (options && driver_streq(a, "--")) {
    139       options = 0;
    140       continue;
    141     }
    142     if (!options) continue;
    143     if (driver_streq(a, "-e")) {
    144       if (i + 1 >= argc) {
    145         driver_errf(A2L_TOOL, "-e requires a path");
    146         rc = 2;
    147         goto done;
    148       }
    149       opts.exe_path = argv[++i];
    150       continue;
    151     }
    152     if (driver_streq(a, "-a") || driver_streq(a, "--addresses")) {
    153       opts.show_addr = 1;
    154       continue;
    155     }
    156     if (driver_streq(a, "-f") || driver_streq(a, "--functions")) {
    157       opts.functions = 1;
    158       continue;
    159     }
    160     if (driver_streq(a, "-p") || driver_streq(a, "--pretty-print")) {
    161       opts.pretty = 1;
    162       continue;
    163     }
    164     if (driver_streq(a, "--basenames")) {
    165       opts.basenames = 1;
    166       continue;
    167     }
    168     if (a[0] == '-' && a[1] != '\0') {
    169       driver_errf(A2L_TOOL, "unknown option: %s", a);
    170       rc = 2;
    171       goto done;
    172     }
    173   }
    174 
    175   if (!opts.exe_path) {
    176     driver_errf(A2L_TOOL, "no object file specified (-e FILE)");
    177     rc = 2;
    178     goto done;
    179   }
    180 
    181   /* open() memsets `sym` before any fallible step, so once we are past this
    182    * point driver_dwarfsym_close is always safe — even on a partial failure. */
    183   opened = 1;
    184   if (driver_dwarfsym_open(&sym, &env, A2L_TOOL, opts.exe_path) != 0) {
    185     rc = 1;
    186     goto done;
    187   }
    188 
    189   /* scan for positional addresses */
    190   options = 1;
    191   for (i = 1; i < argc; ++i) {
    192     const char* a = argv[i];
    193     if (options && driver_streq(a, "--")) {
    194       options = 0;
    195       continue;
    196     }
    197     if (options && a[0] == '-' && a[1] != '\0') {
    198       if (driver_streq(a, "-e")) {
    199         ++i;
    200         continue;
    201       }
    202       continue;
    203     }
    204     {
    205       uint64_t addr = (uint64_t)strtoull(a, NULL, 16);
    206       a2l_translate(&sym, addr, &opts);
    207       stdin_addr_count++;
    208     }
    209   }
    210 
    211   if (stdin_addr_count == 0) {
    212     char line[256];
    213     for (;;) {
    214       int n = driver_read_line(line, sizeof line);
    215       if (n <= 0) break;
    216       {
    217         uint64_t addr = (uint64_t)strtoull(line, NULL, 16);
    218         a2l_translate(&sym, addr, &opts);
    219       }
    220     }
    221   }
    222 
    223   rc = 0;
    224 
    225 done:
    226   if (opened) driver_dwarfsym_close(&sym);
    227   driver_env_fini(&env);
    228   return rc;
    229 }