When tinkering with ld.so crashes due to file corruption the other day
I tested a few changes but did not want to replace /usr/libexec/ld.so
and since recompiling executable to change their interpreter is not
always an option, I went for https://github.com/NixOS/patchelf which
allows me to manipulate executables in place.
The tool works just fine and as a byproduct rearanges program headers;
that in itself is fine except our run-time link-editor is not happy with
such valid executables:
ELF mandates nothing but the file header be at a fixed location, hence
ld.so(1) must not assume any specific order for headers, segments, etc.
Looping over the program header table to parse segment headers,
_dl_boot() creates the executable object upon DYNAMIC and expects it to
be set upon GNU_RELRO, resulting in a NULL dereference iff that order is
reversed.
Store relocation bits in temporary variables and update the executable
object once all segment headers are parsed to lift this dependency.
Under __mips__ _dl_boot() later on uses the same temporary variable, so
move nothing but the declaration out of MI code so as to not alter the
MD code's logic/behaviour.
This fix is needed for the following work on OpenBSD:
$ patchelf --set-interpreter $PWD/obj/ld.so ./my-ldso-test
$ readelf -l ./my-ldso-test | grep interpreter
[Requesting program interpreter: /usr/src/libexec/ld.so/obj/ld.so]
$ ./my-ldso-test
it works!
amd64 and arm64 regress is happy and all my patched executables work
with this.
Feedback? Objections? OK?
diff d7231fb4fb547dd287a884c56ae7c8b10f9145fe
f023dbe355bef379d55eb93eddbb2702559d5bdb
blob - 18bd30af759bffbc4e3fbfee9ffc29906f0d1bb0
blob + b66dbb169aad9afffa1283d480ad9276aff9072a
--- libexec/ld.so/loader.c
+++ libexec/ld.so/loader.c
@@ -464,6 +464,7 @@ _dl_boot(const char **argv, char **envp, const long dy
int failed;
struct dep_node *n;
Elf_Addr minva, maxva, exe_loff, exec_end, cur_exec_end;
+ Elf_Addr relro_addr = 0, relro_size = 0;
Elf_Phdr *ptls = NULL;
int align;
@@ -552,8 +553,8 @@ _dl_boot(const char **argv, char **envp, const long dy
ptls = phdp;
break;
case PT_GNU_RELRO:
- exe_obj->relro_addr = phdp->p_vaddr + exe_loff;
- exe_obj->relro_size = phdp->p_memsz;
+ relro_addr = phdp->p_vaddr + exe_loff;
+ relro_size = phdp->p_memsz;
break;
}
phdp++;
@@ -561,6 +562,8 @@ _dl_boot(const char **argv, char **envp, const long dy
exe_obj->load_list = load_list;
exe_obj->obj_flags |= DF_1_GLOBAL;
exe_obj->load_size = maxva - minva;
+ exe_obj->relro_addr = relro_addr;
+ exe_obj->relro_size = relro_size;
_dl_set_sod(exe_obj->load_name, &exe_obj->sod);
#ifdef __i386__
@@ -638,7 +641,7 @@ _dl_boot(const char **argv, char **envp, const long dy
debug_map->r_ldbase = dyn_loff;
_dl_debug_map = debug_map;
#ifdef __mips__
- Elf_Addr relro_addr = exe_obj->relro_addr;
+ relro_addr = exe_obj->relro_addr;
if (dynp->d_tag == DT_DEBUG &&
((Elf_Addr)map_link + sizeof(*map_link) <= relro_addr ||
(Elf_Addr)map_link >= relro_addr + exe_obj->relro_size)) {