From a0b392ee1010bf340374950b5c18afc9da214af6 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Wed, 30 Oct 2024 16:14:44 -0400 Subject: [PATCH 01/13] rtld: Remove code to track start and end offsets of text and rodata This was used in CHERI-MIPS to enforce stricter PCC bounds for some ABIs but is no longer used. --- libexec/rtld-elf/map_object.c | 27 ----------------------- libexec/rtld-elf/rtld.c | 41 +---------------------------------- libexec/rtld-elf/rtld.h | 4 +--- 3 files changed, 2 insertions(+), 70 deletions(-) diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c index 3865d872c374..627969d2ca70 100644 --- a/libexec/rtld-elf/map_object.c +++ b/libexec/rtld-elf/map_object.c @@ -118,10 +118,6 @@ map_object(int fd, const char *path, const struct stat *sb, const char* main_pat char *note_map; size_t note_map_len; Elf_Addr text_end; -#ifdef __CHERI_PURE_CAPABILITY__ - Elf_Addr text_rodata_start_offset = 0; - Elf_Addr text_rodata_end_offset = 0; -#endif hdr = get_elf_header(fd, path, sb, main_path, &phdr); if (hdr == NULL) @@ -161,16 +157,6 @@ map_object(int fd, const char *path, const struct stat *sb, const char* main_pat path, nsegs); goto error; } -#ifdef __CHERI_PURE_CAPABILITY__ - if (!(segs[nsegs]->p_flags & PF_W)) { - Elf_Addr start_addr = segs[nsegs]->p_vaddr; - text_rodata_start_offset = rtld_min(start_addr, text_rodata_start_offset); - text_rodata_end_offset = rtld_max(start_addr + segs[nsegs]->p_memsz, text_rodata_end_offset); - dbg("%s: processing readonly PT_LOAD[%d], new text/rodata start " - " = %zx text/rodata end = %zx", path, nsegs, - (size_t)text_rodata_start_offset, (size_t)text_rodata_end_offset); - } -#endif if ((segs[nsegs]->p_flags & PF_X) == PF_X) { text_end = MAX(text_end, rtld_round_page(segs[nsegs]->p_vaddr + @@ -205,13 +191,6 @@ map_object(int fd, const char *path, const struct stat *sb, const char* main_pat case PT_GNU_RELRO: relro_page = phdr->p_vaddr; relro_size = phdr->p_memsz; -#ifdef __CHERI_PURE_CAPABILITY__ - text_rodata_start_offset = rtld_min(phdr->p_vaddr, text_rodata_start_offset); - text_rodata_end_offset = rtld_max(phdr->p_vaddr + phdr->p_memsz, text_rodata_end_offset); - dbg("%s: Adding PT_GNU_RELRO, new text/rodata start " - " = %zx text/rodata end = %zx", path, - (size_t)text_rodata_start_offset, (size_t)text_rodata_end_offset); -#endif break; case PT_NOTE: @@ -379,12 +358,6 @@ map_object(int fd, const char *path, const struct stat *sb, const char* main_pat rtld_fdprintf(STDERR_FILENO, "%s: nonzero vaddrbase %zd may be broken " "for CheriABI", path, obj->vaddrbase); } - obj->text_rodata_start_offset = text_rodata_start_offset; - obj->text_rodata_end_offset = text_rodata_end_offset; - /* - * Note: no csetbounds yet since we also need to include .cap_table (which - * is part of the r/w section). Bounds are set after .dynamic is read. - */ obj->text_rodata_cap = obj->relocbase; fix_obj_mapping_cap_permissions(obj, path); #endif diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 918dcb785113..561b592be25b 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -1924,16 +1924,6 @@ digest_phdr(const Elf_Phdr *phdr, int phnum, dlfunc_t entry, const char *path) rtld_round_page(ph->p_vaddr + ph->p_memsz) - obj->vaddrbase); } nsegs++; -#ifdef __CHERI_PURE_CAPABILITY__ - if (!(ph->p_flags & PF_W)) { - Elf_Addr start_addr = ph->p_vaddr; - obj->text_rodata_start_offset = rtld_min(start_addr, obj->text_rodata_start_offset); - obj->text_rodata_end_offset = rtld_max(start_addr + ph->p_memsz, obj->text_rodata_end_offset); - dbg("%s: processing readonly PT_LOAD[%d], new text/rodata start " - " = %zx text/rodata end = %zx", path, nsegs, - (size_t)obj->text_rodata_start_offset, (size_t)obj->text_rodata_end_offset); - } -#endif break; case PT_DYNAMIC: @@ -1965,13 +1955,6 @@ digest_phdr(const Elf_Phdr *phdr, int phnum, dlfunc_t entry, const char *path) obj->relro_page = obj->relocbase + rtld_trunc_page(ph->p_vaddr); obj->relro_size = rtld_trunc_page(ph->p_vaddr + ph->p_memsz) - rtld_trunc_page(ph->p_vaddr); -#ifdef __CHERI_PURE_CAPABILITY__ - obj->text_rodata_start_offset = rtld_min(ph->p_vaddr, obj->text_rodata_start_offset); - obj->text_rodata_end_offset = rtld_max(ph->p_vaddr + ph->p_memsz, obj->text_rodata_end_offset); - dbg("%s: Adding PT_GNU_RELRO, new text/rodata start " - " = %zx text/rodata end = %zx", path, - (size_t)obj->text_rodata_start_offset, (size_t)obj->text_rodata_end_offset); -#endif break; case PT_NOTE: @@ -1995,7 +1978,7 @@ digest_phdr(const Elf_Phdr *phdr, int phnum, dlfunc_t entry, const char *path) } /* * Derive text_rodata cap from AT_ENTRY (but set the address to the beginning - * of the object). Note: csetbounds is done after parsing .dynamic + * of the object). */ obj->text_rodata_cap = (const char *)cheri_copyaddress(entry, obj->relocbase); fix_obj_mapping_cap_permissions(obj, path); @@ -2753,28 +2736,6 @@ init_rtld(caddr_t mapbase, Elf_Auxinfo **aux_info) #endif #ifdef __CHERI_PURE_CAPABILITY__ - /* find the end of rodata/text: */ - - for (int i = 0; i < ehdr->e_phnum; i++) { - const Elf_Phdr *ph = &objtmp.phdr[i]; - if (ph->p_type != PT_LOAD) - continue; - if (!(ph->p_flags & PF_W)) { - Elf_Addr start_addr = ph->p_vaddr; - objtmp.text_rodata_start_offset = rtld_min(start_addr, objtmp.text_rodata_start_offset); - objtmp.text_rodata_end_offset = rtld_max(start_addr + ph->p_memsz, objtmp.text_rodata_end_offset); -#if defined(DEBUG_VERBOSE) && DEBUG_VERBOSE > 3 - /* debug is not initialized yet so dbg() is a no-op */ - rtld_fdprintf(STDERR_FILENO, "rtld: processing PT_LOAD phdr[%d], " - "new text/rodata start = %zx text/rodata end = %zx\n", i + 1, - (size_t)objtmp.text_rodata_start_offset, (size_t)objtmp.text_rodata_end_offset); -#endif - } - } - /* - * Note: no csetbounds yet since we also need to include .cap_table (which - * is part of the r/w section). Bounds are set after .dynamic is read. - */ objtmp.text_rodata_cap = objtmp.relocbase; fix_obj_mapping_cap_permissions(&objtmp, "RTLD"); #endif diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index 0b49324ee898..b9e0019e7a8c 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -200,11 +200,9 @@ typedef struct Struct_Obj_Entry { /* * For CHERI we need a capability for the executable + rodata segments so * that we can derive code capabilities from it. - * By having these additional members we can remove execute permissions from + * By having this additional member we can remove execute permissions from * relocbase and mapbase. */ - Elf_Addr text_rodata_start_offset; - Elf_Addr text_rodata_end_offset; const char *text_rodata_cap; /* Capability for the executable mapping */ #endif caddr_t relocbase; /* Relocation constant = mapbase - vaddrbase */ From e91535ecec0fa0cfe3baae1f36dea91e589c1b99 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Fri, 6 Dec 2024 17:37:50 -0500 Subject: [PATCH 02/13] rtld: Support multiple PT_GNU_RELRO program headers Iterate over all the program headers in obj_remap_relro and remove the relro fields from Obj_Entry. Skip the call to obj_enforce_relro() in relocate_object() for the rtld object as well as the main program object. obj_enforce_relro() is called later when it safe to reference globals such as page_size. Reviewed by: kib Obtained from: CheriBSD Sponsored by: AFRL, DARPA Differential Revision: https://reviews.freebsd.org/D47884 (cherry picked from commit fda0403eb0839b29b0b271c69c5cb6bfc874a3b5) --- libexec/rtld-elf/map_object.c | 12 ---------- libexec/rtld-elf/rtld.c | 43 ++++++++++++++++++----------------- libexec/rtld-elf/rtld.h | 3 --- 3 files changed, 22 insertions(+), 36 deletions(-) diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c index 627969d2ca70..e353d8ed9d33 100644 --- a/libexec/rtld-elf/map_object.c +++ b/libexec/rtld-elf/map_object.c @@ -111,8 +111,6 @@ map_object(int fd, const char *path, const struct stat *sb, const char* main_pat #ifndef __CHERI_PURE_CAPABILITY__ Elf_Word stack_flags; #endif - Elf_Addr relro_page; - size_t relro_size; caddr_t note_start; caddr_t note_end; char *note_map; @@ -132,8 +130,6 @@ map_object(int fd, const char *path, const struct stat *sb, const char* main_pat nsegs = -1; phdyn = phinterp = phtls = NULL; phdr_vaddr = 0; - relro_page = 0; - relro_size = 0; note_start = 0; note_end = 0; note_map = NULL; @@ -188,11 +184,6 @@ map_object(int fd, const char *path, const struct stat *sb, const char* main_pat #endif break; - case PT_GNU_RELRO: - relro_page = phdr->p_vaddr; - relro_size = phdr->p_memsz; - break; - case PT_NOTE: if (phdr->p_offset > page_size || phdr->p_offset + phdr->p_filesz > page_size) { @@ -397,9 +388,6 @@ map_object(int fd, const char *path, const struct stat *sb, const char* main_pat #ifndef __CHERI_PURE_CAPABILITY__ obj->stack_flags = stack_flags; #endif - obj->relro_page = obj->relocbase + rtld_trunc_page(relro_page); - obj->relro_size = rtld_trunc_page(relro_page + relro_size) - - rtld_trunc_page(relro_page); if (note_start < note_end) digest_notes(obj, (const Elf_Note *)note_start, (const Elf_Note *)note_end); if (note_map != NULL) diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 561b592be25b..9a498c204960 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -1951,12 +1951,6 @@ digest_phdr(const Elf_Phdr *phdr, int phnum, dlfunc_t entry, const char *path) #endif break; - case PT_GNU_RELRO: - obj->relro_page = obj->relocbase + rtld_trunc_page(ph->p_vaddr); - obj->relro_size = rtld_trunc_page(ph->p_vaddr + ph->p_memsz) - - rtld_trunc_page(ph->p_vaddr); - break; - case PT_NOTE: note_start = obj->relocbase + ph->p_vaddr; note_end = note_start + ph->p_filesz; @@ -2671,11 +2665,6 @@ parse_rtld_phdr(Obj_Entry *obj) obj->stack_flags = ph->p_flags; #endif break; - case PT_GNU_RELRO: - obj->relro_page = obj->relocbase + - rtld_trunc_page(ph->p_vaddr); - obj->relro_size = rtld_round_page(ph->p_memsz); - break; case PT_NOTE: note_start = (Elf_Note *)((char *)obj->relocbase + ph->p_vaddr); note_end = (Elf_Note *)((char *)note_start + ph->p_filesz); @@ -3727,7 +3716,7 @@ relocate_object(Obj_Entry *obj, bool bind_now, Obj_Entry *rtldobj, lockstate) == -1) return (-1); - if (!obj->mainprog && obj_enforce_relro(obj) == -1) + if (obj != rtldobj && !obj->mainprog && obj_enforce_relro(obj) == -1) return (-1); /* @@ -6406,16 +6395,28 @@ _rtld_is_dlopened(void *arg) static int obj_remap_relro(Obj_Entry *obj, int prot) { - if (obj->relro_size == 0) - return (0); + const Elf_Phdr *ph; + caddr_t relro_page; + size_t relro_size; - dbg("Enforcing RELRO for %s (%p -> %p)", obj->path, obj->relro_page, - obj->relro_page + obj->relro_size); - if (obj->relro_size > 0 && mprotect(obj->relro_page, obj->relro_size, - prot) == -1) { - _rtld_error("%s: Cannot set relro protection to %#x: %s", - obj->path, prot, rtld_strerror(errno)); - return (-1); + for (ph = obj->phdr; (const char *)ph < (const char *)obj->phdr + + obj->phsize; ph++) { + switch (ph->p_type) { + case PT_GNU_RELRO: + relro_page = obj->relocbase + + rtld_trunc_page(ph->p_vaddr); + relro_size = + rtld_round_page(ph->p_vaddr + ph->p_memsz) - + rtld_trunc_page(ph->p_vaddr); + dbg("Enforcing RELRO for %s (%p -> %p)", obj->path, + relro_page, relro_page + relro_size); + if (mprotect(relro_page, relro_size, prot) == -1) { + _rtld_error("%s: Cannot set relro protection to %#x: %s", + obj->path, prot, rtld_strerror(errno)); + return (-1); + } + break; + } } return (0); } diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index b9e0019e7a8c..0da06de7b86b 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -224,9 +224,6 @@ typedef struct Struct_Obj_Entry { size_t tlsalign; /* Alignment of static TLS block */ size_t tlspoffset; /* p_offset of the static TLS block */ - caddr_t relro_page; - size_t relro_size; - /* Items from the dynamic section. */ uintptr_t *pltgot; /* PLT or GOT, depending on architecture */ const Elf_Rel *rel; /* Relocation entries */ From 7d6a2fdf3c4850de9e5cfea5b01f1041b42a1977 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Mon, 28 Oct 2024 15:26:41 -0400 Subject: [PATCH 03/13] rtld: Support multiple PLTs Add a new Plt_Entry structure that describes the data associated with a PLT including the ELF relocation table and PLT GOT. Count the number of PLTs and allocate an array of Plt_Entry objects in the Obj_Entry. Instead of storing the Obj_Entry pointer in GOTs, store the Plt_Entry pointer. The special wrinkles for PowerPC are a bit hackish here and would need some tweaks before upstreaming. --- libexec/rtld-elf/aarch64/reloc.c | 86 ++++++++++------ libexec/rtld-elf/aarch64/rtld_machdep.h | 2 + libexec/rtld-elf/aarch64/rtld_start.S | 2 +- libexec/rtld-elf/amd64/reloc.c | 81 +++++++++------ libexec/rtld-elf/amd64/rtld_machdep.h | 2 + libexec/rtld-elf/amd64/rtld_start.S | 8 +- libexec/rtld-elf/arm/reloc.c | 26 ++--- libexec/rtld-elf/arm/rtld_machdep.h | 2 + libexec/rtld-elf/arm/rtld_start.S | 2 +- libexec/rtld-elf/debug.c | 25 +++-- libexec/rtld-elf/i386/reloc.c | 78 +++++++++----- libexec/rtld-elf/i386/rtld_machdep.h | 2 + libexec/rtld-elf/i386/rtld_start.S | 6 +- libexec/rtld-elf/map_object.c | 2 + libexec/rtld-elf/powerpc/reloc.c | 97 +++++++++-------- libexec/rtld-elf/powerpc/rtld_machdep.h | 4 +- libexec/rtld-elf/powerpc64/reloc.c | 120 ++++++++++++++-------- libexec/rtld-elf/powerpc64/rtld_machdep.h | 2 + libexec/rtld-elf/powerpc64/rtld_start.S | 2 +- libexec/rtld-elf/riscv/reloc.c | 80 +++++++++------ libexec/rtld-elf/riscv/rtld_machdep.h | 2 + libexec/rtld-elf/riscv/rtld_start.S | 4 +- libexec/rtld-elf/rtld.c | 114 +++++++++++++++----- libexec/rtld-elf/rtld.h | 26 +++-- 24 files changed, 497 insertions(+), 278 deletions(-) diff --git a/libexec/rtld-elf/aarch64/reloc.c b/libexec/rtld-elf/aarch64/reloc.c index 7bbe8599eff7..924ac4fc6727 100644 --- a/libexec/rtld-elf/aarch64/reloc.c +++ b/libexec/rtld-elf/aarch64/reloc.c @@ -120,23 +120,23 @@ arch_digest_note(struct Struct_Obj_Entry *obj __unused, const Elf_Note *note) } void -init_pltgot(Obj_Entry *obj) +init_pltgot(Plt_Entry *plt) { - if (obj->pltgot != NULL) { + if (plt->pltgot != NULL) { #ifdef CHERI_LIB_C18N if (C18N_ENABLED) - obj->pltgot[1] = (uintptr_t)cheri_seal(obj, + plt->pltgot[1] = (uintptr_t)cheri_seal(plt, sealer_pltgot); else #endif - obj->pltgot[1] = (uintptr_t)obj; + plt->pltgot[1] = (uintptr_t)plt; #ifdef CHERI_LIB_C18N if (C18N_ENABLED) - obj->pltgot[2] = (uintptr_t)&_rtld_bind_start_c18n; + plt->pltgot[2] = (uintptr_t)&_rtld_bind_start_c18n; else #endif - obj->pltgot[2] = (uintptr_t)&_rtld_bind_start; + plt->pltgot[2] = (uintptr_t)&_rtld_bind_start; } } @@ -406,8 +406,9 @@ reloc_tlsdesc(const Obj_Entry *obj, const Elf_Rela *rela, * Process the PLT relocations. */ int -reloc_plt(Obj_Entry *obj, int flags, RtldLockState *lockstate) +reloc_plt(Plt_Entry *plt, int flags, RtldLockState *lockstate) { + Obj_Entry *obj = plt->obj; const Obj_Entry *defobj; const Elf_Rela *relalim; const Elf_Rela *rela; @@ -417,13 +418,12 @@ reloc_plt(Obj_Entry *obj, int flags, RtldLockState *lockstate) #endif bool lazy; - relalim = (const Elf_Rela *)((const char *)obj->pltrela + - obj->pltrelasize); + relalim = (const Elf_Rela *)((const char *)plt->rela + plt->relasize); #ifdef __CHERI_PURE_CAPABILITY__ jump_slot_base = (uintptr_t)cheri_clearperm(obj->text_rodata_cap, FUNC_PTR_REMOVE_PERMS); #endif - for (rela = obj->pltrela; rela < relalim; rela++) { + for (rela = plt->rela; rela < relalim; rela++) { uintptr_t *where, target; #ifdef __CHERI_PURE_CAPABILITY__ Elf_Addr *fragment; @@ -541,19 +541,19 @@ reloc_plt(Obj_Entry *obj, int flags, RtldLockState *lockstate) * LD_BIND_NOW was set - force relocation for all jump slots */ int -reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) +reloc_jmpslots(Plt_Entry *plt, int flags, RtldLockState *lockstate) { + Obj_Entry *obj = plt->obj; const Obj_Entry *defobj; const Elf_Rela *relalim; const Elf_Rela *rela; const Elf_Sym *def; - if (obj->jmpslots_done) + if (plt->jmpslots_done) return (0); - relalim = (const Elf_Rela *)((const char *)obj->pltrela + - obj->pltrelasize); - for (rela = obj->pltrela; rela < relalim; rela++) { + relalim = (const Elf_Rela *)((const char *)plt->rela + plt->relasize); + for (rela = plt->rela; rela < relalim; rela++) { uintptr_t *where, target; where = (uintptr_t *)(obj->relocbase + rela->r_offset); @@ -585,7 +585,7 @@ reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) break; } } - obj->jmpslots_done = true; + plt->jmpslots_done = true; return (0); } @@ -647,28 +647,36 @@ reloc_iresolve_one(Obj_Entry *obj, const Elf_Rela *rela, *where = target; } -int -reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) +static void +reloc_iresolve_plt(Plt_Entry *plt, struct Struct_RtldLockState *lockstate) { const Elf_Rela *relalim; const Elf_Rela *rela; - if (!obj->irelative) - return (0); - obj->irelative = false; - relalim = (const Elf_Rela *)((const char *)obj->pltrela + - obj->pltrelasize); - for (rela = obj->pltrela; rela < relalim; rela++) { + relalim = (const Elf_Rela *)((const char *)plt->rela + plt->relasize); + for (rela = plt->rela; rela < relalim; rela++) { switch (ELF_R_TYPE(rela->r_info)) { #ifdef __CHERI_PURE_CAPABILITY__ case R_MORELLO_IRELATIVE: #else case R_AARCH64_IRELATIVE: #endif - reloc_iresolve_one(obj, rela, lockstate); + reloc_iresolve_one(plt->obj, rela, lockstate); break; } } +} + +int +reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) +{ + unsigned long i; + + if (!obj->irelative) + return (0); + obj->irelative = false; + for (i = 0; i < obj->nplts; i++) + reloc_iresolve_plt(&obj->plts[i], lockstate); return (0); } @@ -696,20 +704,18 @@ reloc_iresolve_nonplt(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) return (0); } -int -reloc_gnu_ifunc(Obj_Entry *obj, int flags, - struct Struct_RtldLockState *lockstate) +static bool +reloc_gnu_ifunc_plt(Plt_Entry *plt, int flags, RtldLockState *lockstate) { + Obj_Entry *obj = plt->obj; const Elf_Rela *relalim; const Elf_Rela *rela; uintptr_t *where, target; const Elf_Sym *def; const Obj_Entry *defobj; - if (!obj->gnu_ifunc) - return (0); - relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize); - for (rela = obj->pltrela; rela < relalim; rela++) { + relalim = (const Elf_Rela *)((const char *)plt->rela + plt->relasize); + for (rela = plt->rela; rela < relalim; rela++) { where = (uintptr_t *)(obj->relocbase + rela->r_offset); switch (ELF_R_TYPE(rela->r_info)) { #ifdef __CHERI_PURE_CAPABILITY__ @@ -720,7 +726,7 @@ reloc_gnu_ifunc(Obj_Entry *obj, int flags, def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); if (def == NULL) - return (-1); + return (false); if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC) continue; lock_release(rtld_bind_lock, lockstate); @@ -738,6 +744,20 @@ reloc_gnu_ifunc(Obj_Entry *obj, int flags, (const Elf_Rel *)rela); } } + return (true); +} + +int +reloc_gnu_ifunc(Obj_Entry *obj, int flags, + struct Struct_RtldLockState *lockstate) +{ + unsigned long i; + + if (!obj->gnu_ifunc) + return (0); + for (i = 0; i < obj->nplts; i++) + if (!reloc_gnu_ifunc_plt(&obj->plts[i], flags, lockstate)) + return (-1); obj->gnu_ifunc = false; return (0); } diff --git a/libexec/rtld-elf/aarch64/rtld_machdep.h b/libexec/rtld-elf/aarch64/rtld_machdep.h index b7a736d6d515..1b945fec8476 100644 --- a/libexec/rtld-elf/aarch64/rtld_machdep.h +++ b/libexec/rtld-elf/aarch64/rtld_machdep.h @@ -49,6 +49,8 @@ struct Struct_Obj_Entry; +#define MD_PLT_ENTRY + #define MD_OBJ_ENTRY \ bool variant_pcs : 1; /* Object has a variant pcs function */ diff --git a/libexec/rtld-elf/aarch64/rtld_start.S b/libexec/rtld-elf/aarch64/rtld_start.S index ecd18a7abf8f..5d47c91ac3f0 100644 --- a/libexec/rtld-elf/aarch64/rtld_start.S +++ b/libexec/rtld-elf/aarch64/rtld_start.S @@ -181,7 +181,7 @@ ENTRY(C18N_SYM(_rtld_bind_start)) #endif add x1, x1, x3 /* reloff = x3 + offset = (3 or 1.5) * offset */ - /* Load obj */ + /* Load plt */ ldr PTR(0), [PTR(16), #-PTR_WIDTH] /* Call into rtld */ diff --git a/libexec/rtld-elf/amd64/reloc.c b/libexec/rtld-elf/amd64/reloc.c index 2e24d6287ea3..6dee6c097bc3 100644 --- a/libexec/rtld-elf/amd64/reloc.c +++ b/libexec/rtld-elf/amd64/reloc.c @@ -114,11 +114,11 @@ do_copy_relocations(Obj_Entry *dstobj) /* Initialize the special GOT entries. */ void -init_pltgot(Obj_Entry *obj) +init_pltgot(Plt_Entry *plt) { - if (obj->pltgot != NULL) { - obj->pltgot[1] = (Elf_Addr) obj; - obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start; + if (plt->pltgot != NULL) { + plt->pltgot[1] = (Elf_Addr) plt; + plt->pltgot[2] = (Elf_Addr) &_rtld_bind_start; } } @@ -326,13 +326,14 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, /* Process the PLT relocations. */ int -reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused) +reloc_plt(Plt_Entry *plt, int flags __unused, RtldLockState *lockstate __unused) { + Obj_Entry *obj = plt->obj; const Elf_Rela *relalim; const Elf_Rela *rela; - relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize); - for (rela = obj->pltrela; rela < relalim; rela++) { + relalim = (const Elf_Rela *)((const char *)plt->rela + plt->relasize); + for (rela = plt->rela; rela < relalim; rela++) { Elf_Addr *where; switch(ELF_R_TYPE(rela->r_info)) { @@ -357,15 +358,16 @@ reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused) /* Relocate the jump slots in an object. */ int -reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) +reloc_jmpslots(Plt_Entry *plt, int flags, RtldLockState *lockstate) { + Obj_Entry *obj = plt->obj; const Elf_Rela *relalim; const Elf_Rela *rela; - if (obj->jmpslots_done) + if (plt->jmpslots_done) return 0; - relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize); - for (rela = obj->pltrela; rela < relalim; rela++) { + relalim = (const Elf_Rela *)((const char *)plt->rela + plt->relasize); + for (rela = plt->rela; rela < relalim; rela++) { Elf_Addr *where, target; const Elf_Sym *def; const Obj_Entry *defobj; @@ -394,7 +396,7 @@ reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) return (-1); } } - obj->jmpslots_done = true; + plt->jmpslots_done = true; return 0; } @@ -427,21 +429,30 @@ reloc_iresolve_one(Obj_Entry *obj, const Elf_Rela *rela, *where = target; } -int -reloc_iresolve(Obj_Entry *obj, RtldLockState *lockstate) +static void +reloc_iresolve_plt(Plt_Entry *plt, RtldLockState *lockstate) { const Elf_Rela *relalim; const Elf_Rela *rela; + relalim = (const Elf_Rela *)((const char *)plt->rela + + plt->relasize); + for (rela = plt->rela; rela < relalim; rela++) { + if (ELF_R_TYPE(rela->r_info) == R_X86_64_IRELATIVE) + reloc_iresolve_one(plt->obj, rela, lockstate); + } +} + +int +reloc_iresolve(Obj_Entry *obj, RtldLockState *lockstate) +{ + unsigned long i; + if (!obj->irelative) return (0); obj->irelative = false; - relalim = (const Elf_Rela *)((const char *)obj->pltrela + - obj->pltrelasize); - for (rela = obj->pltrela; rela < relalim; rela++) { - if (ELF_R_TYPE(rela->r_info) == R_X86_64_IRELATIVE) - reloc_iresolve_one(obj, rela, lockstate); - } + for (i = 0; i < obj->nplts; i++) + reloc_iresolve_plt(&obj->plts[i], lockstate); return (0); } @@ -462,16 +473,15 @@ reloc_iresolve_nonplt(Obj_Entry *obj, RtldLockState *lockstate) return (0); } -int -reloc_gnu_ifunc(Obj_Entry *obj, int flags, RtldLockState *lockstate) +static bool +reloc_gnu_ifunc_plt(Plt_Entry *plt, int flags, RtldLockState *lockstate) { + Obj_Entry *obj = plt->obj; const Elf_Rela *relalim; const Elf_Rela *rela; - if (!obj->gnu_ifunc) - return (0); - relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize); - for (rela = obj->pltrela; rela < relalim; rela++) { + relalim = (const Elf_Rela *)((const char *)plt->rela + plt->relasize); + for (rela = plt->rela; rela < relalim; rela++) { Elf_Addr *where, target; const Elf_Sym *def; const Obj_Entry *defobj; @@ -482,7 +492,7 @@ reloc_gnu_ifunc(Obj_Entry *obj, int flags, RtldLockState *lockstate) def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); if (def == NULL) - return (-1); + return (false); if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC) continue; lock_release(rtld_bind_lock, lockstate); @@ -492,8 +502,21 @@ reloc_gnu_ifunc(Obj_Entry *obj, int flags, RtldLockState *lockstate) break; } } - obj->gnu_ifunc = false; - return (0); + return (true); +} + +int +reloc_gnu_ifunc(Obj_Entry *obj, int flags, RtldLockState *lockstate) +{ + unsigned long i; + + if (!obj->gnu_ifunc) + return (0); + for (i = 0; i < obj->nplts; i++) + if (!reloc_gnu_ifunc_plt(&obj->plts[i], flags, lockstate)) + return (-1); + obj->gnu_ifunc = false; + return (0); } uint32_t cpu_feature, cpu_feature2, cpu_stdext_feature, cpu_stdext_feature2; diff --git a/libexec/rtld-elf/amd64/rtld_machdep.h b/libexec/rtld-elf/amd64/rtld_machdep.h index 1797d13c847d..026ef41b8d20 100644 --- a/libexec/rtld-elf/amd64/rtld_machdep.h +++ b/libexec/rtld-elf/amd64/rtld_machdep.h @@ -35,6 +35,8 @@ struct Struct_Obj_Entry; +#define MD_PLT_ENTRY + #define MD_OBJ_ENTRY /* Return the address of the .dynamic section in the dynamic linker. */ diff --git a/libexec/rtld-elf/amd64/rtld_start.S b/libexec/rtld-elf/amd64/rtld_start.S index 7f85a9f94d5c..e44d9f9a57e5 100644 --- a/libexec/rtld-elf/amd64/rtld_start.S +++ b/libexec/rtld-elf/amd64/rtld_start.S @@ -55,7 +55,7 @@ /* * Binder entry point. Control is transferred to here by code in the PLT. * On entry, there are two arguments on the stack. In ascending address - * order, they are (1) "obj", a pointer to the calling object's Obj_Entry, + * order, they are (1) "plt", a pointer to the calling object's Plt_Entry, * and (2) "reloff", the byte offset of the appropriate relocation entry * in the PLT relocation table. * @@ -65,7 +65,7 @@ * * Stack map: * reloff 0x60 - * obj 0x58 + * plt 0x58 * spare 0x50 * rflags 0x48 * rax 0x40 @@ -116,7 +116,7 @@ _rtld_bind_start: .cfi_adjust_cfa_offset 8 .cfi_offset %r11,-96 - movq 0x58(%rsp),%rdi # Fetch obj argument + movq 0x58(%rsp),%rdi # Fetch plt argument movq 0x60(%rsp),%rsi # Fetch reloff argument leaq (%rsi,%rsi,2),%rsi # multiply by 3 leaq (,%rsi,8),%rsi # now 8, for 24 (sizeof Elf_Rela) @@ -154,7 +154,7 @@ _rtld_bind_start: .cfi_restore %rax popfq # Restore rflags .cfi_adjust_cfa_offset -8 - leaq 16(%rsp),%rsp # Discard spare, obj, do not change rflags + leaq 16(%rsp),%rsp # Discard spare, plt, do not change rflags ret # "Return" to target address .cfi_endproc .size _rtld_bind_start, . - _rtld_bind_start diff --git a/libexec/rtld-elf/arm/reloc.c b/libexec/rtld-elf/arm/reloc.c index b10e6ce844d5..8388fd632fc9 100644 --- a/libexec/rtld-elf/arm/reloc.c +++ b/libexec/rtld-elf/arm/reloc.c @@ -17,11 +17,11 @@ #include "rtld_paths.h" void -init_pltgot(Obj_Entry *obj) +init_pltgot(Plt_Entry *plt) { - if (obj->pltgot != NULL) { - obj->pltgot[1] = (Elf_Addr) obj; - obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start; + if (plt->pltgot != NULL) { + plt->pltgot[1] = (Elf_Addr) plt; + plt->pltgot[2] = (Elf_Addr) &_rtld_bind_start; } } @@ -348,14 +348,15 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, * * Process the PLT relocations. * */ int -reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused) +reloc_plt(Plt_Entry *plt, int flags __unused, RtldLockState *lockstate __unused) { + Obj_Entry *obj = plt->obj; const Elf_Rel *rellim; const Elf_Rel *rel; - rellim = (const Elf_Rel *)((const char *)obj->pltrel + - obj->pltrelsize); - for (rel = obj->pltrel; rel < rellim; rel++) { + rellim = (const Elf_Rel *)((const char *)plt->rel + + plt->relsize); + for (rel = plt->rel; rel < rellim; rel++) { Elf_Addr *where; assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT); @@ -371,8 +372,9 @@ reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused) * * LD_BIND_NOW was set - force relocation for all jump slots * */ int -reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) +reloc_jmpslots(Plt_Entry *plt, int flags, RtldLockState *lockstate) { + Obj_Entry *obj = plt->obj; const Obj_Entry *defobj; const Elf_Rel *rellim; const Elf_Rel *rel; @@ -380,8 +382,8 @@ reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) Elf_Addr *where; Elf_Addr target; - rellim = (const Elf_Rel *)((const char *)obj->pltrel + obj->pltrelsize); - for (rel = obj->pltrel; rel < rellim; rel++) { + rellim = (const Elf_Rel *)((const char *)plt->rel + plt->relsize); + for (rel = plt->rel; rel < rellim; rel++) { assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT); where = (Elf_Addr *)(obj->relocbase + rel->r_offset); def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, @@ -396,7 +398,7 @@ reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) (const Elf_Rel *) rel); } - obj->jmpslots_done = true; + plt->jmpslots_done = true; return (0); } diff --git a/libexec/rtld-elf/arm/rtld_machdep.h b/libexec/rtld-elf/arm/rtld_machdep.h index f59b30028a3b..5d5561527b24 100644 --- a/libexec/rtld-elf/arm/rtld_machdep.h +++ b/libexec/rtld-elf/arm/rtld_machdep.h @@ -36,6 +36,8 @@ struct Struct_Obj_Entry; +#define MD_PLT_ENTRY + #define MD_OBJ_ENTRY /* Return the address of the .dynamic section in the dynamic linker. */ diff --git a/libexec/rtld-elf/arm/rtld_start.S b/libexec/rtld-elf/arm/rtld_start.S index 609a7d0603ed..12bc7f55a838 100644 --- a/libexec/rtld-elf/arm/rtld_start.S +++ b/libexec/rtld-elf/arm/rtld_start.S @@ -81,7 +81,7 @@ _rtld_bind_start: sub r1, r1, #4 /* r1 = 4 * n */ add r1, r1, r1 /* r1 = 8 * n */ - ldr r0, [lr, #-4] /* get obj ptr from GOT[1] */ + ldr r0, [lr, #-4] /* get plt ptr from GOT[1] */ mov r4, ip /* save GOT location */ mov r5, sp /* Save the stack pointer */ diff --git a/libexec/rtld-elf/debug.c b/libexec/rtld-elf/debug.c index f4f527778836..a2d098b1af12 100644 --- a/libexec/rtld-elf/debug.c +++ b/libexec/rtld-elf/debug.c @@ -73,6 +73,8 @@ dump_relocations (Obj_Entry *obj0) void dump_obj_relocations (Obj_Entry *obj) { + Plt_Entry *plt; + unsigned long i; rtld_printf("Object \"%s\", relocbase %p\n", obj->path, obj->relocbase); @@ -88,16 +90,19 @@ dump_obj_relocations (Obj_Entry *obj) dump_Elf_Rela(obj, obj->rela, obj->relasize); } - if (obj->pltrelsize) { - rtld_printf("PLT Relocations: %ld\n", - (obj->pltrelsize / sizeof(Elf_Rel))); - dump_Elf_Rel(obj, obj->pltrel, obj->pltrelsize); - } - - if (obj->pltrelasize) { - rtld_printf("PLT Relocations with Addend: %ld\n", - (obj->pltrelasize / sizeof(Elf_Rela))); - dump_Elf_Rela(obj, obj->pltrela, obj->pltrelasize); + for (i = 0; i < obj->nplts; i++) { + plt = &obj->plts[i]; + if (plt->relsize) { + rtld_printf("PLT[%lu] Relocations: %ld\n", i, + (plt->relsize / sizeof(Elf_Rel))); + dump_Elf_Rel(obj, plt->rel, plt->relsize); + } + + if (plt->relasize) { + rtld_printf("PLT[%lu] Relocations with Addend: %ld\n", i, + (plt->relasize / sizeof(Elf_Rela))); + dump_Elf_Rela(obj, plt->rela, plt->relasize); + } } } diff --git a/libexec/rtld-elf/i386/reloc.c b/libexec/rtld-elf/i386/reloc.c index c91239eb3989..b7b3e20e30c8 100644 --- a/libexec/rtld-elf/i386/reloc.c +++ b/libexec/rtld-elf/i386/reloc.c @@ -112,11 +112,11 @@ do_copy_relocations(Obj_Entry *dstobj) /* Initialize the special GOT entries. */ void -init_pltgot(Obj_Entry *obj) +init_pltgot(Plt_Entry *plt) { - if (obj->pltgot != NULL) { - obj->pltgot[1] = (Elf_Addr) obj; - obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start; + if (plt->pltgot != NULL) { + plt->pltgot[1] = (Elf_Addr) plt; + plt->pltgot[2] = (Elf_Addr) &_rtld_bind_start; } } @@ -279,13 +279,14 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, /* Process the PLT relocations. */ int -reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused) +reloc_plt(Plt_Entry *plt, int flags __unused, RtldLockState *lockstate __unused) { + Obj_Entry *obj = plt->obj; const Elf_Rel *rellim; const Elf_Rel *rel; - rellim = (const Elf_Rel *)((const char *)obj->pltrel + obj->pltrelsize); - for (rel = obj->pltrel; rel < rellim; rel++) { + rellim = (const Elf_Rel *)((const char *)plt->rel + plt->relsize); + for (rel = plt->rel; rel < rellim; rel++) { Elf_Addr *where/*, val*/; switch (ELF_R_TYPE(rel->r_info)) { @@ -310,15 +311,16 @@ reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused) /* Relocate the jump slots in an object. */ int -reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) +reloc_jmpslots(Plt_Entry *plt, int flags, RtldLockState *lockstate) { + Obj_Entry *obj = plt->obj; const Elf_Rel *rellim; const Elf_Rel *rel; - if (obj->jmpslots_done) + if (plt->jmpslots_done) return 0; - rellim = (const Elf_Rel *)((const char *)obj->pltrel + obj->pltrelsize); - for (rel = obj->pltrel; rel < rellim; rel++) { + rellim = (const Elf_Rel *)((const char *)plt->rel + plt->relsize); + for (rel = plt->rel; rel < rellim; rel++) { Elf_Addr *where, target; const Elf_Sym *def; const Obj_Entry *defobj; @@ -348,7 +350,7 @@ reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) } } - obj->jmpslots_done = true; + plt->jmpslots_done = true; return 0; } @@ -379,20 +381,29 @@ reloc_iresolve_one(Obj_Entry *obj, const Elf_Rel *rel, *where = target; } -int -reloc_iresolve(Obj_Entry *obj, RtldLockState *lockstate) +static void +reloc_iresolve_plt(Plt_Entry *plt, RtldLockState *lockstate) { const Elf_Rel *rellim; const Elf_Rel *rel; + rellim = (const Elf_Rel *)((const char *)plt->rel + plt->relsize); + for (rel = plt->rel; rel < rellim; rel++) { + if (ELF_R_TYPE(rel->r_info) == R_386_IRELATIVE) + reloc_iresolve_one(plt->obj, rel, lockstate); + } +} + +int +reloc_iresolve(Obj_Entry *obj, RtldLockState *lockstate) +{ + unsigned long i; + if (!obj->irelative) return (0); obj->irelative = false; - rellim = (const Elf_Rel *)((const char *)obj->pltrel + obj->pltrelsize); - for (rel = obj->pltrel; rel < rellim; rel++) { - if (ELF_R_TYPE(rel->r_info) == R_386_IRELATIVE) - reloc_iresolve_one(obj, rel, lockstate); - } + for (i = 0; i < obj->nplts; i++) + reloc_iresolve_plt(&obj->plts[i], lockstate); return (0); } @@ -413,16 +424,15 @@ reloc_iresolve_nonplt(Obj_Entry *obj, RtldLockState *lockstate) return (0); } -int -reloc_gnu_ifunc(Obj_Entry *obj, int flags, RtldLockState *lockstate) +static bool +reloc_gnu_ifunc_plt(Plt_Entry *plt, int flags, RtldLockState *lockstate) { + Obj_Entry *obj = plt->obj; const Elf_Rel *rellim; const Elf_Rel *rel; - if (!obj->gnu_ifunc) - return (0); - rellim = (const Elf_Rel *)((const char *)obj->pltrel + obj->pltrelsize); - for (rel = obj->pltrel; rel < rellim; rel++) { + rellim = (const Elf_Rel *)((const char *)plt->rel + plt->relsize); + for (rel = plt->rel; rel < rellim; rel++) { Elf_Addr *where, target; const Elf_Sym *def; const Obj_Entry *defobj; @@ -433,7 +443,7 @@ reloc_gnu_ifunc(Obj_Entry *obj, int flags, RtldLockState *lockstate) def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); if (def == NULL) - return (-1); + return (false); if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC) continue; lock_release(rtld_bind_lock, lockstate); @@ -443,9 +453,21 @@ reloc_gnu_ifunc(Obj_Entry *obj, int flags, RtldLockState *lockstate) break; } } + return (true); +} - obj->gnu_ifunc = false; - return (0); +int +reloc_gnu_ifunc(Obj_Entry *obj, int flags, RtldLockState *lockstate) +{ + unsigned long i; + + if (!obj->gnu_ifunc) + return (0); + for (i = 0; i < obj->nplts; i++) + if (!reloc_gnu_ifunc_plt(&obj->plts[i], flags, lockstate)) + return (-1); + obj->gnu_ifunc = false; + return (0); } uint32_t cpu_feature, cpu_feature2, cpu_stdext_feature, cpu_stdext_feature2; diff --git a/libexec/rtld-elf/i386/rtld_machdep.h b/libexec/rtld-elf/i386/rtld_machdep.h index 581f1dfb002d..84c1a62ddd7d 100644 --- a/libexec/rtld-elf/i386/rtld_machdep.h +++ b/libexec/rtld-elf/i386/rtld_machdep.h @@ -35,6 +35,8 @@ struct Struct_Obj_Entry; +#define MD_PLT_ENTRY + #define MD_OBJ_ENTRY /* Return the address of the .dynamic section in the dynamic linker. */ diff --git a/libexec/rtld-elf/i386/rtld_start.S b/libexec/rtld-elf/i386/rtld_start.S index 9dbe32d27628..528db597082c 100644 --- a/libexec/rtld-elf/i386/rtld_start.S +++ b/libexec/rtld-elf/i386/rtld_start.S @@ -64,7 +64,7 @@ /* * Binder entry point. Control is transferred to here by code in the PLT. * On entry, there are two arguments on the stack. In ascending address - * order, they are (1) "obj", a pointer to the calling object's Obj_Entry, + * order, they are (1) "plt", a pointer to the calling object's Plt_Entry, * and (2) "reloff", the byte offset of the appropriate relocation entry * in the PLT relocation table. * @@ -81,13 +81,13 @@ _rtld_bind_start: pushl %edx # Save %edx pushl %ecx # Save %ecx pushl 20(%esp) # Copy reloff argument - pushl 20(%esp) # Copy obj argument + pushl 20(%esp) # Copy plt argument call _rtld_bind # Transfer control to the binder /* Now %eax contains the entry point of the function being called. */ addl $8,%esp # Discard binder arguments - movl %eax,20(%esp) # Store target over obj argument + movl %eax,20(%esp) # Store target over plt argument popl %ecx # Restore %ecx popl %edx # Restore %edx popl %eax # Restore %eax diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c index e353d8ed9d33..787d1859900b 100644 --- a/libexec/rtld-elf/map_object.c +++ b/libexec/rtld-elf/map_object.c @@ -540,6 +540,8 @@ obj_free(Obj_Entry *obj) free(obj->origin_path); if (obj->z_origin) free(__DECONST(void*, obj->rpath)); + if (obj->plts) + free(obj->plts); if (obj->priv) free(obj->priv); if (obj->path) diff --git a/libexec/rtld-elf/powerpc/reloc.c b/libexec/rtld-elf/powerpc/reloc.c index 82a9669e3e73..c3eef6e9e0be 100644 --- a/libexec/rtld-elf/powerpc/reloc.c +++ b/libexec/rtld-elf/powerpc/reloc.c @@ -60,8 +60,8 @@ void _rtld_bind_secureplt_start(void); bool arch_digest_dynamic(struct Struct_Obj_Entry *obj, const Elf_Dyn *dynp) { - if (dynp->d_tag == DT_PPC_GOT) { - obj->gotptr = (Elf_Addr *)(obj->relocbase + dynp->d_un.d_ptr); + if (dynp->d_tag == DT_PPC_GOT) { + obj->plts[0].gotptr = (Elf_Addr *)(obj->relocbase + dynp->d_un.d_ptr); return (true); } @@ -382,25 +382,26 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, * Initialise a PLT slot to the resolving trampoline */ static int -reloc_plt_object(Obj_Entry *obj, const Elf_Rela *rela) +reloc_plt_object(Plt_Entry *plt, const Elf_Rela *rela) { + Obj_Entry *obj = plt->obj; Elf_Word *where = (Elf_Word *)(obj->relocbase + rela->r_offset); Elf_Addr *pltresolve, *pltlongresolve, *jmptab; Elf_Addr distance; - int N = obj->pltrelasize / sizeof(Elf_Rela); + int N = plt->relasize / sizeof(Elf_Rela); int reloff; - reloff = rela - obj->pltrela; + reloff = rela - plt->rela; if (reloff < 0) return (-1); - if (obj->gotptr != NULL) { + if (plt->gotptr != NULL) { *where += (Elf_Addr)obj->relocbase; return (0); } - pltlongresolve = obj->pltgot + 5; + pltlongresolve = plt->pltgot + 5; pltresolve = pltlongresolve + 5; distance = (Elf_Addr)pltresolve - (Elf_Addr)(where + 1); @@ -414,7 +415,7 @@ reloc_plt_object(Obj_Entry *obj, const Elf_Rela *rela) where[0] = 0x39600000 | reloff; where[1] = 0x48000000 | (distance & 0x03fffffc); } else { - jmptab = obj->pltgot + JMPTAB_BASE(N); + jmptab = plt->pltgot + JMPTAB_BASE(N); jmptab[reloff] = (u_int)pltlongresolve; /* lis r11,jmptab[reloff]@ha */ @@ -440,17 +441,18 @@ reloc_plt_object(Obj_Entry *obj, const Elf_Rela *rela) * Process the PLT relocations. */ int -reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused) +reloc_plt(Plt_Entry *plt, int flags __unused, RtldLockState *lockstate __unused) { + Obj_Entry *obj = plt->obj; const Elf_Rela *relalim; const Elf_Rela *rela; - int N = obj->pltrelasize / sizeof(Elf_Rela); + int N = plt->relasize / sizeof(Elf_Rela); - if (obj->pltrelasize != 0) { + if (plt->relasize != 0) { - relalim = (const Elf_Rela *)((const char *)obj->pltrela + - obj->pltrelasize); - for (rela = obj->pltrela; rela < relalim; rela++) { + relalim = (const Elf_Rela *)((const char *)plt->rela + + plt->relasize); + for (rela = plt->rela; rela < relalim; rela++) { if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE) { dbg("ABI violation - found IRELATIVE in the PLT."); obj->irelative = true; @@ -464,7 +466,7 @@ reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused) */ assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT); - if (reloc_plt_object(obj, rela) < 0) { + if (reloc_plt_object(plt, rela) < 0) { return (-1); } } @@ -474,8 +476,8 @@ reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused) * Sync the icache for the byte range represented by the * trampoline routines and call slots. */ - if (obj->pltgot != NULL && obj->gotptr == NULL) - __syncicache(obj->pltgot, JMPTAB_BASE(N)*4); + if (plt->pltgot != NULL && plt->gotptr == NULL) + __syncicache(plt->pltgot, JMPTAB_BASE(N)*4); return (0); } @@ -484,8 +486,9 @@ reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused) * LD_BIND_NOW was set - force relocation for all jump slots */ int -reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) +reloc_jmpslots(Plt_Entry *plt, int flags, RtldLockState *lockstate) { + Obj_Entry *obj = plt->obj; const Obj_Entry *defobj; const Elf_Rela *relalim; const Elf_Rela *rela; @@ -493,9 +496,9 @@ reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) Elf_Addr *where; Elf_Addr target; - relalim = (const Elf_Rela *)((const char *)obj->pltrela + - obj->pltrelasize); - for (rela = obj->pltrela; rela < relalim; rela++) { + relalim = (const Elf_Rela *)((const char *)plt->rela + + plt->relasize); + for (rela = plt->rela; rela < relalim; rela++) { /* This isn't actually a jump slot, ignore it. */ if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE) continue; @@ -524,7 +527,7 @@ reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) } } - obj->jmpslots_done = true; + plt->jmpslots_done = true; return (0); } @@ -537,6 +540,7 @@ Elf_Addr reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *defobj __unused, const Obj_Entry *obj, const Elf_Rel *rel) { + Plt_Entry *plt = &objs->plts[0]; Elf_Addr offset; const Elf_Rela *rela = (const Elf_Rela *) rel; @@ -550,10 +554,10 @@ reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, /* * Process Secure-PLT. */ - if (obj->gotptr != NULL) { - assert(wherep >= (Elf_Word *)obj->pltgot); + if (plt->gotptr != NULL) { + assert(wherep >= (Elf_Word *)plt->pltgot); assert(wherep < - (Elf_Word *)obj->pltgot + obj->pltrelasize); + (Elf_Word *)plt->pltgot + plt->relasize); if (*wherep != target) *wherep = target; goto out; @@ -578,18 +582,18 @@ reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, } else { Elf_Addr *pltcall, *jmptab; int distance; - int N = obj->pltrelasize / sizeof(Elf_Rela); - int reloff = rela - obj->pltrela; + int N = plt->relasize / sizeof(Elf_Rela); + int reloff = rela - plt->rela; if (reloff < 0) return (-1); - pltcall = obj->pltgot; + pltcall = plt->pltgot; dbg(" reloc_jmpslot: indir, reloff=%x, N=%x\n", reloff, N); - jmptab = obj->pltgot + JMPTAB_BASE(N); + jmptab = plt->pltgot + JMPTAB_BASE(N); jmptab[reloff] = target; mb(); /* Order jmptab update before next changes */ @@ -619,6 +623,7 @@ reloc_iresolve(Obj_Entry *obj, * Since PLT slots on PowerPC are always R_PPC_JMP_SLOT, * R_PPC_IRELATIVE is in RELA. */ + Plt_Entry *plt = &objs->plts[0]; const Elf_Rela *relalim; const Elf_Rela *rela; Elf_Addr *where, target, *ptr; @@ -643,8 +648,8 @@ reloc_iresolve(Obj_Entry *obj, * XXX Remove me when lld is fixed! * LLD currently makes illegal relocations in the PLT. */ - relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize); - for (rela = obj->pltrela; rela < relalim; rela++) { + relalim = (const Elf_Rela *)((const char *)plt->rela + plt->relasize); + for (rela = plt->rela; rela < relalim; rela++) { if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE) { ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend); where = (Elf_Addr *)(obj->relocbase + rela->r_offset); @@ -672,6 +677,7 @@ int reloc_gnu_ifunc(Obj_Entry *obj __unused, int flags __unused, struct Struct_RtldLockState *lockstate __unused) { + Plt_Entry *plt = &objs->plts[0]; const Elf_Rela *relalim; const Elf_Rela *rela; Elf_Addr *where, target; @@ -680,8 +686,8 @@ reloc_gnu_ifunc(Obj_Entry *obj __unused, int flags __unused, if (!obj->gnu_ifunc) return (0); - relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize); - for (rela = obj->pltrela; rela < relalim; rela++) { + relalim = (const Elf_Rela *)((const char *)plt->rela + plt->relasize); + for (rela = plt->rela; rela < relalim; rela++) { if (ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT) { where = (Elf_Addr *)(obj->relocbase + rela->r_offset); def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, @@ -709,25 +715,26 @@ reloc_gnu_ifunc(Obj_Entry *obj __unused, int flags __unused, #define PLTRESOLVE_SIZE 24 void -init_pltgot(Obj_Entry *obj) +init_pltgot(Plt_Entry *plt) { + Obj_Entry *obj = plt->obj; Elf_Word *pltcall, *pltresolve, *pltlongresolve; Elf_Word *jmptab; - int N = obj->pltrelasize / sizeof(Elf_Rela); + int N = plt->relasize / sizeof(Elf_Rela); - pltcall = obj->pltgot; + pltcall = plt->pltgot; if (pltcall == NULL) { return; } /* Handle Secure-PLT first, if applicable. */ - if (obj->gotptr != NULL) { - obj->gotptr[1] = (Elf_Addr)_rtld_bind_secureplt_start; - obj->gotptr[2] = (Elf_Addr)obj; + if (plt->gotptr != NULL) { + plt->gotptr[1] = (Elf_Addr)_rtld_bind_secureplt_start; + plt->gotptr[2] = (Elf_Addr)plt; dbg("obj %s secure-plt gotptr=%p start=%p obj=%p", - obj->path, obj->gotptr, - (void *)obj->gotptr[1], (void *)obj->gotptr[2]); + obj->path, plt->gotptr, + (void *)plt->gotptr[1], (void *)plt->gotptr[2]); return; } @@ -758,7 +765,7 @@ init_pltgot(Obj_Entry *obj) * of the jumptable into the absolute-call assembler code so it * can determine this address. */ - jmptab = obj->pltgot + JMPTAB_BASE(N); + jmptab = plt->pltgot + JMPTAB_BASE(N); pltcall[1] |= _ppc_ha(jmptab); /* addis 11,11,jmptab@ha */ pltcall[2] |= _ppc_la(jmptab); /* lwz 11,jmptab@l(11) */ @@ -771,7 +778,7 @@ init_pltgot(Obj_Entry *obj) * We place pltlongresolve first, so it can fix up its arguments * and then fall through to the regular PLT resolver. */ - pltlongresolve = obj->pltgot + 5; + pltlongresolve = plt->pltgot + 5; memcpy(pltlongresolve, _rtld_powerpc_pltlongresolve, PLTLONGRESOLVE_SIZE); @@ -782,8 +789,8 @@ init_pltgot(Obj_Entry *obj) memcpy(pltresolve, _rtld_powerpc_pltresolve, PLTRESOLVE_SIZE); pltresolve[0] |= _ppc_ha(_rtld_bind_start); pltresolve[1] |= _ppc_la(_rtld_bind_start); - pltresolve[3] |= _ppc_ha(obj); - pltresolve[4] |= _ppc_la(obj); + pltresolve[3] |= _ppc_ha(plt); + pltresolve[4] |= _ppc_la(plt); /* * The icache will be sync'd in reloc_plt, which is called diff --git a/libexec/rtld-elf/powerpc/rtld_machdep.h b/libexec/rtld-elf/powerpc/rtld_machdep.h index ec470f238991..e1e60b6d8999 100644 --- a/libexec/rtld-elf/powerpc/rtld_machdep.h +++ b/libexec/rtld-elf/powerpc/rtld_machdep.h @@ -35,9 +35,11 @@ struct Struct_Obj_Entry; -#define MD_OBJ_ENTRY \ +#define MD_PLT_ENTRY \ Elf_Addr *gotptr; /* GOT pointer (secure-plt only) */ +#define MD_OBJ_ENTRY + /* Return the address of the .dynamic section in the dynamic linker. */ #define rtld_dynamic(obj) (&_DYNAMIC) diff --git a/libexec/rtld-elf/powerpc64/reloc.c b/libexec/rtld-elf/powerpc64/reloc.c index 70acd0ac390d..82db2cfd72fb 100644 --- a/libexec/rtld-elf/powerpc64/reloc.c +++ b/libexec/rtld-elf/powerpc64/reloc.c @@ -381,12 +381,13 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, * Initialise a PLT slot to the resolving trampoline */ static int -reloc_plt_object(Obj_Entry *obj, const Elf_Rela *rela) +reloc_plt_object(Plt_Entry *plt, const Elf_Rela *rela) { + Obj_Entry *obj = plt->obj; Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); long reloff; - reloff = rela - obj->pltrela; + reloff = rela - plt->rela; dbg(" reloc_plt_object: where=%p,reloff=%lx,glink=%#lx", (void *)where, reloff, obj->glink); @@ -408,15 +409,16 @@ reloc_plt_object(Obj_Entry *obj, const Elf_Rela *rela) * Process the PLT relocations. */ int -reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused) +reloc_plt(Plt_Entry *plt, int flags __unused, RtldLockState *lockstate __unused) { + Obj_Entry *obj = plt->obj; const Elf_Rela *relalim; const Elf_Rela *rela; - if (obj->pltrelasize != 0) { - relalim = (const Elf_Rela *)((const char *)obj->pltrela + - obj->pltrelasize); - for (rela = obj->pltrela; rela < relalim; rela++) { + if (plt->relasize != 0) { + relalim = (const Elf_Rela *)((const char *)plt->rela + + plt->relasize); + for (rela = plt->rela; rela < relalim; rela++) { #if defined(_CALL_ELF) && _CALL_ELF == 2 if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE) { @@ -432,7 +434,7 @@ reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused) */ assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT); - if (reloc_plt_object(obj, rela) < 0) { + if (reloc_plt_object(plt, rela) < 0) { return (-1); } } @@ -445,8 +447,9 @@ reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused) * LD_BIND_NOW was set - force relocation for all jump slots */ int -reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) +reloc_jmpslots(Plt_Entry *plt, int flags, RtldLockState *lockstate) { + Obj_Entry *obj = plt->obj; const Obj_Entry *defobj; const Elf_Rela *relalim; const Elf_Rela *rela; @@ -454,9 +457,9 @@ reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) Elf_Addr *where; Elf_Addr target; - relalim = (const Elf_Rela *)((const char *)obj->pltrela + - obj->pltrelasize); - for (rela = obj->pltrela; rela < relalim; rela++) { + relalim = (const Elf_Rela *)((const char *)plt->rela + + plt->relasize); + for (rela = plt->rela; rela < relalim; rela++) { /* This isn't actually a jump slot, ignore it. */ if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE) continue; @@ -489,12 +492,11 @@ reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) } } - obj->jmpslots_done = true; + plt->jmpslots_done = true; return (0); } - /* * Update the value of a PLT jump slot. */ @@ -564,6 +566,31 @@ reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *defobj __unuse return (target); } +#if defined(_CALL_ELF) && _CALL_ELF == 2 +static void +reloc_iresolve_plt(Plt_Entry *plt, RtldLockState *lockstate) +{ + Obj_Entry *obj = plt->obj; + const Elf_Rela *relalim; + const Elf_Rela *rela; + Elf_Addr *where, target, *ptr; + + relalim = (const Elf_Rela *)((const char *)plt->rela + plt->relasize); + for (rela = plt->rela; rela < relalim; rela++) { + if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE) { + ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend); + where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + + lock_release(rtld_bind_lock, lockstate); + target = call_ifunc_resolver(ptr); + wlock_acquire(rtld_bind_lock, lockstate); + + *where = target; + } + } +} +#endif + int reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) @@ -581,6 +608,7 @@ reloc_iresolve(Obj_Entry *obj, const Elf_Rela *relalim; const Elf_Rela *rela; Elf_Addr *where, target, *ptr; + unsigned long i; if (!obj->irelative) return (0); @@ -602,51 +630,33 @@ reloc_iresolve(Obj_Entry *obj, * XXX Remove me when lld is fixed! * LLD currently makes illegal relocations in the PLT. */ - relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize); - for (rela = obj->pltrela; rela < relalim; rela++) { - if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE) { - ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend); - where = (Elf_Addr *)(obj->relocbase + rela->r_offset); - - lock_release(rtld_bind_lock, lockstate); - target = call_ifunc_resolver(ptr); - wlock_acquire(rtld_bind_lock, lockstate); - - *where = target; - } - } + for (i = 0; i < obj->nplts; i++) + reloc_iresolve_plt(&obj->plts[i], lockstate); obj->irelative = false; return (0); #endif } -int -reloc_gnu_ifunc(Obj_Entry *obj __unused, int flags __unused, - struct Struct_RtldLockState *lockstate __unused) +#if defined(_CALL_ELF) && _CALL_ELF == 2 +static bool +reloc_gnu_ifunc_plt(Plt_Entry *plt, int flags, RtldLockState *lockstate) { -#if !defined(_CALL_ELF) || _CALL_ELF == 1 - _rtld_error("reloc_gnu_ifunc(): Not implemented!"); - /* XXX not implemented */ - return (-1); -#else - + Obj_Entry *obj = plt->obj; const Elf_Rela *relalim; const Elf_Rela *rela; Elf_Addr *where, target; const Elf_Sym *def; const Obj_Entry *defobj; - if (!obj->gnu_ifunc) - return (0); - relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize); - for (rela = obj->pltrela; rela < relalim; rela++) { + relalim = (const Elf_Rela *)((const char *)plt->rela + plt->relasize); + for (rela = plt->rela; rela < relalim; rela++) { if (ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT) { where = (Elf_Addr *)(obj->relocbase + rela->r_offset); def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); if (def == NULL) - return (-1); + return (false); if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC) continue; lock_release(rtld_bind_lock, lockstate); @@ -656,6 +666,26 @@ reloc_gnu_ifunc(Obj_Entry *obj __unused, int flags __unused, (const Elf_Rel *)rela); } } + return (true); +} +#endif + +int +reloc_gnu_ifunc(Obj_Entry *obj, int flags, + struct Struct_RtldLockState *lockstate) +{ +#if !defined(_CALL_ELF) || _CALL_ELF == 1 + _rtld_error("reloc_gnu_ifunc(): Not implemented!"); + /* XXX not implemented */ + return (-1); +#else + unsigned long i; + + if (!obj->gnu_ifunc) + return (0); + for (i = 0; i < obj->nplts; i++) + if (!reloc_gnu_ifunc_plt(&obj->plts[i], flags, lockstate)) + return (-1); obj->gnu_ifunc = false; return (0); #endif @@ -669,11 +699,11 @@ reloc_iresolve_nonplt(Obj_Entry *obj __unused, } void -init_pltgot(Obj_Entry *obj) +init_pltgot(Plt_Entry *plt) { Elf_Addr *pltcall; - pltcall = obj->pltgot; + pltcall = plt->pltgot; if (pltcall == NULL) { return; @@ -681,10 +711,10 @@ init_pltgot(Obj_Entry *obj) #if defined(_CALL_ELF) && _CALL_ELF == 2 pltcall[0] = (Elf_Addr)&_rtld_bind_start; - pltcall[1] = (Elf_Addr)obj; + pltcall[1] = (Elf_Addr)plt; #else memcpy(pltcall, _rtld_bind_start, sizeof(struct funcdesc)); - pltcall[2] = (Elf_Addr)obj; + pltcall[2] = (Elf_Addr)plt; #endif } diff --git a/libexec/rtld-elf/powerpc64/rtld_machdep.h b/libexec/rtld-elf/powerpc64/rtld_machdep.h index d628e776bae9..1826186ef79e 100644 --- a/libexec/rtld-elf/powerpc64/rtld_machdep.h +++ b/libexec/rtld-elf/powerpc64/rtld_machdep.h @@ -35,6 +35,8 @@ struct Struct_Obj_Entry; +#define MD_PLT_ENTRY + #define MD_OBJ_ENTRY \ Elf_Addr glink; /* GLINK PLT call stub section */ diff --git a/libexec/rtld-elf/powerpc64/rtld_start.S b/libexec/rtld-elf/powerpc64/rtld_start.S index 796a41e34601..82fcdd7d6e0d 100644 --- a/libexec/rtld-elf/powerpc64/rtld_start.S +++ b/libexec/rtld-elf/powerpc64/rtld_start.S @@ -146,7 +146,7 @@ _ENTRY(_rtld_bind_start) mr %r3,%r11 mulli %r4,%r12,24 # Multiply index by sizeof(Elf_Rela) - bl _rtld_bind # target addr = _rtld_bind(obj, reloff) + bl _rtld_bind # target addr = _rtld_bind(plt, reloff) nop #if !defined(_CALL_ELF) || _CALL_ELF == 1 diff --git a/libexec/rtld-elf/riscv/reloc.c b/libexec/rtld-elf/riscv/reloc.c index 9b13afd5323c..fe7d72f4fe9c 100644 --- a/libexec/rtld-elf/riscv/reloc.c +++ b/libexec/rtld-elf/riscv/reloc.c @@ -77,12 +77,12 @@ set_gp(Obj_Entry *obj) #endif void -init_pltgot(Obj_Entry *obj) +init_pltgot(Plt_Entry *plt) { - if (obj->pltgot != NULL) { - obj->pltgot[0] = (Elf_Addr)&_rtld_bind_start; - obj->pltgot[1] = (Elf_Addr)obj; + if (plt->pltgot != NULL) { + plt->pltgot[0] = (Elf_Addr)&_rtld_bind_start; + plt->pltgot[1] = (Elf_Addr)plt; } } @@ -196,14 +196,15 @@ do_copy_relocations(Obj_Entry *dstobj) * Process the PLT relocations. */ int -reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused) +reloc_plt(Plt_Entry *plt, int flags __unused, RtldLockState *lockstate __unused) { + Obj_Entry *obj = plt->obj; const Elf_Rela *relalim; const Elf_Rela *rela; - relalim = (const Elf_Rela *)((const char *)obj->pltrela + - obj->pltrelasize); - for (rela = obj->pltrela; rela < relalim; rela++) { + relalim = (const Elf_Rela *)((const char *)plt->rela + + plt->relasize); + for (rela = plt->rela; rela < relalim; rela++) { Elf_Addr *where; where = (Elf_Addr *)(obj->relocbase + rela->r_offset); @@ -229,16 +230,17 @@ reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused) * LD_BIND_NOW was set - force relocation for all jump slots */ int -reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) +reloc_jmpslots(Plt_Entry *plt, int flags, RtldLockState *lockstate) { + Obj_Entry *obj = plt->obj; const Obj_Entry *defobj; const Elf_Rela *relalim; const Elf_Rela *rela; const Elf_Sym *def; - relalim = (const Elf_Rela *)((const char *)obj->pltrela + - obj->pltrelasize); - for (rela = obj->pltrela; rela < relalim; rela++) { + relalim = (const Elf_Rela *)((const char *)plt->rela + + plt->relasize); + for (rela = plt->rela; rela < relalim; rela++) { Elf_Addr *where; where = (Elf_Addr *)(obj->relocbase + rela->r_offset); @@ -282,22 +284,30 @@ reloc_iresolve_one(Obj_Entry *obj, const Elf_Rela *rela, *where = target; } -int -reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) +static void +reloc_iresolve_plt(Plt_Entry *plt, struct Struct_RtldLockState *lockstate) { const Elf_Rela *relalim; const Elf_Rela *rela; + relalim = (const Elf_Rela *)((const char *)plt->rela + plt->relasize); + for (rela = plt->rela; rela < relalim; rela++) { + if (ELF_R_TYPE(rela->r_info) == R_RISCV_IRELATIVE) + reloc_iresolve_one(plt->obj, rela, lockstate); + } +} + +int +reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) +{ + unsigned long i; + if (!obj->irelative) return (0); obj->irelative = false; - relalim = (const Elf_Rela *)((const char *)obj->pltrela + - obj->pltrelasize); - for (rela = obj->pltrela; rela < relalim; rela++) { - if (ELF_R_TYPE(rela->r_info) == R_RISCV_IRELATIVE) - reloc_iresolve_one(obj, rela, lockstate); - } + for (i = 0; i < obj->nplts; i++) + reloc_iresolve_plt(&obj->plts[i], lockstate); return (0); } @@ -319,27 +329,24 @@ reloc_iresolve_nonplt(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) return (0); } -int -reloc_gnu_ifunc(Obj_Entry *obj, int flags, - struct Struct_RtldLockState *lockstate) +static bool +reloc_gnu_ifunc_plt(Plt_Entry *plt, int flags, RtldLockState *lockstate) { + Obj_Entry *obj = plt->obj; const Elf_Rela *relalim; const Elf_Rela *rela; uintptr_t *where, target; const Elf_Sym *def; const Obj_Entry *defobj; - if (!obj->gnu_ifunc) - return (0); - - relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize); - for (rela = obj->pltrela; rela < relalim; rela++) { + relalim = (const Elf_Rela *)((const char *)plt->rela + plt->relasize); + for (rela = plt->rela; rela < relalim; rela++) { if (ELF_R_TYPE(rela->r_info) == R_RISCV_JUMP_SLOT) { where = (uintptr_t *)(obj->relocbase + rela->r_offset); def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); if (def == NULL) - return (-1); + return (false); if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC) continue; @@ -350,6 +357,21 @@ reloc_gnu_ifunc(Obj_Entry *obj, int flags, (const Elf_Rel *)rela); } } + return (true); +} + + +int +reloc_gnu_ifunc(Obj_Entry *obj, int flags, + struct Struct_RtldLockState *lockstate) +{ + unsigned long i; + + if (!obj->gnu_ifunc) + return (0); + for (i = 0; i < obj->nplts; i++) + if (!reloc_gnu_ifunc_plt(&obj->plts[i], flags, lockstate)) + return (-1); obj->gnu_ifunc = false; return (0); } diff --git a/libexec/rtld-elf/riscv/rtld_machdep.h b/libexec/rtld-elf/riscv/rtld_machdep.h index f464c70e2b06..f80b11a3ffa2 100644 --- a/libexec/rtld-elf/riscv/rtld_machdep.h +++ b/libexec/rtld-elf/riscv/rtld_machdep.h @@ -44,6 +44,8 @@ struct Struct_Obj_Entry; +#define MD_PLT_ENTRY + #define MD_OBJ_ENTRY #ifndef __CHERI_PURE_CAPABILITY__ diff --git a/libexec/rtld-elf/riscv/rtld_start.S b/libexec/rtld-elf/riscv/rtld_start.S index 6e8c0a1ac04c..81af964840e6 100644 --- a/libexec/rtld-elf/riscv/rtld_start.S +++ b/libexec/rtld-elf/riscv/rtld_start.S @@ -147,7 +147,7 @@ ENTRY(.rtld_start) END(.rtld_start) /* - * t0 = obj pointer + * t0 = plt pointer * t1 = reloc offset */ ENTRY(_rtld_bind_start) @@ -190,7 +190,7 @@ ENTRY(_rtld_bind_start) slli a1, t1, 1 /* Mult items by 2 */ add a1, a1, t1 /* Plus item */ - /* Load obj */ + /* Load plt */ MV_PTR PTR(a0), PTR(t0) /* Call into rtld */ diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 9a498c204960..05e57f4ecbda 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -281,7 +281,7 @@ func_ptr_type _rtld(Elf_Auxinfo *aux, func_ptr_type *exit_proc, Obj_Entry **objp #else func_ptr_type _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp); #endif -uintptr_t _rtld_bind(Obj_Entry *obj, Elf_Size reloff); +uintptr_t _rtld_bind(Plt_Entry *obj, Elf_Size reloff); int npagesizes; static int osreldate; @@ -852,7 +852,8 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) dbg("%s is initialized, base address = " PTR_FMT, __progname, (caddr_t) aux_info[AT_BASE]->a_un.a_ptr); dbg("RTLD dynamic = " PTR_FMT, obj_rtld.dynamic); - dbg("RTLD pltgot = " PTR_FMT, obj_rtld.pltgot); + dbg("RTLD pltgot = " PTR_FMT, obj_rtld.nplts == 0 ? NULL : + obj_rtld.plts[0].pltgot); dbg("initializing thread locks"); lockdflt_init(); @@ -1191,11 +1192,12 @@ rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def) } uintptr_t -_rtld_bind(Obj_Entry *obj, Elf_Size reloff) +_rtld_bind(Plt_Entry *plt, Elf_Size reloff) { const Elf_Rel *rel; const Elf_Sym *def; const Obj_Entry *defobj; + Obj_Entry *obj; uintptr_t *where; uintptr_t target; RtldLockState lockstate; @@ -1203,18 +1205,19 @@ _rtld_bind(Obj_Entry *obj, Elf_Size reloff) struct trusted_frame *tf; if (C18N_ENABLED) { - obj = cheri_unseal(obj, sealer_pltgot); + plt = cheri_unseal(plt, sealer_pltgot); tf = push_dummy_rtld_trusted_frame(get_trusted_stk()); } #endif + obj = plt->obj; rlock_acquire(rtld_bind_lock, &lockstate); if (sigsetjmp(lockstate.env, 0) != 0) lock_upgrade(rtld_bind_lock, &lockstate); - if (obj->pltrel) - rel = (const Elf_Rel *)((const char *)obj->pltrel + reloff); + if (plt->rel != NULL) + rel = (const Elf_Rel *)((const char *)plt->rel + reloff); else - rel = (const Elf_Rel *)((const char *)obj->pltrela + reloff); + rel = (const Elf_Rel *)((const char *)plt->rela + reloff); where = (uintptr_t *)(obj->relocbase + rel->r_offset); def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, SYMLOOK_IN_PLT, @@ -1443,6 +1446,33 @@ rtld_die(void) _exit(1); } +/* + * Count the number of PLTs in a shared object's DYNAMIC section. + */ +static unsigned long +count_plts(const Elf_Dyn *dynp) +{ + unsigned long jmprel, pltrelsz, pltgot; + + jmprel = pltrelsz = pltgot = 0; + for (; dynp->d_tag != DT_NULL; dynp++) { + switch (dynp->d_tag) { + case DT_JMPREL: + jmprel++; + break; + case DT_PLTRELSZ: + pltrelsz++; + break; + case DT_PLTGOT: + pltgot++; + break; + default: + break; + } + } + return (MAX(MAX(jmprel, pltrelsz), pltgot)); +} + /* * Process a shared object's DYNAMIC section, and save the important * information in its Obj_Entry structure. @@ -1457,10 +1487,13 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath, Needed_Entry **needed_aux_filtees_tail = &obj->needed_aux_filtees; const Elf_Hashelt *hashtab; const Elf32_Word *hashval; + Plt_Entry *plt; Elf32_Word bkt, nmaskwords; + unsigned long i, jmprel, pltrelsz, pltgot; int bloom_size32; int plttype = DT_REL; + jmprel = pltrelsz = pltgot = 0; *dyn_rpath = NULL; *dyn_soname = NULL; *dyn_runpath = NULL; @@ -1469,6 +1502,9 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath, dynp = obj->dynamic; if (dynp == NULL) return; + obj->nplts = count_plts(dynp); + if (obj->nplts != 0) + obj->plts = xcalloc(obj->nplts, sizeof(*obj->plts)); for (; dynp->d_tag != DT_NULL; dynp++) { switch (dynp->d_tag) { @@ -1485,12 +1521,14 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath, break; case DT_JMPREL: - obj->pltrel = (const Elf_Rel *) + obj->plts[jmprel].rel = (const Elf_Rel *) (obj->relocbase + dynp->d_un.d_ptr); + jmprel++; break; case DT_PLTRELSZ: - obj->pltrelsize = dynp->d_un.d_val; + obj->plts[pltrelsz].relsize = dynp->d_un.d_val; + pltrelsz++; break; case DT_RELA: @@ -1643,7 +1681,9 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath, break; case DT_PLTGOT: - obj->pltgot = (uintptr_t *)(obj->relocbase + dynp->d_un.d_ptr); + obj->plts[pltgot].pltgot = + (uintptr_t *)(obj->relocbase + dynp->d_un.d_ptr); + pltgot++; break; case DT_TEXTREL: @@ -1767,10 +1807,13 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath, obj->traced = false; if (plttype == DT_RELA) { - obj->pltrela = (const Elf_Rela *) obj->pltrel; - obj->pltrel = NULL; - obj->pltrelasize = obj->pltrelsize; - obj->pltrelsize = 0; + for (i = 0; i < obj->nplts; i++) { + plt = &obj->plts[i]; + plt->rela = (const Elf_Rela *) plt->rel; + plt->rel = NULL; + plt->relasize = plt->relsize; + plt->relsize = 0; + } } /* Determine size of dynsym table (equal to nchains of sysv hash) */ @@ -1808,7 +1851,19 @@ static bool digest_dynamic2(Obj_Entry *obj, const Elf_Dyn *dyn_rpath, const Elf_Dyn *dyn_soname, const Elf_Dyn *dyn_runpath) { + Plt_Entry *plt; + unsigned long i; + for (i = 0; i < obj->nplts; i++) { + plt = &obj->plts[i]; + if (plt->pltgot == NULL) + return (false); + if (plt->rel == NULL && plt->rela == NULL) + return (false); + if (plt->relsize == 0 && plt->relasize == 0) + return (false); + plt->obj = obj; + } if (obj->z_origin && !obj_resolve_origin(obj)) return (false); @@ -1827,8 +1882,11 @@ digest_dynamic2(Obj_Entry *obj, const Elf_Dyn *dyn_rpath, // any overflows at runtime. set_bounds_if_nonnull(obj->rel, obj->relsize); set_bounds_if_nonnull(obj->rela, obj->relasize); - set_bounds_if_nonnull(obj->pltrel, obj->pltrelsize); - set_bounds_if_nonnull(obj->pltrela, obj->pltrelasize); + for (i = 0; i < obj->nplts; i++) { + plt = &obj->plts[i]; + set_bounds_if_nonnull(plt->rel, plt->relsize); + set_bounds_if_nonnull(plt->rela, plt->relasize); + } set_bounds_if_nonnull(obj->strtab, obj->strsize); set_bounds_if_nonnull(obj->phdr, obj->phsize); @@ -3675,6 +3733,8 @@ static int relocate_object(Obj_Entry *obj, bool bind_now, Obj_Entry *rtldobj, int flags, RtldLockState *lockstate) { + Plt_Entry *plt; + unsigned long i; if (obj->relocated) return (0); @@ -3705,16 +3765,20 @@ relocate_object(Obj_Entry *obj, bool bind_now, Obj_Entry *rtldobj, if (obj->textrel && reloc_textrel_prot(obj, false) != 0) return (-1); - /* Set the special PLT or GOT entries. */ - init_pltgot(obj); + for (i = 0; i < obj->nplts; i++) { + plt = &obj->plts[i]; - /* Process the PLT relocations. */ - if (reloc_plt(obj, flags, lockstate) == -1) - return (-1); - /* Relocate the jump slots if we are doing immediate binding. */ - if ((obj->bind_now || bind_now) && reloc_jmpslots(obj, flags, - lockstate) == -1) - return (-1); + /* Set the special PLT or GOT entries. */ + init_pltgot(plt); + + /* Process the PLT relocations. */ + if (reloc_plt(plt, flags, lockstate) == -1) + return (-1); + /* Relocate the jump slots if we are doing immediate binding. */ + if ((obj->bind_now || bind_now) && reloc_jmpslots(plt, flags, + lockstate) == -1) + return (-1); + } if (obj != rtldobj && !obj->mainprog && obj_enforce_relro(obj) == -1) return (-1); diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index 0da06de7b86b..0c947f68e8dc 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -165,6 +165,18 @@ typedef struct Struct_Sym_Match_Result { int vcount; } Sym_Match_Result; +typedef struct Struct_Plt_Entry { + struct Struct_Obj_Entry *obj; + + uintptr_t *pltgot; /* PLT or GOT, depending on architecture */ + const Elf_Rel *rel; /* PLT relocation entries */ + unsigned long relsize; /* Size in bytes of PLT relocation info */ + const Elf_Rela *rela; /* PLT relocation entries with addend */ + unsigned long relasize; /* Size in bytes of PLT addend reloc info */ + bool jmpslots_done : 1; /* Already have relocated the jump slots */ + MD_PLT_ENTRY; +} Plt_Entry; + #define VER_INFO_HIDDEN 0x01 /* @@ -225,17 +237,14 @@ typedef struct Struct_Obj_Entry { size_t tlspoffset; /* p_offset of the static TLS block */ /* Items from the dynamic section. */ - uintptr_t *pltgot; /* PLT or GOT, depending on architecture */ + Plt_Entry *plts; + unsigned long nplts; const Elf_Rel *rel; /* Relocation entries */ unsigned long relsize; /* Size in bytes of relocation info */ const Elf_Rela *rela; /* Relocation entries with addend */ unsigned long relasize; /* Size in bytes of addend relocation info */ const Elf_Relr *relr; /* RELR relocation entries */ unsigned long relrsize; /* Size in bytes of RELR relocations */ - const Elf_Rel *pltrel; /* PLT relocation entries */ - unsigned long pltrelsize; /* Size in bytes of PLT relocation info */ - const Elf_Rela *pltrela; /* PLT relocation entries with addend */ - unsigned long pltrelasize; /* Size in bytes of PLT addend reloc info */ const Elf_Sym *symtab; /* Symbol table */ const char *strtab; /* String table */ unsigned long strsize; /* Size in bytes of string table */ @@ -301,7 +310,6 @@ typedef struct Struct_Obj_Entry { bool deepbind : 1; /* True if loaded with RTLD_DEEPBIND" */ bool bind_now : 1; /* True if all relocations should be made first */ bool traced : 1; /* Already printed in ldd trace output */ - bool jmpslots_done : 1; /* Already have relocated the jump slots */ bool init_done : 1; /* Already have added object to init list */ bool tls_static : 1; /* Already allocated offset for static TLS */ bool tls_dynamic : 1; /* A non-static DTV entry has been allocated */ @@ -577,13 +585,13 @@ bool check_elf_headers(const Elf_Ehdr *hdr, const char *path); int do_copy_relocations(Obj_Entry *); int reloc_non_plt(Obj_Entry *, Obj_Entry *, int flags, struct Struct_RtldLockState *); -int reloc_plt(Obj_Entry *, int flags, struct Struct_RtldLockState *); -int reloc_jmpslots(Obj_Entry *, int flags, struct Struct_RtldLockState *); +int reloc_plt(Plt_Entry *, int flags, struct Struct_RtldLockState *); +int reloc_jmpslots(Plt_Entry *, int flags, struct Struct_RtldLockState *); int reloc_iresolve(Obj_Entry *, struct Struct_RtldLockState *); int reloc_iresolve_nonplt(Obj_Entry *, struct Struct_RtldLockState *); int reloc_gnu_ifunc(Obj_Entry *, int flags, struct Struct_RtldLockState *); void ifunc_init(Elf_Auxinfo *[__min_size(AT_COUNT)]); -void init_pltgot(Obj_Entry *); +void init_pltgot(Plt_Entry *); void allocate_initial_tls(Obj_Entry *); #ifdef RTLD_HAS_CAPRELOCS From 716960bdcb65e98d94c7ca2d8985975a0df4ea28 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Tue, 12 Nov 2024 17:59:45 -0500 Subject: [PATCH 04/13] elf: Add definition of PT_CHERI_BOUNDS program header type --- sys/sys/elf_common.h | 1 + 1 file changed, 1 insertion(+) diff --git a/sys/sys/elf_common.h b/sys/sys/elf_common.h index 5af4bc87c6eb..f86507264cda 100644 --- a/sys/sys/elf_common.h +++ b/sys/sys/elf_common.h @@ -533,6 +533,7 @@ typedef struct { #define PT_PHDR 6 /* Location of program header itself. */ #define PT_TLS 7 /* Thread local storage segment */ #define PT_LOOS 0x60000000 /* First OS-specific. */ +#define PT_CHERI_BOUNDS 0x64348450 /* CHERI PCC bounds. */ #define PT_SUNW_UNWIND 0x6464e550 /* amd64 UNWIND program header */ #define PT_GNU_EH_FRAME 0x6474e550 #define PT_GNU_STACK 0x6474e551 From 1a3a97265a54d25294bd2686f940d25dce393182 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Tue, 12 Nov 2024 18:00:25 -0500 Subject: [PATCH 05/13] elftoolchain: Add support for PT_CHERI_BOUNDS --- contrib/elftoolchain/common/elfdefinitions.h | 2 ++ contrib/elftoolchain/elfdump/elfdump.c | 1 + contrib/elftoolchain/readelf/readelf.c | 1 + 3 files changed, 4 insertions(+) diff --git a/contrib/elftoolchain/common/elfdefinitions.h b/contrib/elftoolchain/common/elfdefinitions.h index 358cdfc0f1d9..b7b8bf006fcb 100644 --- a/contrib/elftoolchain/common/elfdefinitions.h +++ b/contrib/elftoolchain/common/elfdefinitions.h @@ -965,6 +965,8 @@ _ELF_DEFINE_PT(PT_PHDR, 6, \ _ELF_DEFINE_PT(PT_TLS, 7, "thread local storage") \ _ELF_DEFINE_PT(PT_LOOS, 0x60000000UL, \ "start of OS-specific range") \ +_ELF_DEFINE_PT(PT_CHERI_BOUNDS, 0x64348450UL, \ + "CHERI PCC bounds") \ _ELF_DEFINE_PT(PT_SUNW_UNWIND, 0x6464E550UL, \ "Solaris/amd64 stack unwind tables") \ _ELF_DEFINE_PT(PT_GNU_EH_FRAME, 0x6474E550UL, \ diff --git a/contrib/elftoolchain/elfdump/elfdump.c b/contrib/elftoolchain/elfdump/elfdump.c index 82d0ca459f3a..ee22b5608c32 100644 --- a/contrib/elftoolchain/elfdump/elfdump.c +++ b/contrib/elftoolchain/elfdump/elfdump.c @@ -351,6 +351,7 @@ elf_phdr_type_str(unsigned int type) case PT_SHLIB: return "PT_SHLIB"; case PT_PHDR: return "PT_PHDR"; case PT_TLS: return "PT_TLS"; + case PT_CHERI_BOUNDS: return "PT_CHERI_BOUNDS"; case PT_GNU_EH_FRAME: return "PT_GNU_EH_FRAME"; case PT_GNU_STACK: return "PT_GNU_STACK"; case PT_GNU_RELRO: return "PT_GNU_RELRO"; diff --git a/contrib/elftoolchain/readelf/readelf.c b/contrib/elftoolchain/readelf/readelf.c index b77fe3663eb5..fa01a9efba81 100644 --- a/contrib/elftoolchain/readelf/readelf.c +++ b/contrib/elftoolchain/readelf/readelf.c @@ -711,6 +711,7 @@ phdr_type(unsigned int mach, unsigned int ptype) case PT_SHLIB: return "SHLIB"; case PT_PHDR: return "PHDR"; case PT_TLS: return "TLS"; + case PT_CHERI_BOUNDS: return "CHERI_BOUNDS"; case PT_GNU_EH_FRAME: return "GNU_EH_FRAME"; case PT_GNU_STACK: return "GNU_STACK"; case PT_GNU_RELRO: return "GNU_RELRO"; From 706b5509945e2e09c4f28fbd6aff58ff4b5b6c78 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Wed, 13 Nov 2024 16:01:36 -0500 Subject: [PATCH 06/13] rtld: Support narrower PCC bounds via PT_CHERI_BOUNDS If an object file includes one or more PT_CHERI_BOUNDS program headers, derive new code capabilities for each header and derive code pointers from the code capability that contains the address. This does not yet check for overlapping bounds or if the resulting PCC bounds are wider than the phdr due to precision. --- libexec/rtld-elf/aarch64/reloc.c | 34 +++++----- libexec/rtld-elf/aarch64/rtld_c18n_machdep.c | 5 +- libexec/rtld-elf/aarch64/rtld_cheri_machdep.h | 4 ++ libexec/rtld-elf/cheri/cheri_reloc.c | 6 ++ libexec/rtld-elf/map_object.c | 24 ++++--- libexec/rtld-elf/riscv/rtld_cheri_machdep.h | 4 ++ libexec/rtld-elf/rtld.c | 66 +++++++++++++++++++ libexec/rtld-elf/rtld.h | 6 ++ 8 files changed, 121 insertions(+), 28 deletions(-) diff --git a/libexec/rtld-elf/aarch64/reloc.c b/libexec/rtld-elf/aarch64/reloc.c index 924ac4fc6727..44c75e610480 100644 --- a/libexec/rtld-elf/aarch64/reloc.c +++ b/libexec/rtld-elf/aarch64/reloc.c @@ -147,7 +147,7 @@ init_pltgot(Plt_Entry *plt) */ static uintcap_t init_cap_from_fragment(const Elf_Addr *fragment, void * __capability data_cap, - const void * __capability text_rodata_cap, Elf_Addr base_addr, + const void * __capability pcc_cap, Elf_Addr base_addr, Elf_Size addend) { uintcap_t cap; @@ -159,7 +159,7 @@ init_cap_from_fragment(const Elf_Addr *fragment, void * __capability data_cap, perms = fragment[1] >> (8 * sizeof(*fragment) - 8); cap = perms == MORELLO_FRAG_EXECUTABLE ? - (uintcap_t)text_rodata_cap : (uintcap_t)data_cap; + (uintcap_t)pcc_cap : (uintcap_t)data_cap; cap = cheri_setaddress(cap, base_addr + address); cap = cheri_clearperm(cap, CAP_RELOC_REMOVE_PERMS); @@ -414,15 +414,11 @@ reloc_plt(Plt_Entry *plt, int flags, RtldLockState *lockstate) const Elf_Rela *rela; const Elf_Sym *def, *sym; #ifdef __CHERI_PURE_CAPABILITY__ - uintptr_t jump_slot_base; + const char *pcc; #endif bool lazy; relalim = (const Elf_Rela *)((const char *)plt->rela + plt->relasize); -#ifdef __CHERI_PURE_CAPABILITY__ - jump_slot_base = (uintptr_t)cheri_clearperm(obj->text_rodata_cap, - FUNC_PTR_REMOVE_PERMS); -#endif for (rela = plt->rela; rela < relalim; rela++) { uintptr_t *where, target; #ifdef __CHERI_PURE_CAPABILITY__ @@ -469,13 +465,15 @@ reloc_plt(Plt_Entry *plt, int flags, RtldLockState *lockstate) * the new ABI is old enough that we * can assume it is in use. */ + pcc = pcc_cap(obj, fragment[0]); + pcc = cheri_clearperm(pcc, + FUNC_PTR_REMOVE_PERMS); if (fragment[1] == 0) *where = cheri_sealentry( - jump_slot_base + fragment[0]); + (uintptr_t)pcc); else *where = init_cap_from_fragment( - fragment, obj->relocbase, - obj->text_rodata_cap, + fragment, obj->relocbase, pcc, (Elf_Addr)(uintptr_t)obj->relocbase, rela->r_addend); #else @@ -623,11 +621,10 @@ reloc_iresolve_one(Obj_Entry *obj, const Elf_Rela *rela, */ if ((fragment[0] == 0 && fragment[1] == 0) || (Elf_Ssize)fragment[0] == rela->r_addend) - ptr = (uintptr_t)(obj->text_rodata_cap + (rela->r_addend - - (obj->text_rodata_cap - obj->relocbase))); + ptr = (uintptr_t)pcc_cap(obj, rela->r_addend); else ptr = init_cap_from_fragment(fragment, obj->relocbase, - obj->text_rodata_cap, + pcc_cap(obj, fragment[0]), (Elf_Addr)(uintptr_t)obj->relocbase, rela->r_addend); #else @@ -802,7 +799,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, Elf_Addr *where, symval; #if __has_feature(capabilities) void * __capability data_cap; - const void * __capability text_rodata_cap; + const void * __capability code_cap; #endif #ifdef __CHERI_PURE_CAPABILITY__ @@ -816,10 +813,9 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, #ifdef __CHERI_PURE_CAPABILITY__ data_cap = obj->relocbase; - text_rodata_cap = obj->text_rodata_cap; #elif __has_feature(capabilities) data_cap = cheri_getdefault(); - text_rodata_cap = cheri_getpcc(); + code_cap = cheri_getpcc(); #endif /* @@ -908,9 +904,11 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, return (-1); break; case R_MORELLO_RELATIVE: +#ifdef __CHERI_PURE_CAPABILITY__ + code_cap = pcc_cap(obj, where[0]); +#endif *(uintcap_t *)(void *)where = - init_cap_from_fragment(where, data_cap, - text_rodata_cap, + init_cap_from_fragment(where, data_cap, code_cap, (Elf_Addr)(uintptr_t)obj->relocbase, rela->r_addend); break; diff --git a/libexec/rtld-elf/aarch64/rtld_c18n_machdep.c b/libexec/rtld-elf/aarch64/rtld_c18n_machdep.c index 729b1b5facb8..2d6ac50547a6 100644 --- a/libexec/rtld-elf/aarch64/rtld_c18n_machdep.c +++ b/libexec/rtld-elf/aarch64/rtld_c18n_machdep.c @@ -235,9 +235,12 @@ _rtld_safebox_code(void *target, struct func_sig sig) } if (sig.valid) { + const char *pcc; + + pcc = pcc_cap(obj, (const char *)target - obj->relocbase); asm ("chkssu %0, %0, %1" : "+C" (target) - : "C" (obj->text_rodata_cap) + : "C" (pcc) : "cc"); target = cheri_seal(target, sealer_tramp + func_sig_to_otype(sig)); diff --git a/libexec/rtld-elf/aarch64/rtld_cheri_machdep.h b/libexec/rtld-elf/aarch64/rtld_cheri_machdep.h index dbe6268cb614..2f33ad1c365f 100644 --- a/libexec/rtld-elf/aarch64/rtld_cheri_machdep.h +++ b/libexec/rtld-elf/aarch64/rtld_cheri_machdep.h @@ -62,7 +62,11 @@ make_code_cap(const Elf_Sym *def, const struct Struct_Obj_Entry *defobj, { const void * __capability ret; +#ifdef __CHERI_PURE_CAPABILITY__ + ret = pcc_cap(defobj, def->st_value); +#else ret = get_codesegment_cap(defobj) + def->st_value; +#endif /* Remove store and seal permissions */ ret = cheri_clearperm(ret, FUNC_PTR_REMOVE_PERMS); if (tight_bounds) { diff --git a/libexec/rtld-elf/cheri/cheri_reloc.c b/libexec/rtld-elf/cheri/cheri_reloc.c index 187d4816221f..063bc665a394 100644 --- a/libexec/rtld-elf/cheri/cheri_reloc.c +++ b/libexec/rtld-elf/cheri/cheri_reloc.c @@ -61,6 +61,12 @@ process___cap_relocs(Obj_Entry* obj) * TODO: reject those binaries and suggest relinking with the right flag */ void * __capability data_base = get_datasegment_cap(obj); +#ifdef __CHERI_PURE_CAPABILITY__ + /* + * XXX: Can't handle different code caps for individual + * cap_reloc entries. cap_relocs needs to die. + */ +#endif const void * __capability code_base = get_codesegment_cap(obj); data_base = cheri_clearperm(data_base, CAP_RELOC_REMOVE_PERMS); code_base = cheri_clearperm(code_base, CAP_RELOC_REMOVE_PERMS); diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c index 787d1859900b..7e7f20111fab 100644 --- a/libexec/rtld-elf/map_object.c +++ b/libexec/rtld-elf/map_object.c @@ -353,14 +353,6 @@ map_object(int fd, const char *path, const struct stat *sb, const char* main_pat fix_obj_mapping_cap_permissions(obj, path); #endif obj->dynamic = (const Elf_Dyn *)(obj->relocbase + phdyn->p_vaddr); - if (hdr->e_entry != 0) { -#ifdef __CHERI_PURE_CAPABILITY__ - obj->entry = (const void*)(obj->text_rodata_cap + hdr->e_entry); - dbg("\tentry for %s: %-#p", path, obj->entry); -#else - obj->entry = (const void*)(obj->relocbase + hdr->e_entry); -#endif - } if (phdr_vaddr != 0) { obj->phdr = (const Elf_Phdr *)(obj->relocbase + phdr_vaddr); } else { @@ -385,9 +377,19 @@ map_object(int fd, const char *path, const struct stat *sb, const char* main_pat obj->tlsinitsize = phtls->p_filesz; obj->tlsinit = mapbase + phtls->p_vaddr; } -#ifndef __CHERI_PURE_CAPABILITY__ +#ifdef __CHERI_PURE_CAPABILITY__ + create_pcc_caps(obj); +#else obj->stack_flags = stack_flags; #endif + if (hdr->e_entry != 0) { +#ifdef __CHERI_PURE_CAPABILITY__ + obj->entry = (const void*)pcc_cap(obj, hdr->e_entry); + dbg("\tentry for %s: %-#p", path, obj->entry); +#else + obj->entry = (const void*)(obj->relocbase + hdr->e_entry); +#endif + } if (note_start < note_end) digest_notes(obj, (const Elf_Note *)note_start, (const Elf_Note *)note_end); if (note_map != NULL) @@ -546,6 +548,10 @@ obj_free(Obj_Entry *obj) free(obj->priv); if (obj->path) free(obj->path); +#ifdef __CHERI_PURE_CAPABILITY__ + if (obj->pcc_caps) + free(obj->pcc_caps); +#endif if (obj->phdr_alloc) free(__DECONST(void *, obj->phdr)); free(obj); diff --git a/libexec/rtld-elf/riscv/rtld_cheri_machdep.h b/libexec/rtld-elf/riscv/rtld_cheri_machdep.h index e6f4f8defe22..f4949f3850a6 100644 --- a/libexec/rtld-elf/riscv/rtld_cheri_machdep.h +++ b/libexec/rtld-elf/riscv/rtld_cheri_machdep.h @@ -61,7 +61,11 @@ make_code_cap(const Elf_Sym *def, const struct Struct_Obj_Entry *defobj, { const void * __capability ret; +#ifdef __CHERI_PURE_CAPABILITY__ + ret = pcc_cap(defobj, def->st_value); +#else ret = get_codesegment_cap(defobj) + def->st_value; +#endif /* Remove store and seal permissions */ ret = cheri_clearperm(ret, FUNC_PTR_REMOVE_PERMS); if (tight_bounds) { diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 05e57f4ecbda..2205fd0b0d7a 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -900,6 +900,10 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) obj_main->text_rodata_cap, #endif obj_main->vaddrbase, obj_main->relocbase); +#ifdef __CHERI_PURE_CAPABILITY__ + for (unsigned long idx = 0; idx < obj_main->npcc_caps; idx++) + dbg("\tpcc_caps[%lu]=" PTR_FMT "\n", idx, obj_main->pcc_caps[idx]); +#endif } if (aux_info[AT_EXECPATH] != NULL && fd == -1) { @@ -1446,6 +1450,66 @@ rtld_die(void) _exit(1); } +#ifdef __CHERI_PURE_CAPABILITY__ +void +create_pcc_caps(Obj_Entry *obj) +{ + const Elf_Phdr *ph; + const char *pcc_cap; + unsigned long i; + + for (ph = obj->phdr; (const char *)ph < (const char *)obj->phdr + + obj->phsize; ph++) { + switch (ph->p_type) { + case PT_CHERI_BOUNDS: + obj->npcc_caps++; + break; + } + } + + if (obj->npcc_caps == 0) + return; + + i = 0; + obj->pcc_caps = xcalloc(obj->npcc_caps, sizeof(*obj->pcc_caps)); + for (ph = obj->phdr; (const char *)ph < (const char *)obj->phdr + + obj->phsize; ph++) { + switch (ph->p_type) { + case PT_CHERI_BOUNDS: + pcc_cap = obj->text_rodata_cap + ph->p_vaddr; + pcc_cap = cheri_setbounds(pcc_cap, ph->p_memsz); + obj->pcc_caps[i] = pcc_cap; + i++; + break; + } + } +} + +/* + * Returns a code pointer to the instruction at the relative offset + * into the mapped object. If the object includes PCC bounds via + * PT_CHERI_BOUNDS headers, the pointer uses the bounds from the + * relevant header. Otherwise the pointer uses bounds for the entire + * object. + */ +const char * +pcc_cap(const Obj_Entry *obj, Elf_Off offset) +{ + Elf_Addr addr; + + if (obj->npcc_caps == 0) + return (obj->text_rodata_cap + offset); + + addr = (Elf_Addr)(uintptr_t)obj->relocbase + offset; + for (unsigned long i = 0; i < obj->npcc_caps; i++) { + const char *pcc_cap = obj->pcc_caps[i]; + if (addr >= (ptraddr_t)pcc_cap && addr < cheri_gettop(pcc_cap)) + return (cheri_setaddress(pcc_cap, addr)); + } + return (NULL); +} +#endif + /* * Count the number of PLTs in a shared object's DYNAMIC section. */ @@ -2034,6 +2098,7 @@ digest_phdr(const Elf_Phdr *phdr, int phnum, dlfunc_t entry, const char *path) */ obj->text_rodata_cap = (const char *)cheri_copyaddress(entry, obj->relocbase); fix_obj_mapping_cap_permissions(obj, path); + create_pcc_caps(obj); #endif obj->entry = entry; @@ -2785,6 +2850,7 @@ init_rtld(caddr_t mapbase, Elf_Auxinfo **aux_info) #ifdef __CHERI_PURE_CAPABILITY__ objtmp.text_rodata_cap = objtmp.relocbase; fix_obj_mapping_cap_permissions(&objtmp, "RTLD"); + create_pcc_caps(&objtmp); #endif /* diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index 0c947f68e8dc..a658c0f2bb6e 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -216,6 +216,8 @@ typedef struct Struct_Obj_Entry { * relocbase and mapbase. */ const char *text_rodata_cap; /* Capability for the executable mapping */ + const char **pcc_caps; + unsigned long npcc_caps; #endif caddr_t relocbase; /* Relocation constant = mapbase - vaddrbase */ const Elf_Dyn *dynamic; /* Dynamic section */ @@ -494,6 +496,10 @@ extern Elf_Sym sym_zero; /* For resolving undefined weak refs. */ extern bool ld_bind_not; extern bool ld_fast_sigblock; +#ifdef __CHERI_PURE_CAPABILITY__ +void create_pcc_caps(Obj_Entry *); +const char *pcc_cap(const Obj_Entry *, Elf_Off); +#endif void dump_relocations(Obj_Entry *); void dump_obj_relocations(Obj_Entry *); void dump_Elf_Rel(Obj_Entry *, const Elf_Rel *, u_long); From a10092d96c1481175342de60b76465aa166b3225 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Mon, 6 Jan 2025 19:44:04 -0500 Subject: [PATCH 07/13] fixup! rtld: Support narrower PCC bounds via PT_CHERI_BOUNDS --- libexec/rtld-elf/map_object.c | 5 ++++- libexec/rtld-elf/rtld.c | 35 ++++++++++++++++++++++++++++++----- libexec/rtld-elf/rtld.h | 2 +- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c index 7e7f20111fab..b86c0059a281 100644 --- a/libexec/rtld-elf/map_object.c +++ b/libexec/rtld-elf/map_object.c @@ -378,7 +378,10 @@ map_object(int fd, const char *path, const struct stat *sb, const char* main_pat obj->tlsinit = mapbase + phtls->p_vaddr; } #ifdef __CHERI_PURE_CAPABILITY__ - create_pcc_caps(obj); + if (!create_pcc_caps(obj)) { + obj_free(obj); + goto error1; + } #else obj->stack_flags = stack_flags; #endif diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 2205fd0b0d7a..f54cf2098a5f 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -1451,12 +1451,12 @@ rtld_die(void) } #ifdef __CHERI_PURE_CAPABILITY__ -void +bool create_pcc_caps(Obj_Entry *obj) { const Elf_Phdr *ph; const char *pcc_cap; - unsigned long i; + unsigned long i, j; for (ph = obj->phdr; (const char *)ph < (const char *)obj->phdr + obj->phsize; ph++) { @@ -1468,7 +1468,7 @@ create_pcc_caps(Obj_Entry *obj) } if (obj->npcc_caps == 0) - return; + return (true); i = 0; obj->pcc_caps = xcalloc(obj->npcc_caps, sizeof(*obj->pcc_caps)); @@ -1483,6 +1483,29 @@ create_pcc_caps(Obj_Entry *obj) break; } } + + /* + * Require each PCC capability to be non-overlapping with + * other PCC capabilities. + */ + for (i = 1; i < obj->npcc_caps; i++) { + pcc_cap = obj->pcc_caps[i]; + for (j = 0; j < i; j++) { + if (cheri_is_address_inbounds(pcc_cap, + cheri_getbase(obj->pcc_caps[j])) || + cheri_is_address_inbounds(pcc_cap, + cheri_gettop(obj->pcc_caps[j])) || + cheri_is_address_inbounds(obj->pcc_caps[j], + cheri_getbase(pcc_cap)) || + cheri_is_address_inbounds(obj->pcc_caps[j], + cheri_gettop(pcc_cap))) { + _rtld_error("Overlapping PCC capabilities for %s", + obj->path); + return (false); + } + } + } + return (true); } /* @@ -2098,7 +2121,8 @@ digest_phdr(const Elf_Phdr *phdr, int phnum, dlfunc_t entry, const char *path) */ obj->text_rodata_cap = (const char *)cheri_copyaddress(entry, obj->relocbase); fix_obj_mapping_cap_permissions(obj, path); - create_pcc_caps(obj); + if (!create_pcc_caps(obj)) + return (NULL); #endif obj->entry = entry; @@ -2850,7 +2874,8 @@ init_rtld(caddr_t mapbase, Elf_Auxinfo **aux_info) #ifdef __CHERI_PURE_CAPABILITY__ objtmp.text_rodata_cap = objtmp.relocbase; fix_obj_mapping_cap_permissions(&objtmp, "RTLD"); - create_pcc_caps(&objtmp); + if (!create_pcc_caps(&objtmp)) + rtld_die(); #endif /* diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index a658c0f2bb6e..f333cc6bf121 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -497,7 +497,7 @@ extern bool ld_bind_not; extern bool ld_fast_sigblock; #ifdef __CHERI_PURE_CAPABILITY__ -void create_pcc_caps(Obj_Entry *); +bool create_pcc_caps(Obj_Entry *); const char *pcc_cap(const Obj_Entry *, Elf_Off); #endif void dump_relocations(Obj_Entry *); From a0e96d53776358aea58f55234b01b12ec11ab4bb Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Wed, 20 Nov 2024 17:53:37 -0500 Subject: [PATCH 08/13] rtld: Require PT_CHERI_BOUNDS to be exact Fail with an error message if the resulting PCC bounds do not match the segment's relocated address and length. --- libexec/rtld-elf/rtld.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index f54cf2098a5f..9be0b7212cc8 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -1478,6 +1478,17 @@ create_pcc_caps(Obj_Entry *obj) case PT_CHERI_BOUNDS: pcc_cap = obj->text_rodata_cap + ph->p_vaddr; pcc_cap = cheri_setbounds(pcc_cap, ph->p_memsz); + if (cheri_getbase(pcc_cap) != + cheri_getaddress(pcc_cap)) { + _rtld_error("pcc_cap %#p start is not aligned for %s", + pcc_cap, obj->path); + return (false); + } + if (cheri_getlen(pcc_cap) != ph->p_memsz) { + _rtld_error("pcc_cap %#p length is not %zu for %s", + pcc_cap, (size_t)ph->p_memsz, obj->path); + return (false); + } obj->pcc_caps[i] = pcc_cap; i++; break; From f6c3abd77736074b534f99972c24cc79c51262e5 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Thu, 5 Dec 2024 17:08:09 -0500 Subject: [PATCH 09/13] elf: Add definitions for PT_COMPARTMENT and DT_C18NSTRTAB* --- sys/sys/elf_common.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sys/sys/elf_common.h b/sys/sys/elf_common.h index f86507264cda..f39e948ba863 100644 --- a/sys/sys/elf_common.h +++ b/sys/sys/elf_common.h @@ -533,6 +533,7 @@ typedef struct { #define PT_PHDR 6 /* Location of program header itself. */ #define PT_TLS 7 /* Thread local storage segment */ #define PT_LOOS 0x60000000 /* First OS-specific. */ +#define PT_COMPARTMENT 0x64331380 /* Sub-object compartment. */ #define PT_CHERI_BOUNDS 0x64348450 /* CHERI PCC bounds. */ #define PT_SUNW_UNWIND 0x6464e550 /* amd64 UNWIND program header */ #define PT_GNU_EH_FRAME 0x6474e550 @@ -631,6 +632,8 @@ typedef struct { #define DT_SUNW_FILTER 0x6000000f /* symbol filter name */ #define DT_SUNW_CAP 0x60000010 /* hardware/software */ #define DT_SUNW_ASLR 0x60000023 /* ASLR control */ +#define DT_C18NSTRTAB 0x64331380 /* Compartment string table */ +#define DT_C18NSTRTABSZ 0x64331381 /* Compartment string table size */ #define DT_HIOS 0x6ffff000 /* Last OS-specific */ /* From d607049aa374b40e06bcf5845173dc1ffb3cad24 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Thu, 5 Dec 2024 17:08:50 -0500 Subject: [PATCH 10/13] elftoolchain: Add support for PT_COMPARTMENT and DT_C18NSTRTAB* --- contrib/elftoolchain/common/elfdefinitions.h | 6 ++++++ contrib/elftoolchain/elfdump/elfdump.c | 5 +++++ contrib/elftoolchain/readelf/readelf.c | 5 +++++ 3 files changed, 16 insertions(+) diff --git a/contrib/elftoolchain/common/elfdefinitions.h b/contrib/elftoolchain/common/elfdefinitions.h index b7b8bf006fcb..89f4cd2356a3 100644 --- a/contrib/elftoolchain/common/elfdefinitions.h +++ b/contrib/elftoolchain/common/elfdefinitions.h @@ -187,6 +187,10 @@ _ELF_DEFINE_DT(DT_SUNW_CAP, 0x60000010UL, \ "address of hardware capabilities section") \ _ELF_DEFINE_DT(DT_SUNW_ASLR, 0x60000023UL, \ "Address Space Layout Randomization flag") \ +_ELF_DEFINE_DT(DT_C18NSTRTAB, 0x64331380UL, \ + "address of compartment string table") \ +_ELF_DEFINE_DT(DT_C18NSTRTABSZ, 0x64331381UL, \ + "size of the compartment string table") \ _ELF_DEFINE_DT(DT_HIOS, 0x6FFFF000UL, \ "end of OS-specific types") \ _ELF_DEFINE_DT(DT_VALRNGLO, 0x6FFFFD00UL, \ @@ -965,6 +969,8 @@ _ELF_DEFINE_PT(PT_PHDR, 6, \ _ELF_DEFINE_PT(PT_TLS, 7, "thread local storage") \ _ELF_DEFINE_PT(PT_LOOS, 0x60000000UL, \ "start of OS-specific range") \ +_ELF_DEFINE_PT(PT_COMPARTMENT, 0x64331380UL, \ + "Sub-object compartment") \ _ELF_DEFINE_PT(PT_CHERI_BOUNDS, 0x64348450UL, \ "CHERI PCC bounds") \ _ELF_DEFINE_PT(PT_SUNW_UNWIND, 0x6464E550UL, \ diff --git a/contrib/elftoolchain/elfdump/elfdump.c b/contrib/elftoolchain/elfdump/elfdump.c index ee22b5608c32..e2eee3aa3f30 100644 --- a/contrib/elftoolchain/elfdump/elfdump.c +++ b/contrib/elftoolchain/elfdump/elfdump.c @@ -191,6 +191,8 @@ d_tags(uint64_t tag) case DT_FLAGS: return "DT_FLAGS"; case DT_PREINIT_ARRAY: return "DT_PREINIT_ARRAY"; /* XXX DT_ENCODING */ case DT_PREINIT_ARRAYSZ:return "DT_PREINIT_ARRAYSZ"; + case DT_C18NSTRTAB: return "DT_C18NSTRTAB"; + case DT_C18NSTRTABSZ: return "DT_C18NSTRTABSZ"; /* 0x6000000D - 0x6ffff000 operating system-specific semantics */ case 0x6ffffdf5: return "DT_GNU_PRELINKED"; case 0x6ffffdf6: return "DT_GNU_CONFLICTSZ"; @@ -351,6 +353,7 @@ elf_phdr_type_str(unsigned int type) case PT_SHLIB: return "PT_SHLIB"; case PT_PHDR: return "PT_PHDR"; case PT_TLS: return "PT_TLS"; + case PT_COMPARTMENT: return "PT_COMPARTMENT"; case PT_CHERI_BOUNDS: return "PT_CHERI_BOUNDS"; case PT_GNU_EH_FRAME: return "PT_GNU_EH_FRAME"; case PT_GNU_STACK: return "PT_GNU_STACK"; @@ -1667,6 +1670,7 @@ elf_print_dynamic(struct elfdump *ed) case DT_VERNEED: case DT_VERNEEDNUM: case DT_VERSYM: + case DT_C18NSTRTABSZ: if (ed->flags & SOLARIS_FMT) PRT("%#jx\n", (uintmax_t)dyn.d_un.d_val); else @@ -1683,6 +1687,7 @@ elf_print_dynamic(struct elfdump *ed) case DT_REL: case DT_JMPREL: case DT_DEBUG: + case DT_C18NSTRTAB: if (ed->flags & SOLARIS_FMT) PRT("%#jx\n", (uintmax_t)dyn.d_un.d_ptr); else diff --git a/contrib/elftoolchain/readelf/readelf.c b/contrib/elftoolchain/readelf/readelf.c index fa01a9efba81..c4eb0293c423 100644 --- a/contrib/elftoolchain/readelf/readelf.c +++ b/contrib/elftoolchain/readelf/readelf.c @@ -711,6 +711,7 @@ phdr_type(unsigned int mach, unsigned int ptype) case PT_SHLIB: return "SHLIB"; case PT_PHDR: return "PHDR"; case PT_TLS: return "TLS"; + case PT_COMPARTMENT: return "COMPARTMENT"; case PT_CHERI_BOUNDS: return "CHERI_BOUNDS"; case PT_GNU_EH_FRAME: return "GNU_EH_FRAME"; case PT_GNU_STACK: return "GNU_STACK"; @@ -886,6 +887,8 @@ dt_type(unsigned int mach, unsigned int dtype) case DT_SUNW_FILTER: return "SUNW_FILTER"; case DT_SUNW_CAP: return "SUNW_CAP"; case DT_SUNW_ASLR: return "SUNW_ASLR"; + case DT_C18NSTRTAB: return "C18NSTRTAB"; + case DT_C18NSTRTABSZ: return "C18NSTRTABSZ"; case DT_CHECKSUM: return "CHECKSUM"; case DT_PLTPADSZ: return "PLTPADSZ"; case DT_MOVEENT: return "MOVEENT"; @@ -3031,6 +3034,7 @@ dump_dyn_val(struct readelf *re, GElf_Dyn *dyn, uint32_t stab) case DT_GNU_HASH: case DT_GNU_LIBLIST: case DT_GNU_CONFLICT: + case DT_C18NSTRTAB: printf(" 0x%jx\n", (uintmax_t) dyn->d_un.d_val); break; case DT_PLTRELSZ: @@ -3045,6 +3049,7 @@ dump_dyn_val(struct readelf *re, GElf_Dyn *dyn, uint32_t stab) case DT_FINI_ARRAYSZ: case DT_GNU_CONFLICTSZ: case DT_GNU_LIBLISTSZ: + case DT_C18NSTRTABSZ: printf(" %ju (bytes)\n", (uintmax_t) dyn->d_un.d_val); break; case DT_RELACOUNT: From c6bda9859ff95642f23d1d6abc0b244a564348b8 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Fri, 6 Dec 2024 17:52:24 -0500 Subject: [PATCH 11/13] rtld: Add compartments for sub-object compartments described by PT_COMPARTMENT - Recognize the new c18n string table via DT_C18NSTRTAB* and save bounded pointers in each Obj_Entry. - Define a new Compart_Entry type to hold information about a sub-object compartment including its compartment ID, virtual address bounds, and name. The name for sub-object is ":". Currently the default compartment for an object does not have a suffix. Possibly it should. - This requires reworking compartment assignment to be more explicitly timed (always after digest_dynamic) rather than a side effect of object_add_name. Since we now always have DT_SONAME if it is present, save a pointer to DT_SONAME in Obj_Entry and prefer it for the library name for a compartment (instead of using the first name added). --- libexec/rtld-elf/map_object.c | 8 +++ libexec/rtld-elf/rtld.c | 130 ++++++++++++++++++++++++++++++++-- libexec/rtld-elf/rtld.h | 19 +++++ 3 files changed, 151 insertions(+), 6 deletions(-) diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c index b86c0059a281..c5516862f40e 100644 --- a/libexec/rtld-elf/map_object.c +++ b/libexec/rtld-elf/map_object.c @@ -552,6 +552,14 @@ obj_free(Obj_Entry *obj) if (obj->path) free(obj->path); #ifdef __CHERI_PURE_CAPABILITY__ +#ifdef CHERI_LIB_C18N + if (obj->comparts) { + for (unsigned long i = 0; i < obj->ncomparts; i++) { + free(obj->comparts[i].compart_name); + } + free(obj->comparts); + } +#endif if (obj->pcc_caps) free(obj->pcc_caps); #endif diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 9be0b7212cc8..3c2015305ce9 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -207,6 +207,9 @@ static void rtld_fill_dl_phdr_info(const Obj_Entry *obj, static uint32_t gnu_hash(const char *); static bool matched_symbol(SymLook *, const Obj_Entry *, Sym_Match_Result *, const unsigned long); +#ifdef CHERI_LIB_C18N +static bool c18n_add_obj(Obj_Entry *, const char *); +#endif void r_debug_state(struct r_debug *, struct link_map *) __noinline __exported; void _r_debug_postinit(struct link_map *) __noinline __exported; @@ -959,7 +962,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) /* * Manually register the main object after the policy is loaded. */ - object_add_name(obj_main, obj_main->path); + (void)c18n_add_obj(obj_main, obj_main->path); } #endif @@ -1673,6 +1676,14 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath, obj->sigtab = (const struct func_sig *) (obj->relocbase + dynp->d_un.d_ptr); break; + + case DT_C18NSTRTAB: + obj->c18nstrtab = (const char *)(obj->relocbase + dynp->d_un.d_ptr); + break; + + case DT_C18NSTRTABSZ: + obj->c18nstrsize = dynp->d_un.d_val; + break; #endif case DT_STRTAB: @@ -1972,8 +1983,12 @@ digest_dynamic2(Obj_Entry *obj, const Elf_Dyn *dyn_rpath, obj->rpath = (const char *)obj->strtab + dyn_rpath->d_un.d_val; obj->rpath = origin_subst(obj, obj->rpath); } - if (dyn_soname != NULL) + if (dyn_soname != NULL) { +#ifdef CHERI_LIB_C18N + obj->soname = obj->strtab + dyn_soname->d_un.d_val; +#endif object_add_name(obj, obj->strtab + dyn_soname->d_un.d_val); + } #ifdef __CHERI_PURE_CAPABILITY__ // Set tight bounds on the individual members now (for the ones that // we iterate over) instead of inheriting the relocbase bounds to avoid @@ -1985,6 +2000,9 @@ digest_dynamic2(Obj_Entry *obj, const Elf_Dyn *dyn_rpath, set_bounds_if_nonnull(plt->rel, plt->relsize); set_bounds_if_nonnull(plt->rela, plt->relasize); } +#ifdef CHERI_LIB_C18N + set_bounds_if_nonnull(obj->c18nstrtab, obj->c18nstrsize); +#endif set_bounds_if_nonnull(obj->strtab, obj->strsize); set_bounds_if_nonnull(obj->phdr, obj->phsize); @@ -3323,6 +3341,10 @@ do_load_object(int fd, const char *name, char *path, struct stat *sbp, obj->path = path; if (!digest_dynamic(obj, 0)) goto errp; +#ifdef CHERI_LIB_C18N + if (!c18n_add_obj(obj, name)) + goto errp; +#endif dbg("%s valid_hash_sysv %d valid_hash_gnu %d dynsymcount %d", obj->path, obj->valid_hash_sysv, obj->valid_hash_gnu, obj->dynsymcount); if (obj->z_pie && (flags & RTLD_LO_TRACE) == 0) { @@ -6282,6 +6304,106 @@ _rtld_free_tls(void *tcb, size_t tcbsize, size_t tcbalign) lock_release(rtld_bind_lock, &lockstate); } +#ifdef CHERI_LIB_C18N +static bool +validate_c18nstrtab(const Obj_Entry *obj) +{ + const Elf_Phdr *ph; + + if (obj->c18nstrtab != NULL) { + if (obj->c18nstrsize == 0) + return (false); + if (obj->c18nstrtab[obj->c18nstrsize - 1] != '\0') + return (false); + } + + for (ph = obj->phdr; (const char *)ph < (const char *)obj->phdr + + obj->phsize; ph++) { + switch (ph->p_type) { + case PT_COMPARTMENT: + if (obj->c18nstrtab == NULL || + ph->p_paddr >= obj->c18nstrsize) + return (false); + break; + } + } + return (true); +} + +static void +c18n_setup_compartments(Obj_Entry *obj, const char *name) +{ + Compart_Entry *compart; + const Elf_Phdr *ph; + size_t len; + + assert(obj->compart_id == 0); + obj->compart_id = compart_id_allocate(name); + + for (ph = obj->phdr; (const char *)ph < (const char *)obj->phdr + + obj->phsize; ph++) { + switch (ph->p_type) { + case PT_COMPARTMENT: + obj->ncomparts++; + break; + } + } + + if (obj->ncomparts == 0) + return; + + obj->comparts = xcalloc(obj->ncomparts, sizeof(*obj->comparts)); + compart = obj->comparts; + for (ph = obj->phdr; (const char *)ph < (const char *)obj->phdr + + obj->phsize; ph++) { + switch (ph->p_type) { + case PT_COMPARTMENT: + compart->obj = obj; + compart->name = obj->c18nstrtab + ph->p_paddr; + compart->start = (ptraddr_t)obj->relocbase + + ph->p_vaddr; + compart->end = compart->start + ph->p_memsz; + + len = strlen(name) + 1 + strlen(compart->name) + 1; + compart->compart_name = malloc(len); + rtld_snprintf(compart->compart_name, len, "%s:%s", + name, compart->name); + compart->compart_id = + compart_id_allocate(compart->compart_name); + compart++; + break; + } + } +} + +static bool +c18n_add_obj(Obj_Entry *obj, const char *name) +{ + if (!C18N_ENABLED) + return (true); + + if (!validate_c18nstrtab(obj)) + return (false); + + /* Prefer DT_SONAME as the compartment base name if present. */ + if (obj->soname != NULL) + name = obj->soname; + + /* Try to find a name if none provided. */ + if (name == NULL) { + if (!STAILQ_EMPTY(&obj->names)) + name = STAILQ_FIRST(&obj->names)->name; + else if (obj->path != NULL) + name = obj->path; + else + return (false); + } + + c18n_setup_compartments(obj, name); + return (true); +} +#endif + static void object_add_name(Obj_Entry *obj, const char *name) { @@ -6294,10 +6416,6 @@ object_add_name(Obj_Entry *obj, const char *name) if (entry != NULL) { strcpy(entry->name, name); STAILQ_INSERT_TAIL(&obj->names, entry, link); -#ifdef CHERI_LIB_C18N - if (C18N_ENABLED && obj->compart_id == 0) - obj->compart_id = compart_id_allocate(entry->name); -#endif } } diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index f333cc6bf121..680a88124b70 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -177,6 +177,18 @@ typedef struct Struct_Plt_Entry { MD_PLT_ENTRY; } Plt_Entry; +#ifdef CHERI_LIB_C18N +typedef struct Struct_Compart_Entry { + struct Struct_Obj_Entry *obj; + + const char *name; + Elf_Addr start; + Elf_Addr end; + char *compart_name; + uint16_t compart_id; +} Compart_Entry; +#endif + #define VER_INFO_HIDDEN 0x01 /* @@ -250,6 +262,12 @@ typedef struct Struct_Obj_Entry { const Elf_Sym *symtab; /* Symbol table */ const char *strtab; /* String table */ unsigned long strsize; /* Size in bytes of string table */ +#ifdef CHERI_LIB_C18N + Compart_Entry *comparts; + unsigned long ncomparts; + const char *c18nstrtab; /* Compartment string table */ + unsigned long c18nstrsize; /* Size in bytes of compartment string table */ +#endif #ifdef RTLD_HAS_CAPRELOCS caddr_t cap_relocs; /* start of the __cap_relocs section */ size_t cap_relocs_size; /* size of the __cap_relocs section */ @@ -287,6 +305,7 @@ typedef struct Struct_Obj_Entry { int vernum; /* Number of entries in vertab */ #ifdef CHERI_LIB_C18N + const char *soname; uint16_t compart_id; const struct func_sig *sigtab; #endif From 51db786798a3c5ee864274629713dded18da9577 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Mon, 9 Dec 2024 12:15:13 -0500 Subject: [PATCH 12/13] rtld_c18n: Fix copy/paste typo in _rtld_safebox_code error messages --- libexec/rtld-elf/aarch64/rtld_c18n_machdep.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libexec/rtld-elf/aarch64/rtld_c18n_machdep.c b/libexec/rtld-elf/aarch64/rtld_c18n_machdep.c index 2d6ac50547a6..d4b28f35e9f9 100644 --- a/libexec/rtld-elf/aarch64/rtld_c18n_machdep.c +++ b/libexec/rtld-elf/aarch64/rtld_c18n_machdep.c @@ -217,7 +217,7 @@ _rtld_safebox_code(void *target, struct func_sig sig) if (!func_sig_legal(sig)) { _rtld_error( - "_rtld_sandbox_code: Invalid signature " + "_rtld_safebox_code: Invalid signature " C18N_SIG_FORMAT_STRING, C18N_SIG_FORMAT(sig)); return (NULL); @@ -229,7 +229,7 @@ _rtld_safebox_code(void *target, struct func_sig sig) obj = obj_from_addr(target); if (obj == NULL) { _rtld_error( - "_rtld_sandbox_code: " + "_rtld_safebox_code: " "%#p does not belong to any object", target); return (NULL); } From f1799127c00d3a70ed0637f837395f6d60d02ec5 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Mon, 9 Dec 2024 14:12:36 -0500 Subject: [PATCH 13/13] rtld: Use compartment IDs from sub-object compartments for policy enforcement - Add a helper function to lookup the relevant compartment ID for a given virtual address and shared object. - Save a compartment ID for each PLT (based on the address of the associated PLT GOT) and use this as the "subject" (caller) for policy enforcement when handling PLT GOT relocations. - Use the target address of a function call to determine the "object" (callee). Note: rtld currently does not enforce any policy for access to data via the normal GOT. --- libexec/rtld-elf/aarch64/reloc.c | 11 +++-- libexec/rtld-elf/aarch64/rtld_c18n_machdep.c | 2 +- libexec/rtld-elf/aarch64/rtld_machdep.h | 6 ++- libexec/rtld-elf/rtld.c | 46 ++++++++++++++++---- libexec/rtld-elf/rtld.h | 3 ++ libexec/rtld-elf/rtld_c18n.c | 38 ++++++++++------ libexec/rtld-elf/rtld_c18n.h | 3 +- libexec/rtld-elf/rtld_lock.c | 2 +- 8 files changed, 81 insertions(+), 30 deletions(-) diff --git a/libexec/rtld-elf/aarch64/reloc.c b/libexec/rtld-elf/aarch64/reloc.c index 44c75e610480..03424fc21d15 100644 --- a/libexec/rtld-elf/aarch64/reloc.c +++ b/libexec/rtld-elf/aarch64/reloc.c @@ -493,6 +493,7 @@ reloc_plt(Plt_Entry *plt, int flags, RtldLockState *lockstate) defobj); #ifdef CHERI_LIB_C18N target = (uintptr_t)tramp_intern(obj, + plt->compart_id, &(struct tramp_data) { .target = (void *)target, .defobj = defobj, @@ -571,7 +572,8 @@ reloc_jmpslots(Plt_Entry *plt, int flags, RtldLockState *lockstate) } target = (uintptr_t)make_function_pointer(def, defobj); #ifdef CHERI_LIB_C18N - target = (uintptr_t)tramp_intern(obj, &(struct tramp_data) { + target = (uintptr_t)tramp_intern(obj, plt->compart_id, + &(struct tramp_data) { .target = (void *)target, .defobj = defobj, .def = def, @@ -632,7 +634,8 @@ reloc_iresolve_one(Obj_Entry *obj, const Elf_Rela *rela, #endif lock_release(rtld_bind_lock, lockstate); #ifdef CHERI_LIB_C18N - ptr = (uintptr_t)tramp_intern(NULL, &(struct tramp_data) { + ptr = (uintptr_t)tramp_intern(NULL, RTLD_COMPART_ID, + &(struct tramp_data) { .target = (void *)ptr, .defobj = obj, .sig = (struct func_sig) { .valid = true, @@ -729,7 +732,9 @@ reloc_gnu_ifunc_plt(Plt_Entry *plt, int flags, RtldLockState *lockstate) lock_release(rtld_bind_lock, lockstate); target = (uintptr_t)rtld_resolve_ifunc(defobj, def); #ifdef CHERI_LIB_C18N - target = (uintptr_t)tramp_intern(obj, &(struct tramp_data) { + target = (uintptr_t)tramp_intern(obj, + plt->compart_id, + &(struct tramp_data) { .target = (void *)target, .defobj = defobj, .def = def, diff --git a/libexec/rtld-elf/aarch64/rtld_c18n_machdep.c b/libexec/rtld-elf/aarch64/rtld_c18n_machdep.c index d4b28f35e9f9..22bcf713e1ac 100644 --- a/libexec/rtld-elf/aarch64/rtld_c18n_machdep.c +++ b/libexec/rtld-elf/aarch64/rtld_c18n_machdep.c @@ -282,7 +282,7 @@ _rtld_sandbox_code(void *target, struct func_sig sig) target = cheri_sealentry(target_unsealed); } - target = tramp_intern(NULL, &(struct tramp_data) { + target = tramp_intern(NULL, RTLD_COMPART_ID, &(struct tramp_data) { .target = target, .defobj = obj, .sig = sig diff --git a/libexec/rtld-elf/aarch64/rtld_machdep.h b/libexec/rtld-elf/aarch64/rtld_machdep.h index 1b945fec8476..b6884af2f662 100644 --- a/libexec/rtld-elf/aarch64/rtld_machdep.h +++ b/libexec/rtld-elf/aarch64/rtld_machdep.h @@ -86,7 +86,8 @@ uintptr_t reloc_jmpslot(uintptr_t *where, uintptr_t target, /* TODO: Per-function captable/PLT/FNDESC support */ #ifdef CHERI_LIB_C18N #define call_init_array_pointer(_obj, _target) \ - (((InitArrFunc)tramp_intern(NULL, &(struct tramp_data) { \ + (((InitArrFunc)tramp_intern(NULL, RTLD_COMPART_ID, \ + &(struct tramp_data) { \ .target = (void *)(_target).value, \ .defobj = _obj, \ .sig = (struct func_sig) { .valid = true, \ @@ -94,7 +95,8 @@ uintptr_t reloc_jmpslot(uintptr_t *where, uintptr_t target, }))(main_argc, main_argv, environ)) #define call_fini_array_pointer(_obj, _target) \ - (((InitFunc)tramp_intern(NULL, &(struct tramp_data) { \ + (((InitFunc)tramp_intern(NULL, RTLD_COMPART_ID, \ + &(struct tramp_data) { \ .target = (void *)(_target).value, \ .defobj = _obj, \ .sig = (struct func_sig) { .valid = true, \ diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 3c2015305ce9..08e503a23834 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -1151,7 +1151,8 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) if (rtld_exit_ptr == NULL) { rtld_exit_ptr = make_rtld_function_pointer(rtld_exit); #ifdef CHERI_LIB_C18N - rtld_exit_ptr = tramp_intern(NULL, &(struct tramp_data) { + rtld_exit_ptr = tramp_intern(NULL, RTLD_COMPART_ID, + &(struct tramp_data) { .target = rtld_exit_ptr, .defobj = &obj_rtld, .sig = (struct func_sig) { @@ -1165,7 +1166,8 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) *objp = obj_main; #ifdef CHERI_LIB_C18N - return ((func_ptr_type)tramp_intern(NULL, &(struct tramp_data) { + return ((func_ptr_type)tramp_intern(NULL, RTLD_COMPART_ID, + &(struct tramp_data) { .target = cheri_sealentry(obj_main->entry), .defobj = obj_main, .sig = (struct func_sig) { @@ -1186,7 +1188,7 @@ rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def) ptr = (void *)make_function_pointer(def, obj); #ifdef CHERI_LIB_C18N - ptr = tramp_intern(NULL, &(struct tramp_data) { + ptr = tramp_intern(NULL, RTLD_COMPART_ID, &(struct tramp_data) { .target = ptr, .defobj = obj, .def = def, @@ -1237,7 +1239,8 @@ _rtld_bind(Plt_Entry *plt, Elf_Size reloff) #ifdef __CHERI_PURE_CAPABILITY__ target = (uintptr_t)make_function_pointer(def, defobj); #ifdef CHERI_LIB_C18N - target = (uintptr_t)tramp_intern(obj, &(struct tramp_data) { + target = (uintptr_t)tramp_intern(obj, plt->compart_id, + &(struct tramp_data) { .target = (void *)target, .defobj = defobj, .def = def, @@ -3638,7 +3641,8 @@ objlist_call_init(Objlist *list, RtldLockState *lockstate) if (reg != NULL) { func_ptr_type exit_ptr = make_rtld_function_pointer(rtld_exit); #ifdef CHERI_LIB_C18N - exit_ptr = tramp_intern(NULL, &(struct tramp_data) { + exit_ptr = tramp_intern(NULL, RTLD_COMPART_ID, + &(struct tramp_data) { .target = exit_ptr, .defobj = &obj_rtld, .sig = (struct func_sig) { @@ -3651,7 +3655,8 @@ objlist_call_init(Objlist *list, RtldLockState *lockstate) reg(exit_ptr); rtld_exit_ptr = make_rtld_function_pointer(rtld_nop_exit); #ifdef CHERI_LIB_C18N - rtld_exit_ptr = tramp_intern(NULL, &(struct tramp_data) { + rtld_exit_ptr = tramp_intern(NULL, RTLD_COMPART_ID, + &(struct tramp_data) { .target = rtld_exit_ptr, .defobj = &obj_rtld, .sig = (struct func_sig) { @@ -4848,7 +4853,7 @@ dl_iterate_phdr(__dl_iterate_hdr_callback callback, void *param) error = 0; #ifdef CHERI_LIB_C18N - callback = tramp_intern(NULL, &(struct tramp_data) { + callback = tramp_intern(NULL, RTLD_COMPART_ID, &(struct tramp_data) { .target = callback, .defobj = obj_from_addr(callback), .sig = (struct func_sig) { @@ -5159,7 +5164,7 @@ get_program_var_addr(const char *name, RtldLockState *lockstate) if (ELF_ST_TYPE(req.sym_out->st_info) == STT_FUNC) { void *target = make_function_pointer(req.sym_out, req.defobj_out); #ifdef CHERI_LIB_C18N - target = tramp_intern(NULL, &(struct tramp_data) { + target = tramp_intern(NULL, RTLD_COMPART_ID, &(struct tramp_data) { .target = target, .defobj = req.defobj_out, .def = req.sym_out @@ -5169,7 +5174,7 @@ get_program_var_addr(const char *name, RtldLockState *lockstate) } else if (ELF_ST_TYPE(req.sym_out->st_info) == STT_GNU_IFUNC) { void *target = rtld_resolve_ifunc(req.defobj_out, req.sym_out); #ifdef CHERI_LIB_C18N - target = tramp_intern(NULL, &(struct tramp_data) { + target = tramp_intern(NULL, RTLD_COMPART_ID, &(struct tramp_data) { .target = target, .defobj = req.defobj_out, .def = req.sym_out @@ -6376,6 +6381,28 @@ c18n_setup_compartments(Obj_Entry *obj, const char *name) } } +compart_id_t +compart_id_for_address(const Obj_Entry *obj, Elf_Addr addr) +{ + assert(cheri_is_address_inbounds(obj->relocbase, addr)); + + for (unsigned long i = 0; i < obj->ncomparts; i++) { + if (addr >= obj->comparts[i].start && + addr < obj->comparts[i].end) + return (obj->comparts[i].compart_id); + } + return (obj->compart_id); +} + +static void +c18n_assign_plt_compartments(Obj_Entry *obj) +{ + for (unsigned long i = 0; i < obj->nplts; i++) { + obj->plts[i].compart_id = compart_id_for_address(obj, + (ptraddr_t)obj->plts[i].pltgot); + } +} + static bool c18n_add_obj(Obj_Entry *obj, const char *name) { @@ -6400,6 +6427,7 @@ c18n_add_obj(Obj_Entry *obj, const char *name) } c18n_setup_compartments(obj, name); + c18n_assign_plt_compartments(obj); return (true); } #endif diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index 680a88124b70..167f70cec2c0 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -174,6 +174,9 @@ typedef struct Struct_Plt_Entry { const Elf_Rela *rela; /* PLT relocation entries with addend */ unsigned long relasize; /* Size in bytes of PLT addend reloc info */ bool jmpslots_done : 1; /* Already have relocated the jump slots */ +#ifdef CHERI_LIB_C18N + uint16_t compart_id; +#endif MD_PLT_ENTRY; } Plt_Entry; diff --git a/libexec/rtld-elf/rtld_c18n.c b/libexec/rtld-elf/rtld_c18n.c index a698ebbed9ef..3302911abb13 100644 --- a/libexec/rtld-elf/rtld_c18n.c +++ b/libexec/rtld-elf/rtld_c18n.c @@ -546,8 +546,10 @@ evaluate_rules(compart_id_t caller, compart_id_t callee, const char *sym) } static bool -tramp_should_include(const Obj_Entry *reqobj, const struct tramp_data *data) +tramp_should_include(const Obj_Entry *reqobj, compart_id_t caller, + const struct tramp_data *data) { + compart_id_t callee; const char *sym; if (data->def == NULL) @@ -561,23 +563,28 @@ tramp_should_include(const Obj_Entry *reqobj, const struct tramp_data *data) if (string_base_search(&uni_compart.trusts, sym) != -1) return (false); - if (reqobj == NULL) + if (reqobj == NULL) { + assert(caller == RTLD_COMPART_ID); return (true); + } - if (reqobj->compart_id == data->defobj->compart_id) + callee = compart_id_for_address(data->defobj, + (ptraddr_t)data->defobj->relocbase + data->def->st_value); + + if (caller == callee) return (false); - if (string_base_search(&comparts.data[reqobj->compart_id].trusts, sym) + if (string_base_search(&comparts.data[caller].trusts, sym) != -1) return (false); - if (evaluate_rules(reqobj->compart_id, data->defobj->compart_id, sym)) + if (evaluate_rules(caller, callee, sym)) return (true); rtld_fatal("c18n: Policy violation: %s is not allowed to access symbol " "%s defined by %s", - comparts.data[reqobj->compart_id].name, sym, - comparts.data[data->defobj->compart_id].name); + comparts.data[caller].name, sym, + comparts.data[callee].name); } /* @@ -1375,7 +1382,8 @@ tramp_make_entry(const struct tramp_header *header) } void * -tramp_intern(const Obj_Entry *reqobj, const struct tramp_data *data) +tramp_intern(const Obj_Entry *reqobj, compart_id_t caller, + const struct tramp_data *data) { RtldLockState lockstate; const struct tramp_header *header; @@ -1411,7 +1419,7 @@ tramp_intern(const Obj_Entry *reqobj, const struct tramp_data *data) data->defobj->dynsymcount); assert(func_sig_legal(data->sig)); - if (!tramp_should_include(reqobj, data)) + if (!tramp_should_include(reqobj, caller, data)) return (data->target); start: @@ -1720,7 +1728,8 @@ c18n_init2(Obj_Entry *obj_rtld) * XXX: Manually wrap _rtld_unw_setcontext_impl in a trampoline for now * because it is called via a function pointer. */ - _rtld_unw_setcontext_ptr = tramp_intern(NULL, &(struct tramp_data) { + _rtld_unw_setcontext_ptr = tramp_intern(NULL, RTLD_COMPART_ID, + &(struct tramp_data) { .target = &_rtld_unw_setcontext_impl, .defobj = obj_rtld, .sig = (struct func_sig) { @@ -1744,7 +1753,8 @@ _rtld_thread_start_init(void (*p)(struct pthread *)) { assert((cheri_getperm(p) & CHERI_PERM_EXECUTIVE) == 0); assert(thr_thread_start == NULL); - thr_thread_start = tramp_intern(NULL, &(struct tramp_data) { + thr_thread_start = tramp_intern(NULL, RTLD_COMPART_ID, + &(struct tramp_data) { .target = p, .defobj = obj_from_addr(p), .sig = (struct func_sig) { @@ -1888,7 +1898,8 @@ _rtld_sighandler_init(__siginfohandler_t *handler) { assert((cheri_getperm(handler) & CHERI_PERM_EXECUTIVE) == 0); assert(signal_dispatcher == sigdispatch); - signal_dispatcher = tramp_intern(NULL, &(struct tramp_data) { + signal_dispatcher = tramp_intern(NULL, RTLD_COMPART_ID, + &(struct tramp_data) { .target = handler, .defobj = obj_from_addr(handler), .sig = (struct func_sig) { @@ -2126,7 +2137,8 @@ _rtld_siginvoke(int sig, siginfo_t *info, ucontext_t *ucp, header = tramp_reflect(sigfunc); if (header == NULL) { defobj = obj_from_addr(sigfunc); - sigfunc = tramp_intern(NULL, &(struct tramp_data) { + sigfunc = tramp_intern(NULL, RTLD_COMPART_ID, + &(struct tramp_data) { .target = sigfunc, .defobj = defobj }); diff --git a/libexec/rtld-elf/rtld_c18n.h b/libexec/rtld-elf/rtld_c18n.h index 6a4c5bf3829a..97c7a8b82050 100644 --- a/libexec/rtld-elf/rtld_c18n.h +++ b/libexec/rtld-elf/rtld_c18n.h @@ -61,6 +61,7 @@ typedef uint16_t compart_id_t; typedef struct { uint16_t val; } stk_table_index; compart_id_t compart_id_allocate(const char *); +compart_id_t compart_id_for_address(const Obj_Entry *, Elf_Addr); /* * Stack switching @@ -244,7 +245,7 @@ void tramp_hook(void); size_t tramp_compile(char **, const struct tramp_data *); -void *tramp_intern(const Obj_Entry *reqobj, const struct tramp_data *); +void *tramp_intern(const Obj_Entry *, compart_id_t, const struct tramp_data *); struct tramp_header *tramp_reflect(const void *); struct func_sig sigtab_get(const Obj_Entry *, unsigned long); diff --git a/libexec/rtld-elf/rtld_lock.c b/libexec/rtld-elf/rtld_lock.c index 7194cdf0b575..f75f4e2f1763 100644 --- a/libexec/rtld-elf/rtld_lock.c +++ b/libexec/rtld-elf/rtld_lock.c @@ -428,7 +428,7 @@ _rtld_thread_init(struct RtldLockInfo *pli) #ifdef CHERI_LIB_C18N tmplockinfo = *pli; #define WRAP(_target, _valid, _reg_args, _mem_args, _ret_args) \ - _target = tramp_intern(NULL, &(struct tramp_data) { \ + _target = tramp_intern(NULL, RTLD_COMPART_ID, &(struct tramp_data) { \ .target = _target, \ .defobj = obj, \ .sig = (struct func_sig) { \