我一直在尝试使用 elfutils 包的 libelf 通过程序获取节的地址。我的程序摘录如下:
fd = open(argv[1], O_RDONLY);
if (fd < 0) {
printf("Error: file %s cannot be opened\n", argv[1]);
goto out_ret;
}
elf_version(EV_CURRENT);
elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
if (!elf){
printf("Error: elf_begin\n");
goto out_close;
}
if (gelf_getehdr(elf, &ehdr) == NULL) {
printf("Error : gelf_getehdr\n");
goto out_close;
}
if (elf_getshdrstrndx(elf, &shstrndx) != 0) {
printf("Error : elf_getshdrstrndx\n");
goto out_close;
}
while ((scn = elf_nextscn(elf, scn)) != NULL) {
gelf_getshdr(scn, &shdr);
name = elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name);
printf("name = %s\n, addr = 0x%x\n", name, (unsigned)shdr.sh_addr);
}
但是,当我使用/lib64/libc.so.6 或其他 libpthread.so 等调用此程序时,我得到以下输出:
name = .note.gnu.build-id, addr = 0x47a00270
name = .note.ABI-tag, addr = 0x47a00294
name = .gnu.hash, addr = 0x47a002b8
name = .dynsym, addr = 0x47a03cd8
name = .dynstr, addr = 0x47a10b48
name = .gnu.version, addr = 0x47a16338
name = .gnu.version_d, addr = 0x47a17470
name = .gnu.version_r, addr = 0x47a17758
name = .rela.dyn, addr = 0x47a17788
name = .rela.plt, addr = 0x47a1efd0
name = .plt, addr = 0x47a1f0e0
name = .text, addr = 0x47a1f1a0
name = __libc_freeres_fn, addr = 0x47b60960
name = __libc_thread_freeres_fn, addr = 0x47b62000
name = .rodata, addr = 0x47b62300
name = .stapsdt.base, addr = 0x47b7cc10
name = .interp, addr = 0x47b7cc20
name = .eh_frame_hdr, addr = 0x47b7cc3c
name = .eh_frame, addr = 0x47b83478
name = .gcc_except_table, addr = 0x47ba97fc
name = .hash, addr = 0x47ba9bc0
name = .tdata, addr = 0x47dad6f0
name = .tbss, addr = 0x47dad700
name = .init_array, addr = 0x47dad700
当我使用 readelf 查找这些部分的地址时,我发现地址有所不同:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .note.gnu.build-i NOTE 0000003b47a00270 00000270
0000000000000024 0000000000000000 A 0 0 4
[ 2] .note.ABI-tag NOTE 0000003b47a00294 00000294
0000000000000020 0000000000000000 A 0 0 4
[ 3] .gnu.hash GNU_HASH 0000003b47a002b8 000002b8
0000000000003a20 0000000000000000 A 4 0 8
[ 4] .dynsym DYNSYM 0000003b47a03cd8 00003cd8
000000000000ce70 0000000000000018 A 5 3 8
[ 5] .dynstr STRTAB 0000003b47a10b48 00010b48
00000000000057ef 0000000000000000 A 0 0 1
[ 6] .gnu.version VERSYM 0000003b47a16338 00016338
0000000000001134 0000000000000002 A 4 0 2
[ 7] .gnu.version_d VERDEF 0000003b47a17470 00017470
00000000000002e4 0000000000000000 A 5 21 8
[ 8] .gnu.version_r VERNEED 0000003b47a17758 00017758
0000000000000030 0000000000000000 A 5 1 8
[ 9] .rela.dyn RELA 0000003b47a17788 00017788
0000000000007848 0000000000000018 A 4 0 8
[10] .rela.plt RELA 0000003b47a1efd0 0001efd0
0000000000000108 0000000000000018 A 4 11 8
[11] .plt PROGBITS 0000003b47a1f0e0 0001f0e0
00000000000000c0 0000000000000010 AX 0 0 16
地址不同。例如,在我的输出中,.note.gnu.build-id,addr = 0x47a00270,在 readelf 中,地址为 3b47a00270。我使用的是 Fedora 18,我的 m/c 是 64 位。我不明白原因。谁能给我解释一下原因。提前致谢。
最佳答案
当您像这样转换为无符号并且运行在 64 位二进制文件上时,您只会打印出 32 位值 - 尝试 %p
作为地址的格式字符串 - 它应该允许您可以正确打印指针。
虽然,既然我写了这篇文章,我对通用 elf 库并不完全熟悉,所以如果您需要同时支持 32 位和 64 位代码,那么您需要根据条件有条件地打印出这些值elf 代码的位模式,即如果是 32 位代码,则打印 32 位地址,如果是 64 位代码,则打印 64 位地址。
要仅支持 64 位代码,并假设您在 64 位模式下构建代码,您需要更改打印代码以执行以下操作:
while ((scn = elf_nextscn(elf, scn)) != NULL) {
gelf_getshdr(scn, &shdr);
name = elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name);
printf("name = %s\n, addr = 0x%p\n", name, (void *)shdr.sh_addr);
}
如果您作为 32 位程序运行,则需要显式转换为 uint64_t
并使用 inttypes.h< 中的
:PRIx64
进行打印
printf("name = %s\n, addr = 0x" PRIx64 "\n", name, (uint64_t)shdr.sh_addr);
The lazy amongst us would cast to
unsigned long long
and print it as%llx
关于c - ELF:使用 elfutils 正确获取节地址时出现困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18981475/