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