kit

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

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 }