2012-09-03 2 views
6

J'essaie d'obtenir des informations sur les conventions d'appel à partir des informations DWARF. Plus spécifique, je veux savoir quels registres/emplacements de pile sont utilisés pour passer des arguments aux fonctions. Mon problème est que je reçois des informations erronées dans certains cas de DWARF. L'exemple que je me sers est le suivant « code C »:Obtention des conventions d'appel à partir de DWARF info

int __attribute__ ((fastcall)) __attribute__ ((noinline)) mult (int x, int y) { 
return x*y; 
} 

Je compile cet exemple en utilisant la commande suivante:

gcc -c -g -m32 test.c -o test.o 

Maintenant, quand j'utilise la commande suivante pour obtenir la décharge naine:

dwarfdump test.o 

Je reçois les informations suivantes sur cette fonction:

< 2><0x00000042>  DW_TAG_formal_parameter 
         DW_AT_name     "x" 
         DW_AT_decl_file    0x00000001 /home/khaled/Repo_current/trunk/test.c 
         DW_AT_decl_line    0x00000001 
         DW_AT_type     <0x0000005b> 
         DW_AT_location    DW_OP_fbreg -12 
< 2><0x0000004e>  DW_TAG_formal_parameter 
         DW_AT_name     "y" 
         DW_AT_decl_file    0x00000001 /home/khaled/Repo_current/trunk/test.c 
         DW_AT_decl_line    0x00000001 
         DW_AT_type     <0x0000005b> 
         DW_AT_location    DW_OP_fbreg -16 

En regardant les entrées DW_AT_location, il y a un décalage par rapport à la base de l'image. Cela implique qu'ils sont des arguments de mémoire, mais que la convention d'appel réelle "fastcall" force leur passage dans des registres. En regardant le désassemblage du fichier d'objet produit, je peux voir qu'ils sont copiés des registres aux emplacements de pile au point d'entrée de la fonction. Y a-t-il un moyen de savoir à partir du vidage nain - ou de toute autre manière - où les arguments sont passés lors de l'appel initialement?

Merci,

Répondre

6

Parce que vous utilisez. gcc -c -g -m32 test.c -o test.o Bien qu'il s'agisse d'une fonction fastcall, GCC doit encore générer du code pour enregistrer les valeurs des registres dans la trame de la pile au début de la fonction. Sans cela, n'importe quel débogueur ou gdb ne peut pas déboguer le programme ou ils diront que l'argument est optimisé et non montré. Cela rend le débogage impossible.

En x86_64, le compilateur utilise également certains registres pour transmettre certains arguments par défaut, même sans spécifier l'attribut fastcall pour une fonction. Vous pouvez également trouver ces registres copiés dans la pile.

// x86_64 assembly code 
_mult: 
Leh_func_begin1: 
     pushq %rbp 
Ltmp0: 
     movq %rsp, %rbp 
Ltmp1: 
     movl %edi, -4(%rbp) 
     movl %esi, -8(%rbp) 
     movl -4(%rbp), %eax 
     movl -8(%rbp), %ecx 
     imull %ecx, %eax 

Si vous activez le drapeau d'optimisation -O, -O2, -O3 (peu importe -g ou non), vous pouvez démonter et trouver il n'y a rien à copier sur le cadre de la pile. Et quand vous gdb le fichier exécutable optimisé, et arrêtez au début de la fonction pour montrer les variables locales, gdb vous dira que ces arguments sont en train d'être optimisés.

le dwarfdump exemple du programme 32 bits ressemblerait

0x00000083:  TAG_formal_parameter [4] 
       AT_name("x") 
       AT_decl_file("test.c") 
       AT_decl_line(1) 
       AT_type({0x0000005f} (int)) 
       AT_location(0x00000000 
        0x00000000 - 0x00000003: ecx 
        0x00000003 - 0x00000018: ecx) 

0x00000090:  TAG_formal_parameter [4] 
       AT_name("y") 
       AT_decl_file("test.c") 
       AT_decl_line(1) 
       AT_type({0x0000005f} (int)) 
       AT_location(0x0000001e 
        0x00000000 - 0x00000003: edx 
        0x00000003 - 0x00000018: edx) 

Et vous pouvez trouver le code assembleur généré est beaucoup plus simple et propre.

_mult: 
     pushl %ebp 
     movl %esp, %ebp 
     movl %ecx, %eax 
     imull %edx, %eax 
     popl %ebp 
     ret  $12 
Questions connexes