89 votes

Qu'est-ce que @plt signifie ici ?

0x00000000004004b6 :   callq  0x400398 

Quelqu'un sait?

MISE À JOUR

Pourquoi deux disas printf me donnent des résultats différents?

(gdb) disas printf
Dump of assembler code for function printf@plt:
0x0000000000400398 :  jmpq   *0x2004c2(%rip)        # 0x600860 <_GLOBAL_OFFSET_TABLE_+24>
0x000000000040039e :  pushq  $0x0
0x00000000004003a3 : jmpq   0x400388

(gdb) disas printf
Dump of assembler code for function printf:
0x00000037aa44d360 :  sub    $0xd8,%rsp
0x00000037aa44d367 :  mov    %rdx,0x30(%rsp)
0x00000037aa44d36c : movzbl %al,%edx
0x00000037aa44d36f : mov    %rsi,0x28(%rsp)
0x00000037aa44d374 : lea    0x0(,%rdx,4),%rax
0x00000037aa44d37c : lea    0x3f(%rip),%rdx        # 0x37aa44d3c2

161voto

paxdiablo Points 341644

C'est une façon d'obtenir des corrections de code (ajustant les adresses en fonction de l'emplacement du code dans la mémoire virtuelle, qui peut varier d'un processus à l'autre) sans avoir à maintenir une copie séparée du code pour chaque processus. Le PLT, ou table de liaison de procédure, est l'une des structures qui facilite l'utilisation du chargement et du liage dynamiques (une autre est la GOT, ou table des offsets globaux).

Consultez le diagramme suivant, qui montre à la fois votre code appelant et le code de bibliothèque (que vous appelez) cartographiés à différentes adresses virtuelles dans deux processus différents, A et B. Il n'y a qu'une seule copie de chaque morceau de code en mémoire réelle, les différentes adresses virtuelles à l'intérieur de chaque processus étant mappées sur cette adresse réelle) :

Processus A
    Adresses (virtuelles):
        0x1234                      0x8888
        +-------------+ +---------+ +---------+
        |             | | Privé   | |         |
        |             | | PLT/GOT | |         |
        | Partagé     | +---------+ | Partagé |
    ===== application =============== bibliothèque =====
        | code        | +---------+ | code    |
        |             | | Privé   | |         |
        |             | | PLT/GOT | |         |
        +-------------+ +---------+ +---------+
        0x2020                      0x6666
Processus B

Lorsque la bibliothèque partagée est introduite dans l'espace d'adressage, des entrées sont construites dans le PLT et/ou GOT spécifiques au processus qui, lors de la première utilisation, effectueront certaines corrections pour rendre les choses plus rapides. Les utilisations ultérieures contourneront ensuite la correction car elle ne sera plus nécessaire.

Le processus se déroule de la manière suivante.

printf@plt est en réalité un petit segment qui (au final) appelle la vraie fonction printf, en modifiant des choses en cours de route pour rendre les appels suivants plus rapides.

La vraie fonction printf est cartographiée dans un emplacement arbitraire dans un processus donné (espace d'adresse virtuelle), tout comme le code qui essaie de l'appeler.

Ainsi, pour permettre le partage approprié du code appelant (côté gauche ci-dessus) et du code appelé (côté droit), vous ne pouvez pas appliquer de corrections au code appelant directement car cela "endommagerait" son fonctionnement dans les autres processus (ce qui n'aurait pas d'importance s'il était mappé à la même emplacement dans chaque processus mais c'est une restriction un peu ennuyeuse, surtout si autre chose avait déjà été mappé là-bas).

Ainsi, le PLT est une zone plus petite et spécifique au processus à une adresse calculée de manière fiable à l'exécution qui n'est pas partagée entre les processus, de sorte que tout processus donné est libre de le modifier comme il le souhaite, sans effets indésirables sur les autres processus.


Examinons le processus un peu plus en détail. Le diagramme ci-dessus ne montre pas l'adresse du PLT/GOT car elle peut être trouvée en utilisant une adresse relative au compteur de programme actuel. Cela est attesté par votre recherche relative au PC :

: jmpq  *0x2004c2(%rip)  ; 0x600860 <_GOT_+24>

En utilisant un code indépendant de la position dans la bibliothèque appelée, avec le PLT/GOT, le premier appel à la fonction printf@plt (donc dans le PLT) est une opération à plusieurs étapes, au cours de laquelle les actions suivantes ont lieu :

  • Il appelle la version GOT (via un pointeur) qui pointe initialement vers un code de configuration dans le PLT.
  • Ce code de configuration charge la bibliothèque partagée pertinente si ce n'est pas encore fait, puis modifie le pointeur GOT de sorte que les appels ultérieurs se fassent directement vers le vrai printf (à l'adresse virtuelle spécifique au processus) plutôt que le code de configuration du PLT.
  • Ensuite, il appelle le code de printf chargé à cette adresse.

Lors de appels ultérieurs, parce que le pointeur GOT a été modifié, l'approche à plusieurs étapes est simplifiée :

  • Il appelle la version GOT (via pointeur), qui pointe maintenant vers le vrai printf.

Un bon article peut être trouvé ici, détaillant comment glibc est chargé à l'exécution.

7voto

redobot Points 31

Je ne suis pas sûr, mais probablement ce que vous avez vu a du sens. La première fois que vous exécutez la commande disas, le printf n'est pas encore appelé donc il n'est pas résolu. Une fois que votre programme appelle la méthode printf pour la première fois, le GOT est mis à jour et maintenant le printf est résolu et le GOT pointe vers la vraie fonction. Ainsi, l'appel suivant à la commande disas affiche l'assemblage réel de printf.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X