Los comandos nm proporcionan información sobre los símbolos que se utilizan en un archivo de objeto o un archivo ejecutable.
La información predeterminada que proporciona el comando 'nm' es:
- Dirección virtual del símbolo
- Un carácter que representa el tipo de símbolo. Si el carácter está en minúsculas, el símbolo es local, pero si el carácter está en mayúsculas, el símbolo es externo
- Nombre del símbolo
Los caracteres que identifican el tipo de símbolo describen:
- A : Símbolo absoluto global.
- un : Símbolo absoluto local.
- B :Símbolo bss global.
- b :símbolo bss local.
- D :Símbolo de datos globales.
- d :Símbolo de datos locales.
- f :símbolo de nombre de archivo de origen.
- L :símbolo local de subproceso global (TLS).
- l :Símbolo local de subproceso estático (TLS).
- T :símbolo de texto global.
- t :Símbolo de texto local.
- T :Símbolo indefinido.
Tenga en cuenta que esta lista no es exhaustiva, pero contiene algunos tipos de símbolos importantes. Para obtener información completa, consulte la página del manual de esta utilidad.
La forma predeterminada de usar la utilidad 'nm' es:
$ nm <object file or executable name>
si no se proporciona un nombre ejecutable, entonces nm asume que el nombre es 'a.out'.
Con la idea básica de esta utilidad, uno puede preguntarse por qué se requiere esta información.
Bueno, suponga que tiene un ejecutable que está hecho de muchos archivos de objetos diferentes. Ahora suponga que mientras compila el código, el enlazador da un error sobre un símbolo 'temp' no resuelto. Ahora será una pesadilla encontrar dónde está el símbolo 'temp' en el código si el código es demasiado grande e incluye muchos encabezados. Es aquí donde esta utilidad viene al rescate. Con algunas opciones adicionales, esta utilidad también proporciona el archivo en el que se encuentra el símbolo.
Ya que ahora tenemos una idea básica sobre la utilidad nm. Entendamos el uso de esta utilidad a través de algunos comandos prácticos.
1. Mostrar archivos de objetos que hacen referencia a un símbolo
El siguiente comando muestra todos los archivos de objetos que se refieren al símbolo 'func' en mi directorio actual
$ 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
Tenga en cuenta que el indicador -A se usa para mostrar el nombre del archivo junto con otra información. Entonces vemos que en la salida obtenemos todos los archivos de objetos donde se usa el símbolo 'func'. Esto podría ser extremadamente útil en los casos en que queremos saber qué archivos de objetos están usando un símbolo en particular.
2. Mostrar todos los símbolos indefinidos en un ejecutable
El siguiente comando enumera todos los símbolos no definidos en un archivo ejecutable '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
Tenga en cuenta que la bandera '-u' se usa en este caso para enumerar solo los símbolos no definidos. Esto podría ser extremadamente útil en casos en los que uno desee saber acerca de los símbolos no definidos que se utilizan en el código que realmente podrían no estar resueltos o podrían resolverse en tiempo de ejecución a través de bibliotecas compartidas.
En un tema relacionado, también debe comprender cómo funciona el proceso de vinculación de GCC.
3. Mostrar todos los símbolos en un ejecutable
El siguiente comando enumera todos los símbolos en el ejecutable 'namepid' pero en el orden de sus direcciones
$ 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 ... ... ...
Vemos que al usar la bandera '-n', la salida se ordena primero con los símbolos indefinidos y luego de acuerdo con las direcciones. La clasificación podría facilitar la vida de un desarrollador que está depurando un problema.
4. Busque un símbolo y muestre su tamaño
El siguiente comando busca un símbolo 'abc' y también muestra su tamaño
$ nm -S 1 | grep abc 0000000000601040 0000000000000004 B abc
Entonces vemos que la bandera -S muestra información adicional sobre el tamaño del símbolo 'abc'
5. Mostrar símbolos dinámicos en un ejecutable
El siguiente comando se muestra en símbolos dinámicos en el ejecutable '1'.
$ nm -D 1 w __gmon_start__ U __libc_start_main U free U malloc U printf
Esto podría ser extremadamente útil en los casos en los que uno esté interesado en conocer los símbolos que solo pueden resolver las bibliotecas compartidas en tiempo de ejecución.
6. Extraer símbolos de varios tipos
Otra característica poderosa del comando nm es poder extraer el símbolo de varios tipos de formato de archivo de objeto. Normalmente, en Linux tenemos un objeto o código ejecutable en formato 'a.out' o ELF, pero si un objeto o código ejecutable tiene algún otro formato, nm también proporciona un indicador '–target' para él.
7. Cambiar el formato de la salida nm
Por defecto, el formato de salida que muestra nm es el tipo bsd. Podemos cambiar el formato usando la bandera -f. El siguiente comando muestra la salida del comando nm en estilo 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
De manera similar, podemos usar '-f sysv' si queremos que la salida tenga el estilo systemV.
8. Mostrar solo los símbolos externos de un ejecutable
El siguiente comando enumera solo los símbolos externos en el ejecutable
$ 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 ...
Tenga en cuenta que el uso de flag -g habilita la salida de solo símbolos externos. Esto podría resultar útil al depurar especialmente símbolos externos.
9. Ordene la salida nm por el tamaño del símbolo
El siguiente comando ordena la salida por el tamaño de los símbolos
$ 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
Tenga en cuenta que la marca –size-sort ordena la salida con respecto al tamaño. Como ya se explicó, -g se usa para mostrar solo símbolos externos.
10. Especificar opciones nm en un archivo
Otra característica valiosa de nm es que puede tomar la entrada de la línea de comandos de un archivo. Puede especificar todas las opciones en un archivo y especificar el nombre del archivo en el comando nm y hará el resto por usted. Por ejemplo, en el siguiente comando, la utilidad nm lee la entrada de la línea de comando del archivo 'nm_file' y produce la salida
Tenga en cuenta que se requiere el símbolo '@' si proporciona el nombre del archivo.
$ nm @nm_file 0000000000000002 T __libc_csu_fini 0000000000000004 R _IO_stdin_used 0000000000000004 B abc 0000000000000084 T main 0000000000000089 T __libc_csu_init