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 }