Hemant Kumar
Hemant Kumar

Reputation: 87

ELF: Confusion in obtaining the address of a section correctly using elfutils

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

Answers (1)

Anya Shenanigans
Anya Shenanigans

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

Related Questions