diff --git a/lib/csu/aarch64/Makefile b/lib/csu/aarch64/Makefile index 888ca993de30..b1a139bbb3cc 100644 --- a/lib/csu/aarch64/Makefile +++ b/lib/csu/aarch64/Makefile @@ -6,6 +6,8 @@ CFLAGS+= -I${.CURDIR} .if ${MACHINE_CPU:Mcheri} CFLAGS+= -I${.CURDIR:H}/common-cheri + +NO_WCAST_ALIGN= .endif CRT1SRC= crt1_s.S diff --git a/lib/csu/aarch64/caprel.h b/lib/csu/aarch64/caprel.h new file mode 100644 index 000000000000..04710079dbd4 --- /dev/null +++ b/lib/csu/aarch64/caprel.h @@ -0,0 +1,93 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2024 SRI International + * + * This software was developed by SRI International, the University of + * Cambridge Computer Laboratory (Department of Computer Science and + * Technology), and Capabilities Limited under Defense Advanced Research + * Projects Agency (DARPA) Contract No. FA8750-24-C-B047 ("DEC"). + */ + +#ifndef __CAPREL_H__ +#define __CAPREL_H__ + +#include +#include + +#include + +#define FUNC_PTR_REMOVE_PERMS \ + (CHERI_PERM_SEAL | CHERI_PERM_STORE | CHERI_PERM_STORE_CAP | \ + CHERI_PERM_STORE_LOCAL_CAP) + +#define DATA_PTR_REMOVE_PERMS \ + (CHERI_PERM_SEAL | CHERI_PERM_EXECUTE) + +#define CAP_RELOC_REMOVE_PERMS \ + (CHERI_PERM_SW_VMEM) + +/* + * Fragments consist of a 64-bit address followed by a 56-bit length and an + * 8-bit permission field. + */ +static __always_inline uintcap_t +init_cap_from_fragment(const Elf_Addr *fragment, void * __capability data_cap, + const void * __capability text_rodata_cap, Elf_Addr base_addr, + Elf_Size addend) +{ + uintcap_t cap; + Elf_Addr address, len; + uint8_t perms; + + address = fragment[0]; + len = fragment[1] & ((1UL << (8 * sizeof(*fragment) - 8)) - 1); + perms = fragment[1] >> (8 * sizeof(*fragment) - 8); + + cap = perms == MORELLO_FRAG_EXECUTABLE ? + (uintcap_t)text_rodata_cap : (uintcap_t)data_cap; + cap = cheri_setaddress(cap, base_addr + address); + cap = cheri_clearperm(cap, CAP_RELOC_REMOVE_PERMS); + + if (perms == MORELLO_FRAG_EXECUTABLE || perms == MORELLO_FRAG_RODATA) { + cap = cheri_clearperm(cap, FUNC_PTR_REMOVE_PERMS); + } + if (perms == MORELLO_FRAG_RWDATA || perms == MORELLO_FRAG_RODATA) { + cap = cheri_clearperm(cap, DATA_PTR_REMOVE_PERMS); + cap = cheri_setbounds(cap, len); + } + + cap += addend; + + if (perms == MORELLO_FRAG_EXECUTABLE) { + /* + * TODO tight bounds: lower bound and len should be set + * with LSB == 0 for C64 code. + */ + cap = cheri_sealentry(cap); + } + + return (cap); +} + +static __always_inline void +elf_reloc(const Elf_Rela *rela, void * __capability data_cap, + const void * __capability code_cap, Elf_Addr relocbase) +{ + Elf_Addr addr; + Elf_Addr *where; + + if (ELF_R_TYPE(rela->r_info) != R_MORELLO_RELATIVE) + __builtin_trap(); + + addr = relocbase + rela->r_offset; +#ifdef __CHERI_PURE_CAPABILITY__ + where = cheri_setaddress(data_cap, addr); +#else + where = (Elf_Addr *)addr; +#endif + *(uintcap_t *)(void *)where = init_cap_from_fragment(where, data_cap, + code_cap, relocbase, rela->r_addend); +} + +#endif /* __CAPREL_H__ */ diff --git a/lib/csu/aarch64/crt1_c.c b/lib/csu/aarch64/crt1_c.c index dc7a2ac25044..dbfdfaaf754e 100644 --- a/lib/csu/aarch64/crt1_c.c +++ b/lib/csu/aarch64/crt1_c.c @@ -38,9 +38,15 @@ * need to include the code here. */ #if __has_feature(capabilities) && !defined(PIC) +#define CHERI_INIT_RELA + extern int _DYNAMIC; #pragma weak _DYNAMIC +#define RODATA_PTR(x) &x + +#include "caprel.h" + #include "crt_init_globals.c" void __process_cap_relocs(char *env[]); diff --git a/lib/csu/aarch64c/Makefile b/lib/csu/aarch64c/Makefile index 8c8336bdb14c..5b766e8554b5 100644 --- a/lib/csu/aarch64c/Makefile +++ b/lib/csu/aarch64c/Makefile @@ -2,6 +2,8 @@ .PATH: ${.CURDIR}/../common-cheri ${.CURDIR}/../common -CFLAGS+= -I${.CURDIR} +CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../aarch64 + +NO_WCAST_ALIGN= .include diff --git a/lib/csu/aarch64c/crt1_c.c b/lib/csu/aarch64c/crt1_c.c index ca3cf8d39ee3..0598f089a403 100644 --- a/lib/csu/aarch64c/crt1_c.c +++ b/lib/csu/aarch64c/crt1_c.c @@ -44,6 +44,19 @@ * to include the code here. */ #ifndef PIC +#define CHERI_INIT_RELA + +#define RODATA_PTR(x) ({ \ + __typeof__(x) *_p; \ + \ + __asm__ ( \ + "adrp %0, " __STRING(x) "\n\t" \ + "add %0, %0, :lo12:" __STRING(x) "\n\t" \ + : "=C" (_p)); \ + _p; }) + +#include "caprel.h" + #include "crt_init_globals.c" #endif diff --git a/lib/csu/common-cheri/crt_init_globals.c b/lib/csu/common-cheri/crt_init_globals.c index e684a50a6d3f..ccc678ef1cb5 100644 --- a/lib/csu/common-cheri/crt_init_globals.c +++ b/lib/csu/common-cheri/crt_init_globals.c @@ -39,6 +39,34 @@ #error "PIEs never need to initialise their own globals" #endif +#ifdef CHERI_INIT_RELA +extern const Elf_Rela __weak_symbol __rela_dyn_start __hidden; +extern const Elf_Rela __weak_symbol __rela_dyn_end __hidden; + +static __always_inline void +crt_init_rela(const Elf_Phdr *phdr __unused) +{ + const Elf_Rela *rela, *relalim; + void * __capability data_cap; + const void * __capability code_cap; + +#ifdef __CHERI_PURE_CAPABILITY__ + data_cap = __DECONST(void *, phdr); +#else + data_cap = cheri_getdefault(); +#endif + data_cap = cheri_clearperm(data_cap, + CHERI_PERM_EXECUTE | CHERI_PERM_SW_VMEM); + + code_cap = cheri_getpcc(); + + rela = RODATA_PTR(__rela_dyn_start); + relalim = RODATA_PTR(__rela_dyn_end); + for (; rela < relalim; rela++) + elf_reloc(rela, data_cap, code_cap, 0); +} +#endif + #define CHERI_INIT_GLOBALS_GDC_ONLY #include #if !defined(CHERI_INIT_GLOBALS_VERSION) || CHERI_INIT_GLOBALS_VERSION < 4 @@ -66,6 +94,10 @@ crt_init_globals(const Elf_Phdr *phdr, long phnum, const void * __capability code_cap; const void * __capability rodata_cap; +#ifdef CHERI_INIT_RELA + crt_init_rela(phdr); +#endif + /* Attempt to bound the data capability to only the writable segment */ for (const Elf_Phdr *ph = phdr; ph < phlimit; ph++) { if (ph->p_type != PT_LOAD && ph->p_type != PT_GNU_RELRO) { diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 918dcb785113..f5265371a9cd 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -1485,6 +1485,11 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath, break; case DT_JMPREL: +#ifdef __aarch64__ + /* Ignore empty PLT entries for Morello. */ + if (dynp->d_un.d_ptr == 0) + break; +#endif obj->pltrel = (const Elf_Rel *) (obj->relocbase + dynp->d_un.d_ptr); break; @@ -1643,6 +1648,11 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath, break; case DT_PLTGOT: +#ifdef __aarch64__ + /* Ignore empty PLT entries for Morello. */ + if (dynp->d_un.d_ptr == 0) + break; +#endif obj->pltgot = (uintptr_t *)(obj->relocbase + dynp->d_un.d_ptr); break; diff --git a/share/mk/bsd.lib.mk b/share/mk/bsd.lib.mk index 18b6ad8b04e1..8e36dec62926 100644 --- a/share/mk/bsd.lib.mk +++ b/share/mk/bsd.lib.mk @@ -112,6 +112,10 @@ LDFLAGS+= -Wl,-zbti-report=error .endif .endif +.if ${MACHINE_CPUARCH} == "aarch64" && ${MACHINE_CPU:Mcheri} +LDFLAGS+= -Wl,--local-caprelocs=elf +.endif + # Initialize stack variables on function entry .if ${OPT_INIT_ALL} != "none" .if ${COMPILER_FEATURES:Minit-all} diff --git a/share/mk/bsd.prog.mk b/share/mk/bsd.prog.mk index 028e4bbfe882..30eff9cb45f1 100644 --- a/share/mk/bsd.prog.mk +++ b/share/mk/bsd.prog.mk @@ -84,6 +84,10 @@ LDFLAGS+= -Wl,-zbti-report=error .endif .endif +.if ${MACHINE_CPUARCH} == "aarch64" && ${MACHINE_CPU:Mcheri} +LDFLAGS+= -Wl,--local-caprelocs=elf +.endif + # Initialize stack variables on function entry .if ${OPT_INIT_ALL} != "none" .if ${COMPILER_FEATURES:Minit-all}