代码之家  ›  专栏  ›  技术社区  ›  Nick Rolfe

为什么黄金链接器会导致dl\u iterate\u phdr()不返回我的自定义便笺部分?

  •  2
  • Nick Rolfe  · 技术社区  · 7 年前

    在Linux上,我想在自定义 .note.foobar 节并在运行时发现它们。

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

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

    你可以看到 ld -链接版本查找节,而 -链接版本不:

    $ ./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 可以,什么是 以不同的方式导致这种情况?

    #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;
    }
    
    1 回复  |  直到 7 年前
        1
  •  2
  •   Employed Russian    7 年前

    此代码:

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

    假设您的 .note.foobar 是第一个 Elf...Note PT_NOTE 段,但你不能这样假设 PT\U注释 不保证;您需要对所有这些进行迭代。

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

    GNU ld似乎发出一个单独的 PT\U注释 对于每个 .note* 部分,而Gold将它们合并为一个 PT\U注释 段就ELF标准而言,这两种行为都是完美的,尽管GNU ld是浪费的(没有 需要 发出额外的 PT\U注释 程序标题)。

    以下是我为您的测试计划获得的信息:

    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\u iterate\u phdr()不返回我的自定义便笺部分?

    直接的答案是 dl_iterate_phdr 不处理(或关心)部分。它迭代 部分 ,并且将节分配给段是由链接器根据自己的需要执行的。