2

在 Linux 上,我想将一些结构存储在自定义.note.foobar部分中,并在运行时发现它们。

我编译和链接下面的程序一次有gold一次没有:

$ gcc -o test-ld test.c
$ gcc -o test-gold -fuse-ld=gold test.c

您可以看到ld-linked 版本找到了该部分,而gold-linked 版本没有:

$ ./test-ld
note section at vaddr: 2c4
note section at vaddr: 2f0
found f00dface
note section at vaddr: 324
note section at vaddr: 7a8
note section at vaddr: 270
note section at vaddr: 1c8
$ ./test-gold
note section at vaddr: 254
note section at vaddr: 7a8
note section at vaddr: 270
note section at vaddr: 1c8

但是,该部分确实存在于两个二进制文件中:

$ readelf -x .note.foobar test-ld

Hex dump of section '.note.foobar':
  0x000002f0 04000000 14000000 67452301 666f6f00 ........gE#.foo.
  0x00000300 cefa0df0 00000000 00000000 00000000 ................
  0x00000310 04000000 14000000 67452301 666f6f00 ........gE#.foo.
  0x00000320 efbeadde                            ....

$ readelf -x .note.foobar test-gold 

Hex dump of section '.note.foobar':
  0x00000280 04000000 14000000 67452301 666f6f00 ........gE#.foo.
  0x00000290 cefa0df0 00000000 00000000 00000000 ................
  0x000002a0 04000000 14000000 67452301 666f6f00 ........gE#.foo.
  0x000002b0 efbeadde                            ....

因此,您可能希望test-gold程序报告 vaddr 280 处的部分,但事实并非如此。

为什么dl_iterate_phdr找不到此部分,而readelf可以,以及是gold什么不同的原因导致此?

#define _GNU_SOURCE
#include <link.h>
#include <stdlib.h>
#include <stdio.h>

typedef struct {
  unsigned int elf_namesize;
  unsigned int elf_datasize;
  unsigned int elf_type;
  unsigned int elf_name;
  unsigned int bar;
} foo_t;

const foo_t __attribute__((used,section(".note.foobar,\"a\"#"))) foo1 = {
  4,
  20,
  0x01234567,
  0x6f6f66,
  0xf00dface,
};
const foo_t __attribute__((used,section(".note.foobar,\"a\"#"))) foo2 = {
  4,
  20,
  0x01234567,
  0x6f6f66,
  0xdeadbeef,
};

static int
callback(struct dl_phdr_info *info, size_t size, void *data)
{
  for (int i = 0; i < info->dlpi_phnum; i++) {
    const ElfW(Phdr)* phdr = &info->dlpi_phdr[i];
    if (phdr->p_type == PT_NOTE) {
      foo_t *payload = (foo_t*)(info->dlpi_addr + phdr->p_vaddr);
      printf("note section at vaddr: %lx\n", phdr->p_vaddr);
      if (phdr->p_memsz >= sizeof(foo_t) && payload->elf_type == 0x01234567 && payload->elf_name == 0x6f6f66) {
        printf("found %x\n", payload->bar);
      }
    }
  }

  return 0;
}

int
main(int argc, char *argv[])
{
  dl_iterate_phdr(callback, NULL);
  return 0;
}
4

1 回答 1

2

这段代码:

foo_t *payload = (foo_t*)(info->dlpi_addr + phdr->p_vaddr);

假设您.note.foobarElf...NotePT_NOTE片段中的第一个,但您不能做出这样的假设——音符的顺序PT_NOTE无法保证;您需要遍历所有这些。

您可以使用 验证是否有多个注释readelf -n test-{ld,gold}

似乎 GNU-ldPT_NOTE为每个部分发出一个单独的.note*部分,而 Gold 将它们全部合并为一个PT_NOTE部分。就 ELF 标准而言,这两种行为都非常好,尽管 GNU-ld 很浪费(不需要发出额外的PT_NOTE程序头文件)。

这是我为您的测试程序得到的:

readelf -l test-ld | grep NOTE
  NOTE           0x00000000000002c4 0x00000000004002c4 0x00000000004002c4
  NOTE           0x00000000000002f0 0x00000000004002f0 0x00000000004002f0
  NOTE           0x0000000000000324 0x0000000000400324 0x0000000000400324

readelf -l test-gold | grep NOTE
  NOTE           0x0000000000000254 0x0000000000400254 0x0000000000400254

附言

为什么黄金链接器会导致 dl_iterate_phdr() 不返回我的自定义注释部分?

直接的答案是dl_iterate_phdr不处理(或关心)部分。它对进行迭代,并且将段分配给段以供链接器按照他们认为合适的方式执行。

于 2018-01-06T16:16:06.390 回答