On Linux, I would like to store some structures in a custom .note.foobar
section and discover them at runtime.
I compile and link the program below once with gold
and once without:
$ gcc -o test-ld test.c
$ gcc -o test-gold -fuse-ld=gold test.c
You can see that the ld
-linked version finds the section while the gold
-linked version does not:
$ ./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
However, the section does exist in both binaries:
$ 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 ....
So you would expect the test-gold
program to report a section at vaddr 280, but it does not.
Why can dl_iterate_phdr
not find this section, while readelf
can, and what is gold
doing differently to cause this?
#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;
}
This code:
foo_t *payload = (foo_t*)(info->dlpi_addr + phdr->p_vaddr);
assumes that your .note.foobar
is the very first Elf...Note
in the PT_NOTE
segment, but you can't make that assumption -- the order of notes in PT_NOTE
is not guaranteed; you need to iterate over all of them.
You can verify that there are multiple notes with readelf -n test-{ld,gold}
.
It appears that GNU-ld emits a separate PT_NOTE
for each .note*
section, while Gold merges them all into a single PT_NOTE
segment. Either behavior is perfectly fine as far as ELF standard is concerned, though GNU-ld is wasteful (there is no need to emit extra PT_NOTE
program headers).
Here is what I get for your test program:
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?
The direct answer is that dl_iterate_phdr
doesn't deal with (or care) about sections. It iterates over segments, and assignment of sections to segments is up for linkers to perform as they see fit.
User contributions licensed under CC BY-SA 3.0