Re: [PATCH] elf: Support multiple PT_GNU_RELRO segments
Fangrui Song <[email protected]>
| Newsgroups | gmane.comp.lib.glibc.alpha |
|---|---|
| Message-ID | <[email protected]> |
With linker scripts' PHDRS, we can create multiple PT_GNU_RELRO segments, working with both GNU ld and ld.lld.
I have recently added support for multiple PT_GNU_RELRO segments without PHDRS
https://github.com/llvm/llvm-project/pull/203675
// a.c
#define _GNU_SOURCE
#include <link.h>
#include <stdio.h>
const char *const greeting = "Hello, world!";
int counter = 42;
static char perm_at(unsigned long addr) {
FILE *f = fopen("/proc/self/maps", "r");
char line[128], w = '?';
unsigned long beg, end;
char perms[8];
while (f && fgets(line, sizeof line, f))
if (sscanf(line, "%lx-%lx %7s", &beg, &end, perms) == 3 && beg <= addr &&
addr < end) {
w = perms[1];
break;
}
if (f)
fclose(f);
return w;
}
static int report(struct dl_phdr_info *info, size_t size, void *data) {
if (info->dlpi_name[0] != '\0')
return 0;
for (unsigned i = 0; i < info->dlpi_phnum; i++) {
const ElfW(Phdr) *ph = &info->dlpi_phdr[i];
if (ph->p_type != PT_GNU_RELRO)
continue;
unsigned long beg = info->dlpi_addr + ph->p_vaddr;
printf("PT_GNU_RELRO [0x%lx, 0x%lx): %s\n", beg, beg + ph->p_memsz,
perm_at(beg) == 'w' ? "still writable (ld.so did not protect it)"
: "read-only");
}
return 0;
}
int main(void) {
counter++;
printf("%s (counter=%d, greeting is in .data.rel.ro)\n", greeting, counter);
dl_iterate_phdr(report, NULL);
return 0;
}
## relro.lds
SECTIONS {
. = SIZEOF_HEADERS;
.interp : { *(.interp) }
.rodata : { *(.rodata .rodata.*) }
.eh_frame_hdr : { *(.eh_frame_hdr) }
.eh_frame : { *(.eh_frame) }
. = ALIGN(CONSTANT(MAXPAGESIZE));
.init : { *(.init) }
.text : { *(.text .text.*) }
.fini : { *(.fini) }
.plt : { *(.plt) }
/* relro run 1, padded to a full page so that a loader honoring it can
mprotect whole pages */
. = ALIGN(CONSTANT(MAXPAGESIZE));
.init_array : { *(.init_array) }
.fini_array : { *(.fini_array) }
.dynamic : { *(.dynamic) }
.got : { *(.got) }
.got.plt : { *(.got.plt) . = ALIGN(CONSTANT(MAXPAGESIZE)); }
/* non-relro: splits PT_GNU_RELRO in two */
.data : { *(.data) }
/* relro run 2, page-aligned and page-padded (see above) */
. = ALIGN(CONSTANT(MAXPAGESIZE));
.data.rel.ro : { *(.data.rel.ro .data.rel.ro.*) . = ALIGN(CONSTANT(MAXPAGESIZE)); }
.bss : { *(.bss) *(COMMON) }
}
## build.sh
clang -O1 -g -fPIE -c hello.c -o hello.o
clang -pie --ld-path=/tmp/Rel/bin/ld.lld -Wl,-z,now -Wl,-T,relro.lds hello.o -o hello
readelf -lW hello | grep -E 'Type|LOAD|GNU_RELRO'
./hello
## phdrs.lds
PHDRS {
hdrs PT_PHDR PHDRS FLAGS (4);
interp PT_INTERP FLAGS (4);
ro PT_LOAD FILEHDR PHDRS FLAGS (4);
text PT_LOAD FLAGS (5);
rw PT_LOAD FLAGS (6);
dyn PT_DYNAMIC FLAGS (6);
relro1 PT_GNU_RELRO FLAGS (4);
relro2 PT_GNU_RELRO FLAGS (4);
eh PT_GNU_EH_FRAME FLAGS (4);
stack PT_GNU_STACK FLAGS (6);
}
SECTIONS {
. = SIZEOF_HEADERS;
.interp : { *(.interp) } :ro :interp
.note : { *(.note*) } :ro
.dynsym : { *(.dynsym) } :ro
.gnu.version : { *(.gnu.version) } :ro
.gnu.version_r : { *(.gnu.version_r) } :ro
.gnu.hash : { *(.gnu.hash) } :ro
.dynstr : { *(.dynstr) } :ro
.rela.dyn : { *(.rela.dyn) } :ro
.rela.plt : { *(.rela.plt) } :ro
.rodata : { *(.rodata .rodata.*) } :ro
.eh_frame_hdr : { *(.eh_frame_hdr) } :ro :eh
.eh_frame : { *(.eh_frame) } :ro
. = ALIGN(CONSTANT(MAXPAGESIZE));
.init : { *(.init) } :text
.text : { *(.text .text.*) } :text
.fini : { *(.fini) } :text
.plt : { *(.plt) } :text
/* relro run 1, page-padded so that a loader honoring it can mprotect
whole pages */
. = ALIGN(CONSTANT(MAXPAGESIZE));
.init_array : { *(.init_array) } :rw :relro1
.fini_array : { *(.fini_array) } :rw :relro1
.dynamic : { *(.dynamic) } :rw :dyn :relro1
.got : { *(.got) } :rw :relro1
.got.plt : { *(.got.plt) . = ALIGN(CONSTANT(MAXPAGESIZE)); } :rw :relro1
/* non-relro: splits PT_GNU_RELRO in two */
.data : { *(.data) } :rw
.data.rel.local : { *(.data.rel.local) } :rw
/* relro run 2, page-aligned and page-padded (see above) */
. = ALIGN(CONSTANT(MAXPAGESIZE));
.data.rel.ro : { *(.data.rel.ro .data.rel.ro.*) . = ALIGN(CONSTANT(MAXPAGESIZE)); } :rw :relro2
.bss : { *(.bss) *(COMMON) } :rw
}