funciones - Programa Linux C: cómo encontrar la biblioteca a la que pertenece una función
funciones de biblioteca programacion (6)
Por ejemplo, en tiempo de ejecución, quiero averiguar dónde se define una función "printf".
En términos generales y absolutos, probablemente no pueda (al menos no fácilmente). Una función dada podría definirse en varias bibliotecas (para printf
, eso es poco probable, ya que está dentro de la biblioteca estándar de C).
Si construye su sistema Linux desde cero , podría soñar con que algo procesara cada biblioteca en el momento de la creación (por ejemplo, al construir cada biblioteca compartida, podría obtener todos sus nombres públicos con nm(1) y colocarlos en alguna base de datos). Esto aún no se ha hecho realmente hoy, pero algunos proyectos de investigación van en esa dirección (en particular softwareheritage patrimonio del softwareheritage y otros en 2019).
Por cierto, podrías tener varias bibliotecas definiendo printf
. Por ejemplo, si instala GNU glibc y musl-libc en su computadora (o más probablemente, si tiene varias variantes de glibc
). Es poco probable que un programa en particular use ambos (pero aún podría, en teoría, dlopen
ambos).
Tal vez quiera la función dladdr(3) específica de Linux. Desde alguna dirección dada, te dice el objeto compartido que lo tiene.
La función está definida en mi propio programa.
Sí. Lea mucho más sobre la vinculación dinámica . En particular, lea el documento Cómo escribir bibliotecas compartidas de Drepper. Entender cuál es el propósito de la tabla de vinculación de procedimientos .
Por ejemplo, en tiempo de ejecución, quiero averiguar dónde se define una función "printf". ¿Cómo haría esto? Mi primer intento fue imprimir la dirección de "printf" y compararla con la asignación de direcciones virtuales del proceso:
mi programa:
#include <stdio.h>
#include <unistd.h>
void main()
{
printf("address of printf is 0x%X/n", printf);
printf("pid is %d/n", getpid());
while (1);
}
salida:
-bash-4.1$ ./a &
[1] 28837
-bash-4.1$ address of printf is 0x4003F8
pid is 28837
Sin embargo, esto dice que la función está definida en mi propio programa!
-bash-4.1$ head /proc/28837/maps
00400000-00401000 r-xp 00000000 08:06 6946857 /data2/temp/del/a <<<<<<< Address 0x4003F8 is in my own program?
00600000-00601000 rw-p 00000000 08:06 6946857 /data2/temp/del/a
397ec00000-397ec20000 r-xp 00000000 08:11 55837039 /lib64/ld-2.12.so
397ee1f000-397ee20000 r--p 0001f000 08:11 55837039 /lib64/ld-2.12.so
397ee20000-397ee21000 rw-p 00020000 08:11 55837039 /lib64/ld-2.12.so
397ee21000-397ee22000 rw-p 00000000 00:00 0
397f000000-397f18a000 r-xp 00000000 08:11 55837204 /lib64/libc-2.12.so
397f18a000-397f38a000 ---p 0018a000 08:11 55837204 /lib64/libc-2.12.so
397f38a000-397f38e000 r--p 0018a000 08:11 55837204 /lib64/libc-2.12.so
397f38e000-397f38f000 rw-p 0018e000 08:11 55837204 /lib64/libc-2.12.so
¿No debería ser una llamada a libc? ¿Cómo puedo saber de dónde vino este "printf" o cualquier otra función?
Los punteros se
printf
usando%p
, no%X
:printf("address of printf is 0x%p/n", printf);
Si compilas contra estática, libc
printf
estará vinculado a tu binariocuando se compila con
gcc -fPIC a.c # (older gccs) ... gcc -fno-plt a.c # (gcc 6 and above)
salidas:
address of printf is 0x0x7f40acb522a0
que está dentro de
7f40acaff000-7f40accc2000 r-xp 00000000 fd:00 100687388 /usr/lib64/libc-2.17.so
Leer ¿Qué significa @plt aquí? para encontrar mas sobre esto
Analice el archivo elf para las bibliotecas enlazadas dinámicamente necesarias. Luego puedes analizarlos buscando el símbolo requerido.
En tiempo de ejecución, puede usar gdb
para esto:
(terminal 1)$ ./a
pid is 16614
address of printf is 0x400450
(terminal 2)$ gdb -p 16614
(...)
Attaching to process 16614
(...)
0x00000000004005a4 in main ()
(gdb)
(gdb) info sym printf
printf in section .text of /lib/x86_64-linux-gnu/libc.so.6
Si no desea interrumpir su programa o no desea utilizar gdb
, también puede pedirle a ld.so
que ld.so
cierta información de depuración:
(terminal 1)$ LD_DEBUG=bindings LD_DEBUG_OUTPUT=syms ./a
pid is 17180
address of printf is 0x400450
(terminal 2)$ fgrep printf syms.17180
17180: binding file ./a [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `printf'' [GLIBC_2.2.5]
La dirección que observa se encuentra en la Tabla de vinculación de procedimientos (PLT). Este mecanismo se utiliza cuando no se conoce la ubicación de un símbolo externo (enlazado dinámicamente) en el momento en que se compila y se vincula su binario.
El propósito es que el enlace externo ocurra solo en un lugar, el PLT, y no en todos los lugares a lo largo de su código donde ocurre una llamada al símbolo. Entonces, si se llama a printf()
, la forma es:
main -> printf @ PLT -> printf @ libc
En tiempo de ejecución, no puede encontrar fácilmente en qué biblioteca externa se encuentra la función a la que llama; Tendría que analizar los códigos de operación en el destino (el PLT), que generalmente obtiene la dirección de la sección .dynamic y salta allí, luego busca, donde realmente se encuentra el símbolo y finalmente, analiza / proc / pid / maps para obtener La biblioteca externa.
Puedes deducir esto estáticamente. No es necesario ejecutar:
$ readelf -Ws a.out | grep printf
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.2.5 (2)
51: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@@GLIBC_2.2.5