Pourquoi l’éditeur de liens génère-t-il des déplacements apparemment inutiles dans .rela.plt?

Tout d’abord, le programme de jouets avec lequel je joue:

prog.c:

int func1(); int main(int argc, char const *argv[]) { func1(); return 0; } 

lib.c:

 int func1() { return 0; } 

Construire avec:

 gcc -O3 -g -shared -fpic ./lib.c -o liba.so gcc prog.c -g -la -L. -o prog -Wl,-rpath=$PWD 

Et pour être complet:

 $ gcc --version gcc (GCC) 6.3.1 $ ld --version GNU ld version 2.26.1 

Maintenant, ma question. J’ai confirmé ce que j’avais lu sur le fonctionnement des liaisons dynamics des symboles dynamics, c’est-à-dire qu’au départ, l’entrée GOT de func1 à la PLT, à l’instruction suivant le saut:

 $ gdb prog (gdb) disassemble main Dump of assembler code for function main: 0x0000000000400666 : push rbp 0x0000000000400667 : mov rbp,rsp 0x000000000040066a : sub rsp,0x10 0x000000000040066e : mov DWORD PTR [rbp-0x4],edi 0x0000000000400671 : mov QWORD PTR [rbp-0x10],rsi 0x0000000000400675 : mov eax,0x0 0x000000000040067a : call 0x400560  <<< call to shared lib via PLT 0x000000000040067f : mov eax,0x0 0x0000000000400684 : leave 0x0000000000400685 : ret End of assembler dump. (gdb) disassemble 0x400560 Dump of assembler code for function func1@plt: 0x0000000000400560 : jmp QWORD PTR [rip+0x200ab2] # 0x601018 <<< JMP to address stored in GOT 0x0000000000400566 : push 0x0 <<< ... which initially points right back here 0x000000000040056b : jmp 0x400550 End of assembler dump. (gdb) x/g 0x601018 0x601018: 0x400566 <<< GOT point back right after the just-executed jump 

C’est bon. En examinant maintenant la section .got.plt à 0x601018 , qui contient ce pointeur sur 0x400566 voit que le binary lui-même contient l’adresse, sans que l’éditeur de liens dynamic ne fasse quoi que ce soit au moment du chargement. Ce qui est logique, puisque cette adresse est connue au moment du lien:

 $ readelf -x .got.plt ./prog Hex dump of section '.got.plt': NOTE: This section has relocations against it, but these have NOT been applied to this dump. 0x00601000 ???????? ???????? ???????? ???????? ..`............. 0x00601010 ???????? ???????? 66054000 ???????? ........f.@..... 

(où 66054000 est la représentation little-endian pour l’adresse 0x400566 stockée dans le GOT initialement)

Bien bien. Mais alors…

 $ readelf -r prog  Relocation section '.rela.plt' at offset 0x518 contains 1 ensortinges: Offset Info Type Sym. Value Sym. Name + Addend 000000601018 000100000007 R_X86_64_JUMP_SLO 0000000000000000 func1 + 0 

Pourquoi y a-t-il une entrée de relocalisation pour cette adresse GOT? nous avons vu que la bonne adresse est déjà là, dans le binary, placé là au moment du lien. J’ai également modifié cette relocalisation à titre expérimental pour la rendre inefficace, et le programme fonctionne correctement . Donc, la relocalisation semble ne rien apporter. Que fait-il là-bas et plus généralement, quels sont les scénarios dans lesquels les délocalisations dans rela.plt réellement?

Mise à jour n ° 1:

Pour être clair, il s’agit du code PIC et du code 64 bits. Voici les adresses de section pertinentes, pour aider à préciser à qui appartiennent les adresses:

 $ readelf -S ./prog Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 9] .rela.dyn RELA 00000000004004e8 000004e8 0000000000000030 0000000000000018 A 5 0 8 [10] .rela.plt RELA 0000000000400518 00000518 0000000000000018 0000000000000018 AI 5 23 8 [12] .plt PROGBITS 0000000000400550 00000550 0000000000000020 0000000000000010 AX 0 0 16 [21] .dynamic DYNAMIC 0000000000600e00 00000e00 00000000000001f0 0000000000000010 WA 6 0 8 [22] .got PROGBITS 0000000000600ff0 00000ff0 0000000000000010 0000000000000008 WA 0 0 8 [23] .got.plt PROGBITS 0000000000601000 00001000 0000000000000020 0000000000000008 WA 0 0 8 

Mise à jour # 2:

La modification de l’en-tête de section pour .rela.plt ne modifie pas l’image de processus. Je n’ai donc pas désactivé le déplacement. J’avais aussi essayé de changer l’adresse de relocalisation (en une autre adresse accessible en écriture) et cela ne semblait pas faire de différence non plus, mais il s’avère que l’adresse n’est pas utilisée par le résolveur lors de la reliure différée, bien que le rest de la relocalisation est. L’adresse elle-même n’est utilisée que si la liaison LD_BIND_NOW est désactivée avec LD_BIND_NOW .

Merci @yugr

C’est bon. En examinant maintenant la section .got.plt à 0x601018, qui contient ce pointeur sur 0x400566, on voit que le binary lui-même contient l’adresse, sans que l’éditeur de liens dynamic ne fasse quoi que ce soit au moment du chargement.

Pas vraiment. Notez que le code à 0x400566 finit par sauter à 0x400550, soit 16 octets avant le stub PLT. Le code à 0x400550 va pousser l’adresse de GOT sur la stack et appeler dans l’éditeur de liens dynamic. Pour plus d’informations, jetez un coup d’œil à cette présentation (diapositive 14).

Ce qui a du sens, puisque cette adresse est connue au moment du lien

Est-ce L’adresse viendra de la bibliothèque partagée qui sera chargée au hasard au démarrage en raison d’ASLR. Il n’y a donc aucun moyen pour l’éditeur de liens statique de connaître l’adresse …

Pourquoi y a-t-il une entrée de relocalisation pour cette adresse GOT?

Lorsque le stub PLT appelle le lieur dynamic (au premier appel), il lui transmet l’adresse d’entrée GOT. L’éditeur de liens dynamic recherchera .rela.plt pour savoir comment déplacer une entrée GOT (c’est-à-dire le nom de la fonction et son offset).