为什么 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.foobar
是 PT_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
不处理(或关心)部分。它遍历 段 ,并将段分配给段由链接器按照他们认为合适的方式执行。
在 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.foobar
是 PT_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
不处理(或关心)部分。它遍历 段 ,并将段分配给段由链接器按照他们认为合适的方式执行。