2010-06-22 9 views
4

récemment (ne plus y aller) J'ai moi-même appris le format de fichier ELF. J'ai largement suivi la documentation ici: http://www.skyfree.org/linux/references/ELF_Format.pdf.Est-ce que sh_addr est toujours égal à sh_offset dans le format de fichier ELF?

Il était tout va très bien, et j'ai écrit ce programme pour me donner des informations au sujet d'une des sections de fichier ELF:

#include <stdio.h> 
#include <stdlib.h> 
#include <assert.h> 
#include <elf.h> 


void dumpShdrInfo(Elf32_Shdr elfShdr, const char *sectionName) 
{ 
printf("Section '%s' starts at 0x%08X and ends at 0x%08X\n", 
    sectionName, elfShdr.sh_offset, elfShdr.sh_offset + elfShdr.sh_size); 
} 

int search(const char *name) 
{ 
Elf32_Ehdr elfEhdr; 
Elf32_Shdr *elfShdr; 
FILE *targetFile; 
char tempBuf[64]; 
int i, ret = -1; 

targetFile = fopen(name, "r+b"); 

if(targetFile) 
{ 
    /* read the ELF header */ 
    fread(&elfEhdr, sizeof(elfEhdr), 1, targetFile); 


    /* Elf32_Ehdr.e_shnum specifies how many sections there are */ 
    elfShdr = calloc(elfEhdr.e_shnum, sizeof(*elfShdr)); 
    assert(elfShdr); 

    /* set the file pointer to the section header offset and read it */ 
    fseek(targetFile, elfEhdr.e_shoff, SEEK_SET); 
    fread(elfShdr, sizeof(*elfShdr), elfEhdr.e_shnum, targetFile); 


    /* loop through every section */ 
    for(i = 0; (unsigned int)i < elfEhdr.e_shnum; i++) 
    { 


     /* if Elf32_Shdr.sh_addr isn't 0 the section will appear in memory*/ 
     if(elfShdr[i].sh_addr) 
     { 

      /* set the file pointer to the location of the section's name and then read the name */ 
      fseek(targetFile, elfShdr[elfEhdr.e_shstrndx].sh_offset + elfShdr[i].sh_name, SEEK_SET); 
      fgets(tempBuf, sizeof(tempBuf), targetFile); 

      #if defined(DEBUG) 
      dumpShdrInfo(elfShdr[i], tempBuf); 
      #endif 
     } 
    } 

    fclose(targetFile); 
    free(elfShdr); 
} 

return ret; 
} 

int main(int argc, char *argv[]) 
{ 
if(argc > 1) 
{ 
    search(argv[1]); 
} 
return 0; 
} 

Après l'exécution quelques fois sur quelques fichiers que j'ai remarqué quelque chose de bizarre. La section '.text' a toujours commencé à une adresse virtuelle très basse (nous parlons plus petit que 1000h). Après avoir creusé avec gdb pendant un moment, j'ai remarqué que pour chaque section, sh_addr était égal à sh_offset.

C'est ce que je suis confus au sujet de - Elf32_Shdr.sh_addr est documentée comme étant "l'adresse à laquelle le premier octet devrait résider", tandis que Elf32_Shdr.sh_offset est documentée comme étant "le décalage d'octets depuis le début du fichier au premier octet de la fonction ". Si ce sont les deux cas, cela n'a pas vraiment de sens pour moi qu'ils soient tous les deux égaux. Pourquoi est-ce? Maintenant, je sais que certaines sections contiennent des données non initialisées (.bss je pense), il serait donc logique que ces données n'apparaissent pas dans le fichier mais apparaissent dans la mémoire du processus. Cela signifierait que pour chaque section qui vient après celle mentionnée ci-dessus, trouver son adresse virtuelle serait beaucoup plus compliqué qu'une simple variable.

Cela étant dit, existe-t-il un moyen de déterminer réellement l'adresse virtuelle d'une section?

Répondre

3

J'ai essayé cela et Elf32_Shdr.sh_addr n'est pas la même chose que Elf32_Shdr.sh_offset dans mon exemple. Il est décalé par 0x08040000, qui est l'adresse de démarrage virtuelle du programme en mémoire. Elf32_Shdr.sh_offset est 0x00000570 pour la section '.text' et Elf32_Shdr.sh_addr est 0x08048570 pour la même section.

Comme vous avez cité la documentation Elf32_Shdr.sh_offset est « l'octet décalé par rapport au début du fichier au premier octet dans la fonction »:

$> hexdump -C -s 0x00000570 -n 64 elffile 
00000570 31 ed 5e 89 e1 83 e4 f0 50 54 52 68 b0 88 04 08 |1.^.....PTRh....| 
00000580 68 c0 88 04 08 51 56 68 66 88 04 08 e8 3b ff ff |h....QVhf....;..| 
00000590 ff f4 90 90 90 90 90 90 90 90 90 90 90 90 90 90 |................| 
000005a0 55 89 e5 83 ec 08 80 3d 44 a0 04 08 00 74 0c eb |U......=D....t..| 

et Elf32_Shdr.sh_addr est « l'adresse à laquelle le premier octet devrait résider ". C'est l'adresse virtuelle des données dans la mémoire:

(gdb) print/x *(char[64] *) 0x08048570 
$4 = { 
0x31, 0xed, 0x5e, 0x89, 0xe1, 0x83, 0xe4, 0xf0, 0x50, 0x54, 0x52, 0x68, 0xb0, 0x88, 0x04, 0x08, 
0x68, 0xc0, 0x88, 0x04, 0x08, 0x51, 0x56, 0x68, 0x66, 0x88, 0x04, 0x08, 0xe8, 0x3b, 0xff, 0xff, 
0xff, 0xf4, 0x90 <repeats 14 times>, 
0x55, 0x89, 0xe5, 0x83, 0xec, 0x08, 0x80, 0x3d, 0x44, 0xa0, 0x04, 0x08, 0x00, 0x74, 0x0c, 0xeb} 
+0

Utilisez-vous le code que j'ai collé? Si non, pourriez-vous indiquer ce que je fais mal? La sortie que je reçois est ici http://pastebin.com/qTPG85sT – masseyc

+0

err, ne tenez pas compte de cela. http://stackoverflow.com/questions/3091363/question-about-the-elf-file-format-sh-addr-always-equals-sh-offset/3098927#3098927 – masseyc

0

Bon, après avoir pris un coup d'œil à la réponse de rudi-moore Je pensais avec gdb enquêter sur une fois de plus ...

Il tourne dans mon dumpShdrInfo j'imprimais sh_offset au lieu de sh_addr. J'ai vives souvenirs de l'écriture qui fonctionnent et en tapant "sh_addr", ainsi que le débogage avec gdb et en voyant sh_offset étant égal à sh_addr.

Cependant, je suppose que je suis un idiot et mes souvenirs ne valent pas autant parce que dès que je l'ai changé pour sh_addr et recompilé cela a fonctionné. C'est ce que je reçois pour la programmation à 5h du matin. :/

+1

Oui que 5 AM-code, je peux bien comprendre il;) Bien que cela fonctionne maintenant. –

+0

Peut-être que vous regardiez une bibliothèque partagée et non le programme principal.Les bibliothèques partagées ont généralement une adresse de chargement de 0, ce qui signifie qu'elles peuvent être chargées à n'importe quelle adresse, donc les offsets et les adresses virtuelles (relatives) sont généralement identiques (pour le segment de texte, elles sont généralement décalées d'une page). –