I comandi nm forniscono informazioni sui simboli utilizzati in un file oggetto o in un file eseguibile.
Le informazioni predefinite fornite dal comando 'nm' sono:
- Indirizzo virtuale del simbolo
- Un carattere che rappresenta il tipo di simbolo. Se il carattere è in minuscolo il simbolo è locale ma se il carattere è in maiuscolo il simbolo è esterno
- Nome del simbolo
I caratteri che identificano il tipo di simbolo descrivono :
- A : Simbolo assoluto globale.
- a : Simbolo assoluto locale.
- B :Simbolo globale bss.
- b :Simbolo bss locale.
- D :Simbolo dei dati globali.
- d :Simbolo dati locali.
- f :Simbolo del nome del file di origine.
- L :Simbolo locale del thread (TLS).
- l :Simbolo locale del thread (TLS).
- T :Simbolo di testo globale.
- t :Simbolo del testo locale.
- U :Simbolo non definito.
Si noti che questo elenco non è esaustivo ma contiene alcuni tipi di simboli importanti. Per informazioni complete, fare riferimento alla pagina man di questa utility.
Il modo predefinito per utilizzare l'utilità 'nm' è:
$ nm <object file or executable name>
se non viene fornito alcun nome eseguibile, nm assume che il nome sia "a.out".
Con l'idea di base su questa utilità, ci si potrebbe chiedere perché queste informazioni sarebbero necessarie?
Bene, supponiamo di avere un eseguibile composto da molti file oggetto diversi. Si supponga ora che durante la compilazione del codice, il linker dia un errore su un simbolo non risolto "temp". Ora diventerà un incubo trovare dove si trova il simbolo "temp" nel codice se il codice è troppo grande e include molte intestazioni. È qui che questa utilità viene in soccorso. Con alcune opzioni extra, questa utility fornisce anche il file in cui si trova il simbolo.
Da ora abbiamo un'idea di base sull'utilità nm. Consente di comprendere l'utilizzo di questa utility attraverso alcuni pratici comandi.
1. Visualizza i file oggetto che fanno riferimento a un simbolo
Il comando seguente mostra tutti i file oggetto che fanno riferimento al simbolo 'func' nella mia directory corrente
$ 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
Si noti che il flag -A viene utilizzato per visualizzare il nome del file insieme ad altre informazioni. Quindi vediamo che nell'output otteniamo tutti i file oggetto in cui viene utilizzato il simbolo "func". Questo potrebbe essere estremamente utile nei casi in cui vogliamo sapere come quali file oggetto stanno usando un particolare simbolo.
2. Visualizza tutti i simboli non definiti in un eseguibile
Il comando seguente elenca tutti i simboli non definiti in un file eseguibile '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
Nota che la bandiera '-u' è usata in questo caso per elencare solo i simboli non definiti. Questo potrebbe essere estremamente utile nei casi in cui potresti voler conoscere i simboli non definiti utilizzati nel codice che potrebbero essere davvero irrisolti o potrebbero essere risolti in fase di esecuzione tramite librerie condivise.
Su un argomento correlato, dovresti anche capire come funziona il processo di collegamento GCC.
3. Visualizza tutti i simboli in un eseguibile
Il comando seguente elenca tutti i simboli nell'eseguibile 'namepid' ma in ordine di indirizzi
$ 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 ... ... ...
Vediamo che usando il flag '-n', l'output risulta essere ordinato prima con i simboli indefiniti e poi secondo gli indirizzi. L'ordinamento potrebbe semplificare la vita di uno sviluppatore che sta eseguendo il debug di un problema.
4. Cerca un simbolo e visualizza le sue dimensioni
Il comando seguente cerca un simbolo 'abc' e ne mostra anche la dimensione
$ nm -S 1 | grep abc 0000000000601040 0000000000000004 B abc
Quindi vediamo che la bandiera -S mostra un'informazione extra sulla dimensione del simbolo 'abc'
5. Visualizza i simboli dinamici in un eseguibile
Il comando seguente viene visualizzato sui simboli dinamici nell'eseguibile '1'.
$ nm -D 1 w __gmon_start__ U __libc_start_main U free U malloc U printf
Questo potrebbe essere estremamente utile nei casi in cui si è interessati a conoscere i simboli che possono essere risolti solo dalle librerie condivise in fase di esecuzione.
6. Estrai simboli di vario tipo
Un'altra potente caratteristica del comando nm è quella di poter estrarre il simbolo da vari tipi di formati di file oggetto. Normalmente su Linux abbiamo un oggetto in formato 'a.out' o ELF o codice eseguibile, ma se un oggetto o un codice eseguibile è di qualche altro formato, anche nm fornisce un flag '–target' per esso.
7. Modifica il formato dell'output nm
Per impostazione predefinita, il formato di output visualizzato da nm è il tipo bsd. Possiamo cambiare il formato usando il flag -f. Il comando seguente mostra l'output del comando nm in stile 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
Allo stesso modo possiamo usare '-f sysv' se vogliamo che l'output sia in stile systemV.
8. Visualizza solo i simboli esterni di un eseguibile
Il comando seguente elenca solo i simboli esterni nell'eseguibile
$ 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 ...
Si noti che l'uso del flag -g abilita l'output solo di simboli esterni. Questo potrebbe tornare utile durante il debug speciale di simboli esterni.
9. Ordina l'output nm in base alla dimensione del simbolo
Il comando seguente ordina l'output in base alla dimensione dei simboli
$ 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
Si noti che il flag –size-sort ordina l'output rispetto alla dimensione. Come già spiegato -g è usato per visualizzare solo simboli esterni.
10. Specifica nm Opzioni in un file
Un'altra caratteristica preziosa di nm è che può prendere l'input della riga di comando da un file. Puoi specificare tutte le opzioni in un file e specificare il nome del file nel comando nm e farà il resto per te. Ad esempio, nel comando seguente l'utilità nm legge l'input della riga di comando dal file 'nm_file' e produce l'output
Tieni presente che il simbolo "@" è obbligatorio se fornisci il nome del file.
$ nm @nm_file 0000000000000002 T __libc_csu_fini 0000000000000004 R _IO_stdin_used 0000000000000004 B abc 0000000000000084 T main 0000000000000089 T __libc_csu_init