【问题标题】:Is sh_addr always equals sh_offset in the ELF file format?sh_addr 是否总是等于 ELF 文件格式中的 sh_offset?
【发布时间】:2010-06-22 08:14:50
【问题描述】:

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

话虽如此,有没有办法实际确定节的虚拟地址?

【问题讨论】:

    标签: c elf


    【解决方案1】:

    我试过了,但 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}
    

    【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

    • 是的,早上 5 点的代码,我可以很好地理解它;)很好,它现在可以工作了。
    • 也许您正在查看共享库而不是主程序。共享库的加载地址通常为 0,这意味着它们可以在任何地址加载,因此文件偏移量和(相对)虚拟地址通常是相同的(对于文本段;它​​们通常是一页一页的数据)。
    猜你喜欢
    • 1970-01-01
    • 2021-09-04
    • 2016-03-17
    • 2014-12-31
    • 2021-12-17
    • 2011-03-07
    • 2010-09-28
    • 1970-01-01
    相关资源
    最近更新 更多