4

最近(不再上学了)我一直在自学 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),因此该数据不会出现在文件中但会出现在进程的内存中是有道理的。这意味着对于前面提到的每个部分,找出它的虚拟地址将比一个简单的变量复杂得多。

话虽这么说,有没有办法实际确定一个部分的虚拟地址?

4

2 回答 2

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}
于 2010-06-22T09:56:53.390 回答
0

好的,在看了 rudi-moore 的回答之后,我想我会再用 gdb 进行一次调查......

结果在我的 dumpShdrInfo 中我打印的是 sh_offset 而不是 sh_addr。我对编写该函数并输入“sh_addr”以及使用 gdb 进行调试并看到 sh_offset 等于 sh_addr 有着生动的记忆。

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

于 2010-06-23T04:24:30.470 回答