nm.c (11032B)
1 #include <kit/archive.h> 2 #include <kit/core.h> 3 #include <kit/object.h> 4 #include <stdint.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 9 #include "driver.h" 10 11 #define NM_TOOL "nm" 12 13 typedef struct NmSym { 14 KitSlice name; 15 uint64_t value; 16 KitSymBind bind; 17 KitSymKind kind; 18 int defined; /* 1 = has a section, 0 = undef/abs/common */ 19 const char* file_prefix; 20 } NmSym; 21 22 typedef struct NmOpts { 23 int debug_syms; /* -a */ 24 int extern_only; /* -g */ 25 int undef_only; /* -u */ 26 int defined_only; /* --defined-only */ 27 int numeric_sort; /* -n */ 28 int reverse_sort; /* -r */ 29 int no_sort; /* -p / --no-sort */ 30 int print_file; /* -A */ 31 int dynamic; /* -D / --dynamic: list dynamic (.dynsym) symbols */ 32 } NmOpts; 33 34 static int nm_symbol_visible(const KitObjSymInfo* si, const NmOpts* opts) { 35 if (!si->name.len) return 0; 36 if (opts->undef_only && si->section != KIT_SECTION_NONE && 37 si->kind != KIT_SK_ABS) 38 return 0; 39 if (opts->defined_only && si->section == KIT_SECTION_NONE && 40 si->kind != KIT_SK_ABS) 41 return 0; 42 if (opts->extern_only && si->bind != KIT_SB_GLOBAL && si->bind != KIT_SB_WEAK) 43 return 0; 44 if (!opts->debug_syms) { 45 if (si->kind == KIT_SK_FILE) return 0; 46 if (si->kind == KIT_SK_SECTION && si->bind == KIT_SB_LOCAL) return 0; 47 } 48 return 1; 49 } 50 51 static char nm_type_char(const NmSym* s) { 52 if (s->kind == KIT_SK_ABS) return (s->bind == KIT_SB_LOCAL) ? 'a' : 'A'; 53 if (!s->defined) { 54 if (s->kind == KIT_SK_COMMON) { 55 if (s->bind == KIT_SB_WEAK) return 'v'; 56 return (s->bind == KIT_SB_LOCAL) ? 'c' : 'C'; 57 } 58 if (s->bind == KIT_SB_WEAK) return 'w'; 59 return (s->bind == KIT_SB_LOCAL) ? 'u' : 'U'; 60 } 61 switch (s->kind) { 62 case KIT_SK_FUNC: 63 if (s->bind == KIT_SB_WEAK) return 'W'; 64 return (s->bind == KIT_SB_LOCAL) ? 't' : 'T'; 65 case KIT_SK_IFUNC: 66 return (s->bind == KIT_SB_LOCAL) ? 'i' : 'I'; 67 case KIT_SK_OBJ: 68 case KIT_SK_NOTYPE: 69 if (s->bind == KIT_SB_WEAK) return 'V'; 70 return (s->bind == KIT_SB_LOCAL) ? 'd' : 'D'; 71 case KIT_SK_TLS: 72 if (s->bind == KIT_SB_WEAK) return 'W'; 73 return (s->bind == KIT_SB_LOCAL) ? 'r' : 'R'; 74 default: 75 return (s->bind == KIT_SB_LOCAL) ? 'n' : 'N'; 76 } 77 } 78 79 static int nm_name_cmp(const void* a, const void* b) { 80 const NmSym* sa = (const NmSym*)a; 81 const NmSym* sb = (const NmSym*)b; 82 size_t la = sa->name.len, lb = sb->name.len; 83 size_t n = la < lb ? la : lb; 84 int c = memcmp(sa->name.s, sb->name.s, n); 85 if (c) return c; 86 return la < lb ? -1 : (la > lb ? 1 : 0); 87 } 88 89 static int nm_value_cmp(const void* a, const void* b) { 90 const NmSym* sa = (const NmSym*)a; 91 const NmSym* sb = (const NmSym*)b; 92 if (sa->value < sb->value) return -1; 93 if (sa->value > sb->value) return 1; 94 return nm_name_cmp(a, b); 95 } 96 97 static int nm_append_sym(DriverEnv* env, NmSym** syms, uint32_t* n, 98 uint32_t* cap, const KitObjSymInfo* si, 99 const char* file_prefix, int* ptr_digits, 100 KitTargetSpec t) { 101 int d = (t.ptr_size == 4) ? 8 : 16; 102 char* name; 103 if (d > *ptr_digits) *ptr_digits = d; 104 if (*n >= *cap) { 105 uint32_t nc = *cap ? *cap * 2u : 64u; 106 NmSym* ns = (NmSym*)driver_alloc_zeroed(env, (size_t)nc * sizeof(NmSym)); 107 if (!ns) return 1; 108 if (*syms) { 109 memcpy(ns, *syms, (size_t)(*n) * sizeof(NmSym)); 110 driver_free(env, *syms, (size_t)(*cap) * sizeof(NmSym)); 111 } 112 *syms = ns; 113 *cap = nc; 114 } 115 name = (char*)driver_alloc(env, si->name.len); 116 if (!name) return 1; 117 driver_memcpy(name, si->name.s, si->name.len); 118 NmSym* ds = &(*syms)[*n]; 119 ds->name.s = name; 120 ds->name.len = si->name.len; 121 ds->value = si->value; 122 ds->bind = si->bind; 123 ds->kind = si->kind; 124 ds->defined = (si->section != KIT_SECTION_NONE); 125 ds->file_prefix = file_prefix; 126 (*n)++; 127 return 0; 128 } 129 130 static int nm_collect_obj(KitObjFile* of, const NmOpts* opts, 131 const char* file_prefix, NmSym** syms, uint32_t* n, 132 uint32_t* cap, int* ptr_digits, DriverEnv* env) { 133 KitTargetSpec t = kit_obj_target(of); 134 KitObjSymIter* it = NULL; 135 int rc = 0; 136 KitStatus st = opts->dynamic ? kit_obj_dynsymiter_new(of, &it) 137 : kit_obj_symiter_new(of, &it); 138 if (st != KIT_OK) return 1; 139 for (;;) { 140 KitObjSymInfo si; 141 if (kit_obj_symiter_next(it, &si) != KIT_ITER_ITEM) break; 142 if (!nm_symbol_visible(&si, opts)) continue; 143 if (nm_append_sym(env, syms, n, cap, &si, file_prefix, ptr_digits, t) != 144 0) { 145 rc = 1; 146 break; 147 } 148 } 149 kit_obj_symiter_free(it); 150 return rc; 151 } 152 153 static int nm_process_file(const KitContext* ctx, const KitSlice* input, 154 const char* path, const NmOpts* opts, NmSym** syms, 155 uint32_t* n, uint32_t* cap, int* ptr_digits, 156 DriverEnv* env) { 157 KitBinFmt fmt = kit_detect_fmt(input->data, input->len); 158 if (fmt == KIT_BIN_AR) { 159 KitArIter* it = NULL; 160 if (kit_ar_iter_new(ctx, input, &it) != KIT_OK) { 161 driver_errf(NM_TOOL, "%s: not a recognized archive", path); 162 return 1; 163 } 164 for (;;) { 165 KitArMember m; 166 if (kit_ar_iter_next(it, &m) != KIT_ITER_ITEM) break; 167 KitObjFile* of = NULL; 168 KitSlice mb; 169 mb.data = m.data; 170 mb.len = m.size; 171 if (kit_obj_open(ctx, m.name, &mb, &of) == KIT_OK) { 172 int collect_rc = 173 nm_collect_obj(of, opts, NULL, syms, n, cap, ptr_digits, env); 174 kit_obj_free(of); 175 if (collect_rc != 0) { 176 kit_ar_iter_free(it); 177 driver_errf(NM_TOOL, "%s: out of memory while reading symbols", path); 178 return 1; 179 } 180 } 181 } 182 kit_ar_iter_free(it); 183 } else { 184 KitObjFile* of = NULL; 185 if (kit_obj_open(ctx, kit_slice_cstr(path), input, &of) != KIT_OK && 186 kit_obj_open(ctx, KIT_SLICE_NULL, input, &of) != KIT_OK) { 187 driver_errf(NM_TOOL, "%s: not a recognized object file", path); 188 return 1; 189 } 190 if (nm_collect_obj(of, opts, NULL, syms, n, cap, ptr_digits, env) != 0) { 191 kit_obj_free(of); 192 driver_errf(NM_TOOL, "%s: out of memory while reading symbols", path); 193 return 1; 194 } 195 kit_obj_free(of); 196 } 197 return 0; 198 } 199 200 static void nm_free_syms(DriverEnv* env, NmSym* syms, uint32_t nsyms, 201 uint32_t cap) { 202 uint32_t i; 203 if (!syms) return; 204 for (i = 0; i < nsyms; ++i) { 205 if (syms[i].name.s) 206 driver_free(env, (void*)syms[i].name.s, syms[i].name.len); 207 } 208 driver_free(env, syms, (size_t)cap * sizeof(NmSym)); 209 } 210 211 static void nm_sort_syms(NmSym* syms, uint32_t nsyms, const NmOpts* opts) { 212 uint32_t i, j; 213 if (opts->no_sort) return; 214 qsort(syms, (size_t)nsyms, sizeof(NmSym), 215 opts->numeric_sort ? nm_value_cmp : nm_name_cmp); 216 if (!opts->reverse_sort) return; 217 for (i = 0, j = nsyms - 1; i < j; ++i, --j) { 218 NmSym t = syms[i]; 219 syms[i] = syms[j]; 220 syms[j] = t; 221 } 222 } 223 224 static void nm_print_syms(const NmSym* syms, uint32_t nsyms, const NmOpts* opts, 225 int ptr_digits) { 226 uint32_t i; 227 for (i = 0; i < nsyms; ++i) { 228 const NmSym* s = &syms[i]; 229 char tc = nm_type_char(s); 230 const char* pfx = ""; 231 char pbuf[256]; 232 if (opts->print_file && s->file_prefix) { 233 snprintf(pbuf, sizeof pbuf, "%s:", s->file_prefix); 234 pfx = pbuf; 235 } 236 if (ptr_digits == 8) { 237 driver_printf("%s%08llx %c %.*s\n", pfx, (unsigned long long)s->value, tc, 238 (int)s->name.len, s->name.s); 239 } else { 240 driver_printf("%s%016llx %c %.*s\n", pfx, (unsigned long long)s->value, 241 tc, (int)s->name.len, s->name.s); 242 } 243 } 244 } 245 246 void driver_help_nm(void) { 247 driver_printf( 248 "%.*s", 249 KIT_SLICE_ARG(KIT_SLICE_LIT( 250 "kit nm — list symbols from object files\n" 251 "\n" 252 "USAGE\n" 253 " kit nm [OPTIONS] FILE...\n" 254 "\n" 255 "OPTIONS\n" 256 " -a, --debug-syms include debug symbols\n" 257 " -g, --extern-only show only external (global) symbols\n" 258 " -u, --undefined-only show only undefined symbols\n" 259 " --defined-only show only defined symbols\n" 260 " -D, --dynamic list dynamic symbols (.dynsym), not " 261 ".symtab\n" 262 " -n, --numeric-sort sort by address\n" 263 " -r, --reverse-sort reverse the sort order\n" 264 " --no-sort, -p do not sort; print in file order\n" 265 " -A, --print-file-name prefix each line with the input file name\n" 266 " -h, --help show this help\n"))); 267 } 268 269 int driver_nm(int argc, char** argv) { 270 DriverEnv env; 271 KitContext ctx; 272 NmOpts opts; 273 NmSym* syms = NULL; 274 uint32_t nsyms = 0, cap = 0; 275 int ptr_digits = 8; 276 int i, rc = 1, any_input = 0; 277 278 if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) { 279 driver_help_nm(); 280 return 0; 281 } 282 283 memset(&opts, 0, sizeof opts); 284 driver_env_init(&env); 285 ctx = driver_env_to_context(&env); 286 287 for (i = 1; i < argc; ++i) { 288 const char* a = argv[i]; 289 if (driver_streq(a, "-a") || driver_streq(a, "--debug-syms")) { 290 opts.debug_syms = 1; 291 continue; 292 } 293 if (driver_streq(a, "-g") || driver_streq(a, "--extern-only")) { 294 opts.extern_only = 1; 295 continue; 296 } 297 if (driver_streq(a, "-u") || driver_streq(a, "--undefined-only")) { 298 opts.undef_only = 1; 299 continue; 300 } 301 if (driver_streq(a, "--defined-only")) { 302 opts.defined_only = 1; 303 continue; 304 } 305 if (driver_streq(a, "-n") || driver_streq(a, "--numeric-sort")) { 306 opts.numeric_sort = 1; 307 continue; 308 } 309 if (driver_streq(a, "-r") || driver_streq(a, "--reverse-sort")) { 310 opts.reverse_sort = 1; 311 continue; 312 } 313 if (driver_streq(a, "--no-sort") || driver_streq(a, "-p")) { 314 opts.no_sort = 1; 315 continue; 316 } 317 if (driver_streq(a, "-A") || driver_streq(a, "--print-file-name")) { 318 opts.print_file = 1; 319 continue; 320 } 321 if (driver_streq(a, "-D") || driver_streq(a, "--dynamic")) { 322 opts.dynamic = 1; 323 continue; 324 } 325 if (a[0] == '-') { 326 driver_errf(NM_TOOL, "unknown option: %s", a); 327 rc = 2; 328 goto done; 329 } 330 { 331 const char* path = a; 332 DriverLoad ld = {0}; 333 KitSlice input; 334 if (driver_load_bytes(&env.file_io, NM_TOOL, path, &ld, &input) != 0) { 335 rc = 1; 336 goto done; 337 } 338 if (nm_process_file(&ctx, &input, path, &opts, &syms, &nsyms, &cap, 339 &ptr_digits, &env) != 0) { 340 driver_release_bytes(&env.file_io, &ld); 341 rc = 1; 342 goto done; 343 } 344 driver_release_bytes(&env.file_io, &ld); 345 any_input = 1; 346 } 347 } 348 349 if (!any_input) { 350 driver_errf(NM_TOOL, "no input files"); 351 rc = 2; 352 goto done; 353 } 354 355 nm_sort_syms(syms, nsyms, &opts); 356 nm_print_syms(syms, nsyms, &opts, ptr_digits); 357 rc = 0; 358 359 done: 360 nm_free_syms(&env, syms, nsyms, cap); 361 driver_env_fini(&env); 362 return rc; 363 }