为什么 gold 链接器导致 dl_iterate_phdr() 不 return 我的自定义注释部分?

Why does the gold linker cause dl_iterate_phdr() not to return my custom note section?

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

我编译并 link 下面的程序一次使用 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;
}

此代码:

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

假设您的 .note.foobarPT_NOTE 段中的第一个 Elf...Note,但您不能做出该假设 - [=14= 中的注释顺序] 不保证;你需要遍历所有这些。

您可以通过 readelf -n test-{ld,gold}.

验证是否存在多个注释

似乎 GNU-ld 为每个 .note* 部分发出了一个单独的 PT_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

P.S.

Why does the gold linker cause dl_iterate_phdr() not to return my custom note section?

直接的答案是 dl_iterate_phdr 不处理(或关心)部分。它遍历 ,并将段分配给段由链接器按照他们认为合适的方式执行。