addr2line.c (5198B)
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 DriverSymLoc 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 driver_dwarfsym_lookup(sym, 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 "OPTIONS\n" 91 " -e FILE object file with debug info (required)\n" 92 " -a, --addresses print the address before each line\n" 93 " -f, --functions print function names\n" 94 " -p, --pretty-print compact single-line output\n" 95 " --basenames strip directory from file paths\n" 96 " -h, --help show this help\n" 97 "\n" 98 "If no addresses are given on the command line, addresses are read\n" 99 "from standard input, one per line (hex).\n"))); 100 } 101 102 int driver_addr2line(int argc, char** argv) { 103 DriverEnv env; 104 A2lOpts opts; 105 DriverDwarfSym sym; 106 int i, rc = 1, opened = 0; 107 int stdin_addr_count = 0; 108 109 if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) { 110 driver_help_addr2line(); 111 return 0; 112 } 113 114 memset(&opts, 0, sizeof opts); 115 driver_env_init(&env); 116 117 for (i = 1; i < argc; ++i) { 118 const char* a = argv[i]; 119 if (driver_streq(a, "-e")) { 120 if (i + 1 >= argc) { 121 driver_errf(A2L_TOOL, "-e requires a path"); 122 rc = 2; 123 goto done; 124 } 125 opts.exe_path = argv[++i]; 126 continue; 127 } 128 if (driver_streq(a, "-a") || driver_streq(a, "--addresses")) { 129 opts.show_addr = 1; 130 continue; 131 } 132 if (driver_streq(a, "-f") || driver_streq(a, "--functions")) { 133 opts.functions = 1; 134 continue; 135 } 136 if (driver_streq(a, "-p") || driver_streq(a, "--pretty-print")) { 137 opts.pretty = 1; 138 continue; 139 } 140 if (driver_streq(a, "--basenames")) { 141 opts.basenames = 1; 142 continue; 143 } 144 if (a[0] == '-' && a[1] != '\0') { 145 driver_errf(A2L_TOOL, "unknown option: %s", a); 146 rc = 2; 147 goto done; 148 } 149 } 150 151 if (!opts.exe_path) { 152 driver_errf(A2L_TOOL, "no object file specified (-e FILE)"); 153 rc = 2; 154 goto done; 155 } 156 157 /* open() memsets `sym` before any fallible step, so once we are past this 158 * point driver_dwarfsym_close is always safe — even on a partial failure. */ 159 opened = 1; 160 if (driver_dwarfsym_open(&sym, &env, A2L_TOOL, opts.exe_path) != 0) { 161 rc = 1; 162 goto done; 163 } 164 165 /* scan for positional addresses */ 166 for (i = 1; i < argc; ++i) { 167 const char* a = argv[i]; 168 if (a[0] == '-' && a[1] != '\0') { 169 if (driver_streq(a, "-e")) { 170 ++i; 171 continue; 172 } 173 continue; 174 } 175 { 176 uint64_t addr = (uint64_t)strtoull(a, NULL, 16); 177 a2l_translate(&sym, addr, &opts); 178 stdin_addr_count++; 179 } 180 } 181 182 if (stdin_addr_count == 0) { 183 char line[256]; 184 for (;;) { 185 int n = driver_read_line(line, sizeof line); 186 if (n <= 0) break; 187 { 188 uint64_t addr = (uint64_t)strtoull(line, NULL, 16); 189 a2l_translate(&sym, addr, &opts); 190 } 191 } 192 } 193 194 rc = 0; 195 196 done: 197 if (opened) driver_dwarfsym_close(&sym); 198 driver_env_fini(&env); 199 return rc; 200 }