sh_addr总是等于ELF文件格式的sh_offset吗?

时间:2010-06-22 08:14:50

标签: c elf

最近(不再是学校)我一直在教自己关于ELF文件格式。我在很大程度上遵循了这里的文档:http://www.skyfree.org/linux/references/ELF_Format.pdf

一切都很好,我写了这个程序给我一个关于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;
}

在几个文件上运行几次之后我发现了一些奇怪的东西。 '.text'部分始终以非常低的虚拟地址开始(我们说话的时间小于1000h)。在使用gdb挖掘一段时间之后,我注意到对于每个部分,sh_addr等于sh_offset。

这是我很困惑的事情 - Elf32_Shdr.sh_addr被记录为“第一个字节应该驻留的地址”,而Elf32_Shdr.sh_offset被记录为“从文件开头到文件的字节偏移”函数中的第一个字节“。如果这两种情况都是如此,对我来说,他们都是平等的并不合理。这是为什么?

现在,我知道有些部分包含未初始化的数据(我认为是.bss),所以有意义的是,数据不会出现在文件中但会出现在进程的内存中。这意味着对于上述部分之后的每个部分,找出它的虚拟地址将比简单的变量复杂得多。

话虽如此,是否有办法实际确定某个部分的虚拟地址?

2 个答案:

答案 0 :(得分:3)

我试过了,在我的例子中,Elf32_Shdr.sh_addr与Elf32_Shdr.sh_offset不同。它被移位0x08040000,这是内存中程序的虚拟起始地址。 对于'.text'部分,Elf32_Shdr.sh_offset为0x00000570,对于同一部分,Elf32_Shdr.sh_addr为0x08048570。

与文档中引用的一样,Elf32_Shdr.sh_offset是“从文件开头到函数中第一个字节的字节偏移量”:

$> 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..|

和Elf32_Shdr.sh_addr是“第一个字节应该驻留的地址”。这是内存中数据的虚拟地址:

(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}

答案 1 :(得分:0)

好的,看了一下rudi-moore的回答,我以为我会再次与gdb一起调查......

事实证明,在我的dumpShdrInfo中,我正在打印sh_offset而不是sh_addr。我有生动回忆写这个函数并键入“sh_addr”,以及用gdb调试并看到sh_offset等于sh_addr。

然而,我想我是个白痴,我的记忆不值那么多,因为只要我把它改成sh_addr并重新编译就行了。这就是我在凌晨5点编程的结果。 :/