git: d51fa0a9b11d - main - rtld: Add support for arm64 variant pcs

From: Andrew Turner <andrew_at_FreeBSD.org>
Date: Fri, 17 May 2024 10:21:51 UTC
The branch main has been updated by andrew:

URL: https://cgit.FreeBSD.org/src/commit/?id=d51fa0a9b11db524a8e872c0019e54846a03d8a9

commit d51fa0a9b11db524a8e872c0019e54846a03d8a9
Author:     Andrew Turner <andrew@FreeBSD.org>
AuthorDate: 2024-04-17 15:29:29 +0000
Commit:     Andrew Turner <andrew@FreeBSD.org>
CommitDate: 2024-05-17 09:37:23 +0000

    rtld: Add support for arm64 variant pcs
    
    The aarch64 ELF spec has support for a variant of the normal procedure
    call standard that doesn't follow the normal register convention, e.g.
    using more registers as arguments, or different register state is
    preserved.
    
    Add support to rtld to handle this. As we don't know which registers
    need to be preserved disable lazy binding for these functions.
    
    Reviewed by:    kib
    Sponsored by:   Arm Ltd
    Differential Revision:  https://reviews.freebsd.org/D44869
---
 libexec/rtld-elf/aarch64/reloc.c        | 50 +++++++++++++++++++++++++++++++--
 libexec/rtld-elf/aarch64/rtld_machdep.h |  6 ++--
 2 files changed, 51 insertions(+), 5 deletions(-)

diff --git a/libexec/rtld-elf/aarch64/reloc.c b/libexec/rtld-elf/aarch64/reloc.c
index d73982e26b76..78e2e2b1aaae 100644
--- a/libexec/rtld-elf/aarch64/reloc.c
+++ b/libexec/rtld-elf/aarch64/reloc.c
@@ -54,6 +54,17 @@ void *_rtld_tlsdesc_dynamic(void *);
 
 void _exit(int);
 
+bool
+arch_digest_dynamic(struct Struct_Obj_Entry *obj, const Elf_Dyn *dynp)
+{
+	if (dynp->d_tag == DT_AARCH64_VARIANT_PCS) {
+		obj->variant_pcs = true;
+		return (true);
+	}
+
+	return (false);
+}
+
 bool
 arch_digest_note(struct Struct_Obj_Entry *obj __unused, const Elf_Note *note)
 {
@@ -228,19 +239,54 @@ reloc_tlsdesc(const Obj_Entry *obj, const Elf_Rela *rela, Elf_Addr *where,
 int
 reloc_plt(Obj_Entry *obj, int flags, RtldLockState *lockstate)
 {
+	const Obj_Entry *defobj;
 	const Elf_Rela *relalim;
 	const Elf_Rela *rela;
+	const Elf_Sym *def, *sym;
+	bool lazy;
 
 	relalim = (const Elf_Rela *)((const char *)obj->pltrela +
 	    obj->pltrelasize);
 	for (rela = obj->pltrela; rela < relalim; rela++) {
-		Elf_Addr *where;
+		Elf_Addr *where, target;
 
 		where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
 
 		switch(ELF_R_TYPE(rela->r_info)) {
 		case R_AARCH64_JUMP_SLOT:
-			*where += (Elf_Addr)obj->relocbase;
+			lazy = true;
+			if (obj->variant_pcs) {
+				sym = &obj->symtab[ELF_R_SYM(rela->r_info)];
+				/*
+				 * Variant PCS functions don't follow the
+				 * standard register convention. Because of
+				 * this we can't use lazy relocation and
+				 * need to set the target address.
+				 */
+				if ((sym->st_other & STO_AARCH64_VARIANT_PCS) !=
+				    0)
+					lazy = false;
+			}
+			if (lazy) {
+				*where += (Elf_Addr)obj->relocbase;
+			} else {
+				def = find_symdef(ELF_R_SYM(rela->r_info), obj,
+				    &defobj, SYMLOOK_IN_PLT | flags, NULL,
+				    lockstate);
+				if (def == NULL)
+					return (-1);
+				if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC){
+					obj->gnu_ifunc = true;
+					continue;
+				}
+				target = (Elf_Addr)(defobj->relocbase +
+				    def->st_value);
+				/*
+				 * Ignore ld_bind_not as it requires lazy
+				 * binding
+				 */
+				*where = target;
+			}
 			break;
 		case R_AARCH64_TLSDESC:
 			reloc_tlsdesc(obj, rela, where, SYMLOOK_IN_PLT | flags,
diff --git a/libexec/rtld-elf/aarch64/rtld_machdep.h b/libexec/rtld-elf/aarch64/rtld_machdep.h
index 347ef5f381fe..3cc1339fcad4 100644
--- a/libexec/rtld-elf/aarch64/rtld_machdep.h
+++ b/libexec/rtld-elf/aarch64/rtld_machdep.h
@@ -37,7 +37,8 @@
 
 struct Struct_Obj_Entry;
 
-#define	MD_OBJ_ENTRY
+#define	MD_OBJ_ENTRY	\
+    bool variant_pcs : 1;	/* Object has a variant pcs function */
 
 /* Return the address of the .dynamic section in the dynamic linker. */
 #define	rtld_dynamic(obj)						\
@@ -47,8 +48,7 @@ struct Struct_Obj_Entry;
 	(const Elf_Dyn *)_dynamic_addr;					\
 })
 
-/* No arch-specific dynamic tags */
-#define	arch_digest_dynamic(obj, dynp)	false
+bool arch_digest_dynamic(struct Struct_Obj_Entry *obj, const Elf_Dyn *dynp);
 
 bool arch_digest_note(struct Struct_Obj_Entry *obj, const Elf_Note *note);