Příkazy nm poskytují informace o symbolech používaných v objektovém souboru nebo spustitelném souboru.
Výchozí informace, které příkaz ‚nm‘ poskytuje, je:
- Virtuální adresa symbolu
- Znak, který znázorňuje typ symbolu. Pokud je znak napsán malým písmenem, pak je symbol místní, ale pokud je znak velkým, pak je symbol externí
- Název symbolu
Znaky, které identifikují typ symbolu, popisují:
- A : Globální absolutní symbol.
- a : Místní absolutní symbol.
- B :Globální symbol bss.
- b :Místní symbol bss.
- D :Globální datový symbol.
- d :Místní datový symbol.
- f :Symbol názvu zdrojového souboru.
- L :globální symbol místního vlákna (TLS).
- l :Statický místní symbol vlákna (TLS).
- T :Symbol globálního textu.
- t :Symbol místního textu.
- U :Nedefinovaný symbol.
Upozorňujeme, že tento seznam není vyčerpávající, ale obsahuje některé důležité typy symbolů. Úplné informace naleznete v manuálové stránce tohoto nástroje.
Výchozí způsob použití nástroje „nm“ je:
$ nm <object file or executable name>
pokud není zadán název spustitelného souboru, pak nm převezme název „a.out“.
Se základní myšlenkou o této utilitě se lze ptát, proč by tyto informace byly vyžadovány?
Předpokládejme, že máte spustitelný soubor, který se skládá z mnoha různých objektových souborů. Nyní předpokládejme, že při kompilaci kódu linker zobrazí chybu o nevyřešeném symbolu „temp“. Nyní se stane noční můrou najít, kde je v kódu symbol ‚temp‘, pokud je kód příliš velký a obsahuje mnoho záhlaví. Zde tento nástroj přichází na pomoc. S některými dalšími možnostmi poskytuje tento nástroj také soubor, ve kterém se symbol nachází.
Od této chvíle máme základní představu o nástroji nm. Pojďme pochopit použití tohoto nástroje pomocí několika praktických příkazů.
1. Zobrazit soubory objektů, které odkazují na symbol
Následující příkaz zobrazí všechny soubory objektů, které odkazují na symbol ‚func‘ v mém aktuálním adresáři
$ nm -A ./*.o | grep func ./hello2.o:0000000000000000 T func_1 ./hello3.o:0000000000000000 T func_2 ./hello4.o:0000000000000000 T func_3 ./main.o: U func ./reloc.o: U func ./reloc.o:0000000000000000 T func1 ./test1.o:0000000000000000 T func ./test.o: U func
Všimněte si, že parametr -A se používá k zobrazení názvu souboru spolu s dalšími informacemi. Vidíme tedy, že ve výstupu dostáváme všechny soubory objektů, kde se používá symbol ‚func‘. To by mohlo být extrémně užitečné v případech, kdy chceme vědět, jak které soubory objektů používají konkrétní symbol.
2. Zobrazit všechny nedefinované symboly ve spustitelném souboru
Následující příkaz vypíše všechny nedefinované symboly ve spustitelném souboru ‚1‘
$ nm -u 1 w _Jv_RegisterClasses w __gmon_start__ U __libc_start_main@@GLIBC_2.2.5 U free@@GLIBC_2.2.5 U malloc@@GLIBC_2.2.5 U printf@@GLIBC_2.2.5
Všimněte si, že příznak „-u“ se v tomto případě používá pro výpis pouze nedefinovaných symbolů. To by mohlo být mimořádně užitečné v případech, kdy se může chtít dozvědět o nedefinovaných symbolech používaných v kódu, které by mohly být buď skutečně nevyřešené, nebo by mohly být vyřešeny za běhu prostřednictvím sdílených knihoven.
Pokud jde o související téma, měli byste také pochopit, jak funguje proces propojení GCC.
3. Zobrazit všechny symboly ve spustitelném souboru
Následující příkaz uvádí všechny symboly ve spustitelném souboru ‚namepid‘, ale v seřazeném pořadí podle jejich adres
$ nm -n namepid w _Jv_RegisterClasses w __gmon_start__ U __libc_start_main@@GLIBC_2.2.5 U exit@@GLIBC_2.2.5 U fclose@@GLIBC_2.2.5 U fgets@@GLIBC_2.2.5 U fopen@@GLIBC_2.2.5 U fork@@GLIBC_2.2.5 U memset@@GLIBC_2.2.5 U printf@@GLIBC_2.2.5 U puts@@GLIBC_2.2.5 U signal@@GLIBC_2.2.5 U sleep@@GLIBC_2.2.5 U strchr@@GLIBC_2.2.5 U strlen@@GLIBC_2.2.5 U strncat@@GLIBC_2.2.5 U strncpy@@GLIBC_2.2.5 U system@@GLIBC_2.2.5 0000000000400778 T _init 00000000004008a0 T _start 00000000004008cc t call_gmon_start 00000000004008f0 t __do_global_dtors_aux ... ... ...
Vidíme, že při použití příznaku ‚-n‘ se výstup nejprve seřadí podle nedefinovaných symbolů a poté podle adres. Řazení by mohlo usnadnit život vývojáře, který ladí problém.
4. Vyhledejte symboly a zobrazte jejich velikost
Následující příkaz vyhledá symbol „abc“ a také zobrazí jeho velikost
$ nm -S 1 | grep abc 0000000000601040 0000000000000004 B abc
Vidíme tedy, že příznak -S zobrazuje další informaci o velikosti symbolu ‚abc‘
5. Zobrazení dynamických symbolů ve spustitelném souboru
Následující příkaz se zobrazí na dynamických symbolech ve spustitelném souboru „1“.
$ nm -D 1 w __gmon_start__ U __libc_start_main U free U malloc U printf
To by mohlo být mimořádně užitečné v případech, kdy má někdo zájem dozvědět se o symbolech, které lze za běhu vyřešit pouze sdílenými knihovnami.
6. Extrahujte symboly různých typů
Další výkonnou funkcí příkazu nm je schopnost extrahovat symbol z různých typů formátů objektových souborů. Normálně na Linuxu máme objekt nebo spustitelný kód ve formátu ‚a.out‘ nebo ELF, ale pokud je objekt nebo spustitelný kód jiného formátu, pak pro něj také nm poskytuje příznak ‚–target‘.
7. Změňte formát výstupu nm
Ve výchozím nastavení je formát výstupu zobrazený v nm typ bsd. Formát můžeme změnit pomocí parametru -f. Následující příkaz zobrazí výstup příkazu nm ve stylu posix.
$ nm -u -f posix 1 _Jv_RegisterClasses w __gmon_start__ w __libc_start_main@@GLIBC_2.2.5 U free@@GLIBC_2.2.5 U malloc@@GLIBC_2.2.5 U printf@@GLIBC_2.2.5 U
Podobně můžeme použít ‚-f sysv‘, pokud chceme, aby výstup byl ve stylu systemV.
8. Zobrazit pouze externí symboly spustitelného souboru
Následující příkaz uvádí pouze externí symboly ve spustitelném souboru
$ nm -g 1 0000000000400728 R _IO_stdin_used w _Jv_RegisterClasses 0000000000600e30 D __DTOR_END__ 0000000000601030 A __bss_start 0000000000601020 D __data_start 0000000000601028 D __dso_handle w __gmon_start__ 0000000000400640 T __libc_csu_fini 0000000000400650 T __libc_csu_init ...
Vezměte prosím na vědomí, že použití parametru -g umožňuje výstup pouze externích symbolů. To by se mohlo hodit při speciálním ladění externích symbolů.
9. Seřaďte výstup nm podle velikosti symbolu
Následující příkaz seřadí výstup podle velikosti symbolů
$ nm -g --size-sort 1 0000000000000002 T __libc_csu_fini 0000000000000004 R _IO_stdin_used 0000000000000004 B abc 0000000000000084 T main 0000000000000089 T __libc_csu_init
Všimněte si, že příznak –size-sort třídí výstup podle velikosti. Jak již bylo vysvětleno -g se používá k zobrazení pouze externích symbolů.
10. Zadejte možnosti nm v souboru
Další cennou vlastností nm je, že dokáže převzít vstup z příkazového řádku ze souboru. Můžete zadat všechny možnosti v souboru a zadat název souboru příkazu nm a ten udělá zbytek za vás. Například v následujícím příkazu obslužný program nm přečte vstup příkazového řádku ze souboru ‚nm_file‘ a vytvoří výstup
Vezměte prosím na vědomí, že pokud zadáte název souboru, je vyžadován symbol „@“.
$ nm @nm_file 0000000000000002 T __libc_csu_fini 0000000000000004 R _IO_stdin_used 0000000000000004 B abc 0000000000000084 T main 0000000000000089 T __libc_csu_init