2

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 ビットです。理由がわかりません。誰か理由を説明してくれませんか。前もって感謝します。

4

1 に答える 1

2

そのように符号なしにキャストし、64ビットバイナリで実行している場合、32ビット値のみを出力してい%pます-アドレスのフォーマット文字列を試してください-ポインターを適切に出力できるはずです。

とはいえ、これを書いた今、私は一般的なエルフ ライブラリに完全に賛成しているわけではないので、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 ビット プログラムとして実行している場合は、 fromuint64_tを使用して明示的にキャストし、出力する必要があります。PRIx64inttypes.h

    printf("name = %s\n, addr = 0x" PRIx64 "\n", name, (uint64_t)shdr.sh_addr);

私たちの間の怠け者は、キャストして次のように出力unsigned long longします%llx

于 2013-09-24T12:27:38.120 に答える