On Mon, May 24, 2021 at 4:59 AM Klemens Nanni <[email protected]> wrote:
> 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. > (Not quite: it does place ordering requirements in some specific cases, such as that PT_INTERP and PT_PHDR, if present, must appear before any PT_LOAD segments in the program header, and that PT_LOAD segments must appear in p_vaddr order.) 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)) { > > ok guenther@ (This still assumes PT_GNU_RELRO comes after PT_PHDR, for exe_loff, but I suspect even patchelf wouldn't screw with that.)
