Estoy tratando de procesar la salida de un nm o readelf -s en un ejecutable. Sin embargo, tengo problemas para diferenciar las funciones estáticas entre sí en la salida.
Esto es con lo que estoy trabajando:
prueba.c
static int foo() { int x = 6; } main() {}otro.c
static int foo() { int x = 5; }Los compilo así:
gcc -o test test.c other.cY luego ejecute un comando nm para obtener todos los símbolos:
nm testEntre los cuales aparecen los siguientes dos símbolos (para mis funciones estáticas):
00000000004004ed t foo 0000000000400500 t foo¿Existe algún método para poder distinguir de qué archivo apareció la función foo específica? ¿O tendré que hacer algo de magia antes de compilar para que esto funcione?
Debo agregar que para mi caso de uso, tengo acceso al binario final y a los archivos de objeto que usa, pero en realidad no puedo construirlo yo mismo para asegurarme de que tenga una tabla de símbolos.
¡Gracias!
Su pregunta asume que, dado un ejecutable, siempre puede descubrir los nombres de las funciones static (locales) que se compilaron en él, utilizando nm u otra herramienta. Por lo tanto, podrá ver cuándo dos o más de esos nombres son iguales y plantear la cuestión de cómo descubrir de qué archivos fuente se compilaron.
Sin embargo, esa suposición es falsa. En el caso de gcc, si los archivos se compilan con optimización -O0 , los símbolos locales se emitirán en la tabla de símbolos del archivo de objeto. -O0 es el valor predeterminado, por lo que se aplica en el caso de su:
gcc -o test test.c other.c Pero si los archivos se compilan en cualquier nivel de optimización más alto, como ciertamente lo serán para una compilación de lanzamiento, los símbolos locales se omiten de la tabla de símbolos de archivos de objetos. Así que el enlazador ni siquiera los ve. Entonces no puede recuperarlos del ejecutable con nm o cualquier otra cosa.
Compile sus archivos de muestra con:
gcc -O1 -o test test.c other.c luego nm test nuevo, y observará que:
00000000004004ed t foo 0000000000400500 t foohan desaparecido, junto con todos los demás nombres de funciones estáticas.
En ese caso, si como usted dice que no puede controlar cómo se construye el ejecutable, entonces no puede asegurarse de que sea posible que surja su pregunta .
Si pudiera controlar cómo se construye el ejecutable para asegurarse de que los archivos se compilan con -O0 , entonces hay varias formas en las que puede vincular los nombres de las funciones estáticas a los archivos de origen. Dos igualmente simples serían:
readelf -s testy
objdump -t testcada uno de los cuales enumerará un nombre de archivo de origen al principio de cada fragmento de símbolos que provienen de él.
(Y si es necesario decirlo, el enfoque gdb sugerido por @Amol no escapa a la restricción de que el ejecutable debe haber sido compilado con optimización -O0 )
Probé la siguiente secuencia.
Si ha eliminado el archivo de salida sin símbolos de depuración, al usar gdb puede crear un archivo de objeto. Siga los comandos de la siguiente manera:
$ gdb a.outdará lo siguiente como salida
Reading symbols from /home/amol/amol/a.out...(no debugging symbols found)...done. Entonces (gdb) vendrá en la terminal
Dé los siguientes comandos en secuencia (el indicador (gdb) viene por defecto a medida que escribe los comandos)
(gdb) maint print symbols filename (gdb) maint print psymbols filename (gdb) maint print msymbols filenameAhora en su carpeta puede ver un archivo con nombre como nombre de archivo . Abra este archivo en el editor de texto y podrá ver la información de la siguiente manera:
[ 8] t 0x80483b4 foo section .text test.c [ 9] T 0x80483c3 main section .text other.c [10] t 0x80483c8 foo section .text other.c Aquí puede ver claramente qué función foo() proviene de qué archivo .c . Espero que esto te ayude.
Es posible que deba leer la tabla de símbolos ELF y extraer el valor ELF32_ST_BIND.
Según la especificación ELF (consulte http://flint.cs.yale.edu/cs422/doc/ELF_Format.pdf ), los valores para ELF32_ST_BIND pueden ser:
Name Value STB_LOCAL 0 STB_GLOBAL 1 STB_WEAK 2 STB_LOPROC 13 STB_HIPROC 15Donde STB_LOCAL se define como "Los símbolos locales no son visibles fuera del archivo de objeto que contiene su definición. Los símbolos locales del mismo nombre pueden existir en varios archivos sin interferir entre sí". que parecen coincidir bastante bien con las funciones estáticas de C.
Por ejemplo, tomando su muestra y modificándola ligeramente:
test.c: static int foo() { int x = 5; } int bar() { int y = 6; } main() {} other.c: static int foo() { int x = 7; } y compilando con gcc -o test test.c other.c y mirando la tabla de símbolos (se eliminaron muchas entradas):
readelf -s test Num: Value Size Type Bind Vis Ndx Name 37: 00000000004004f0 13 FUNC LOCAL DEFAULT 13 foo 39: 0000000000400510 13 FUNC LOCAL DEFAULT 13 foo 52: 00000000004004fd 13 FUNC GLOBAL DEFAULT 13 barPodemos ver que las dos funciones estáticas se muestran como LOCALES y la función 'normal' se muestra como GLOBAL
Nota: si bien este método funcionará con archivos que no sean de depuración, si se elimina el archivo final, no podemos usar este método.