Dites au moment de l'exécution, je veux savoir où une fonction "printf" est définie. Comment ferais-je cela?
mon programme:
#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);
}
sortie:
-bash-4.1$ ./a &
[1] 28837
-bash-4.1$ address of printf is 0x4003F8
pid is 28837
Cependant, cela indique que la fonction est définie dans mon propre programme!
-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
Ne devrait-il pas s'agir d'un appel à la libc? Comment puis-je savoir d'où vient cette "printf" ou toute autre fonction?
Au moment de l'exécution, vous pouvez utiliser gdb
pour cela:
(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 vous ne souhaitez pas interrompre votre programme ou êtes réticent à utiliser gdb
, vous pouvez également demander à ld.so
de fournir des informations de débogage:
(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]
L'adresse que vous observez se trouve dans le tableau de liaison des procédures (PLT). Ce mécanisme est utilisé lorsque l'emplacement d'un symbole externe (lié dynamiquement) n'est pas connu à ce moment-là, lorsque votre fichier binaire est compilé et lié.
Le but est que le lien externe ne se produise qu’à un seul endroit, le PLT, et non à tous les endroits de votre code où un appel au symbole a lieu. Donc, si printf()
est appelé, le chemin est le suivant:
main -> printf @ PLT -> printf @ libc
Au moment de l'exécution, vous ne pouvez pas trouver facilement dans quelle bibliothèque externe se trouve la fonction que vous appelez; vous auriez à analyser les opcodes à la destination (le PLT), qui récupère généralement l'adresse de la section .dynamic et y saute, puis regardez où le symbole est réellement situé et enfin, analysez/proc/pid/maps la bibliothèque externe.
les pointeurs sont printf
ed en utilisant %p
, pas %X
:
printf("address of printf is 0x%p\n", printf);
Si vous compilez contre la bibliothèque statique printf
sera lié à votre binaire
quand compilé avec
gcc -fPIC a.c # (older gccs)
...
gcc -fno-plt a.c # (gcc 6 and above)
les sorties:
address of printf is 0x0x7f40acb522a0
qui est à l'intérieur de
7f40acaff000-7f40accc2000 r-xp 00000000 fd:00 100687388 /usr/lib64/libc-2.17.so
Lire Que signifie @plt ici? pour en savoir plus à ce sujet.
Dites au moment de l'exécution, je veux savoir où une fonction "printf" est définie.
En termes généraux et absolus, vous ne pourrez probablement pas (du moins pas facilement). Une fonction donnée peut être définie dans plusieurs bibliothèques (pour printf
, il est peu probable; elle se trouve dans la bibliothèque standard C).
Si vous construisez votre système Linux à partir de rien , vous pouvez rêver de traiter toutes les bibliothèques au moment de la construction (par exemple, lors de la construction de chaque bibliothèque partagée, vous pouvez obtenir tous ses noms publics avec nm (1) et les mettre dans une base de données). Ce n'est pas encore vraiment fait aujourd'hui, mais certains projets de recherche vont dans cette direction (notamment softwareheritage et d'autres en 2019).
En passant, vous pourriez avoir plusieurs bibliothèques définissant printf
. Par exemple, si vous installez à la fois GNU glibc et musl-libc sur votre ordinateur (ou plus probablement, si vous avez plusieurs variantes de glibc
). Il est peu probable qu'un programme particulier utilise les deux (mais pourrait, en théorie, dlopen
les deux).
Vous voulez peut-être la fonction dladdr (3) spécifique à Linux. A partir d'une adresse donnée, il vous dit que l'objet partagé l'a.
la fonction est définie dans mon propre programme
Oui. En savoir plus sur la liaison dynamique . En particulier, lisez Drepper's Comment écrire des bibliothèques partagées paper. Comprendre à quoi sert le tableau de correspondance des procédures .
Vous pouvez en déduire statiquement. Pas besoin d'exécuter:
$ 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
Analyser le fichier elf pour les bibliothèques liées dynamiquement nécessaires. Ensuite, vous pouvez les analyser en recherchant le symbole requis