Reputation: 87
I have been trying to obtain the address of sections through a program by using the libelf of elfutils package. The excerpt from my program is :
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);
}
But, when I invoke this program with /lib64/libc.so.6 or other libpthread.so, etc, I get this as the output:
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
When I use readelf to find out the addresses of these sections, I find a difference in the addresses :
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
The addresses are different. Say for e.g., .note.gnu.build-id, addr = 0x47a00270 in case of my output and has the address 3b47a00270 in case of readelf. I am using fedora 18 and my m/c is 64 bit. I don't understand the reason. Can anybody explain me the reason. Thanks in advance.
Upvotes: 2
Views: 514
Reputation: 94859
You're only printing out a 32bit value when you cast to unsigned like that and you're running on a 64bit binary - try %p
for the format string of the address - it should allow you to print a pointer properly.
Although, now that I write that, I'm not totally au-fait with the generic elf libraries, so if you needed to support both a 32bit and 64bit code, then you would need to conditionally print out these values based on the bit-mode of the elf code i.e. if it's 32bit code then print 32bit addresses, if it's 64bit code then print 64bit addresses.
To support only 64bit code, and assuming you're building your code in 64bit mode, you need to change the printing code to do:
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);
}
If you're running as a 32-bit program you would need to explicitly cast to an uint64_t
and print using the PRIx64
from inttypes.h
:
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
Upvotes: 2