web-dev-qa-db-fra.com

Quand / Comment Linux charge-t-il les bibliothèques partagées dans l'espace d'adressage?

Ma question est la suivante:

Quand l'adresse des objets partagés est-elle spécifiée dans les programmes? Pendant la liaison? Chargement? Si je voulais trouver l'adresse mémoire de la commande system à l'intérieur de libc à l'intérieur de mon programme, je pourrais la trouver facilement dans gdb, mais si je ne veux pas mettre le programme dans un débogueur?

Cette adresse peut-elle changer d'une exécution à l'autre? Existe-t-il un autre outil d'analyse statique qui permettra de voir où les bibliothèques ou les fonctions seront chargées dans l'espace mémoire de ce programme lors de son exécution?

EDIT: Je veux ces informations en dehors du programme (c'est-à-dire en utilisant des utilitaires comme objdump pour recueillir des informations)

39
Ryan

Les bibliothèques sont chargées par ld.so (éditeur de liens dynamique ou éditeur d'exécution aka rtld, ld-linux.so.2 ou ld-linux.so.* dans le cas de Linux; partie de la glibc). Il est déclaré comme "interprète" (INTERP; .interp section) de tous les binaires ELF liés dynamiques. Ainsi, lorsque vous démarrez le programme, Linux démarre un ld.so (charger en mémoire et passer à son point d'entrée), puis ld.so chargera votre programme en mémoire, le préparera puis l'exécutera. Vous pouvez également démarrer un programme dynamique avec

 /lib/ld-linux.so.2 ./your_program your_prog_params

ld.so fait un réel open et mmap de tous les fichiers ELF nécessaires, à la fois le fichier ELF de votre programme et les fichiers ELF de toutes les bibliothèques nécessaires. En outre, il remplit les tables GOT et PLT et résout les délocalisations (il écrit les adresses des fonctions des bibliothèques vers les sites d'appels, dans de nombreux cas avec des appels indirects).

L'adresse de chargement typique d'une bibliothèque que vous pouvez obtenir avec l'utilitaire ldd. Il s'agit en fait d'un script bash, qui définit une variable d'environnement de débogage de ld.so (en fait LD_TRACE_LOADED_OBJECTS=1 en cas de rtld de glibc) et démarre un programme. Vous pouvez même le faire vous-même sans avoir besoin du script, par exemple en utilisant bash changement facile des variables d'environnement pour une seule exécution:

 LD_TRACE_LOADED_OBJECTS=1 /bin/echo

Le ld.so verra cette variable et résoudra toutes les bibliothèques nécessaires et en affichera l'adresse de chargement. Mais avec cet ensemble de variables, ld.so ne démarrera pas réellement un programme (pas sûr des constructeurs statiques du programme ou des bibliothèques). Si fonction ASLR est désactivée, l'adresse de chargement sera la même la plupart du temps. Les Linux modernes ont souvent ASLR activé, donc pour le désactiver, utilisez echo 0 | Sudo tee /proc/sys/kernel/randomize_va_space .

Vous pouvez trouver le décalage de la fonction system dans le libc.so avec l'utilitaire nm de binutils. Je pense que vous devriez utiliser nm -D /lib/libc.so ou objdump -T /lib/libc.so et sortie grep.

66
osgx

"Allez directement à la source et demandez au cheval ..."

Drepper - Comment écrire des bibliothèques partagées

Documentation indispensable pour les rédacteurs de bibliothèques Linux. Explique la mécanique du chargement en détail.

13
Dipstick

La commande nm, utilisée sur libc.so, vous montrera l'emplacement du symbole system dans libc.so. Cependant, si ASLR est activé, l'adresse libc.so est chargé à, et donc l'adresse finale de system variera de façon aléatoire à chaque exécution de votre programme. Même sans ASLR, vous devrez déterminer l'adresse libc.so est chargé à et compenser l'adresse de system de ce montant.

Si vous voulez juste l'adresse d'une fonction sans coder en dur le nom, vous pouvez dlopen() le programme principal:

void *self = dlopen(NULL, RTLD_NOW);
dlsym(self, "system"); // returns the pointer to the system() function

Si vous voulez simplement l'adresse d'une fonction dont vous connaissez le nom au moment de la compilation, utilisez simplement void *addr = &system;

7
ThiefMaster