From 978eb3b87bca0837d52d096c343fc70b06d9a04d Mon Sep 17 00:00:00 2001 From: Adhemerval Zanella Date: Thu, 10 Dec 2020 14:06:49 +0000 Subject: [PATCH] [lld] [ELF] AArch64: Handle DT_AARCH64_VARIANT_PCS As indicated by AArch64 ELF specification, symbols with st_other marked with STO_AARCH64_VARIANT_PCS indicates it may follow a variant procedure call standard with different register usage convention (for instance SVE calls). Static linkers must preserve the marking and propagate it to the dynamic symbol table if any reference or definition of the symbol is marked with STO_AARCH64_VARIANT_PCS, and add a DT_AARCH64_VARIANT_PCS dynamic tag if there are R__JUMP_SLOT relocations that reference that symbols. It implements https://bugs.llvm.org/show_bug.cgi?id=48368. Reviewed By: MaskRay Differential Revision: https://reviews.llvm.org/D93045 --- lld/ELF/SyntheticSections.cpp | 11 +++ lld/test/ELF/aarch64-variant_pcs.s | 130 +++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 lld/test/ELF/aarch64-variant_pcs.s diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index eccd3ef1795e..9b5fb3f26c59 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -1436,6 +1436,13 @@ template void DynamicSection::finalizeContents() { case EM_SPARCV9: addInSec(DT_PLTGOT, in.plt); break; + case EM_AARCH64: + if (llvm::find_if(in.relaPlt->relocs, [](const DynamicReloc &r) { + return r.type == target->pltRel && + r.sym->stOther & STO_AARCH64_VARIANT_PCS; + }) != in.relaPlt->relocs.end()) + addInt(DT_AARCH64_VARIANT_PCS, 0); + LLVM_FALLTHROUGH; default: addInSec(DT_PLTGOT, in.gotPlt); break; @@ -2181,6 +2188,10 @@ template void SymbolTableSection::writeTo(uint8_t *buf) { // See getPPC64GlobalEntryToLocalEntryOffset() for more details. if (config->emachine == EM_PPC64) eSym->st_other |= sym->stOther & 0xe0; + // The most significant bit of st_other is used by AArch64 ABI for the + // variant PCS. + else if (config->emachine == EM_AARCH64) + eSym->st_other |= sym->stOther & STO_AARCH64_VARIANT_PCS; eSym->st_name = ent.strTabOffset; if (isDefinedHere) diff --git a/lld/test/ELF/aarch64-variant_pcs.s b/lld/test/ELF/aarch64-variant_pcs.s new file mode 100644 index 000000000000..b7f1efc16d6c --- /dev/null +++ b/lld/test/ELF/aarch64-variant_pcs.s @@ -0,0 +1,130 @@ +# REQUIRES: aarch64 +# RUN: split-file %s %t + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %t/test1 -o %t.o +# RUN: ld.lld %t.o --shared -o %t.so +# RUN: llvm-readelf --dynamic-table %t.so | FileCheck --check-prefix T1-PCSDYN %s +# RUN: llvm-readelf --symbols %t.so | FileCheck --check-prefix T1-PCSSYM %s + +# T1-PCSDYN-NOT: 0x0000000070000005 (AARCH64_VARIANT_PCS) 0 +# T1-PCSSYM: Symbol table '.dynsym' +# T1-PCSSYM: 0 NOTYPE GLOBAL DEFAULT [VARIANT_PCS] [[#]] pcs_func_global_def + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %t/test2 -o %t.o +# RUN: ld.lld %t.o --shared -o %t.so +# RUN: llvm-readelf --dynamic-table %t.so | FileCheck --check-prefix T2-PCSDYN %s +# RUN: llvm-readelf --symbols %t.so | FileCheck --check-prefix T2-PCSSYM %s + +# T2-PCSDYN: 0x0000000070000005 (AARCH64_VARIANT_PCS) 0 +# T2-PCSSYM: Symbol table '.dynsym' +# T2-PCSSYM: 0 NOTYPE GLOBAL DEFAULT [VARIANT_PCS] [[#]] pcs_func_global_def + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %t/test3 -o %t.o +# RUN: ld.lld %t.o --shared -o %t.so +# RUN: llvm-readelf --dynamic-table %t.so | FileCheck --check-prefix T3-PCSDYN %s +# RUN: llvm-readelf --symbols %t.so | FileCheck --check-prefix T3-PCSSYM %s + +# T3-PCSDYN: 0x0000000070000005 (AARCH64_VARIANT_PCS) 0 +# T3-PCSSYM: Symbol table '.dynsym' +# T3-PCSSYM: 0 IFUNC GLOBAL DEFAULT [VARIANT_PCS] UND pcs_ifunc_global_def +# T3-PCSSYM: 0 NOTYPE GLOBAL DEFAULT [[#]] pcs_func_global_def + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %t/test4 -o %t.o +# RUN: ld.lld %t.o --shared -o %t.so +# RUN: llvm-readelf --dynamic-table %t.so | FileCheck --check-prefix T4-PCSDYN %s +# RUN: llvm-readelf --symbols %t.so | FileCheck --check-prefix T4-PCSSYM %s + +# T4-PCSDYN-NOT: 0x0000000070000005 (AARCH64_VARIANT_PCS) 0 +# T4-PCSSYM: Symbol table '.dynsym' +# T4-PCSSYM: 0 IFUNC GLOBAL DEFAULT [VARIANT_PCS] [[#]] pcs_ifunc_global_def + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %t/test5 -o %t.o +# RUN: ld.lld %t.o --shared -o %t.so +# RUN: llvm-readelf --symbols %t.so | FileCheck --check-prefix T5-PCSSYM %s + +# T5-PCSSYM: Symbol table '.dynsym' +# T5-PCSSYM: 0 NOTYPE GLOBAL DEFAULT [VARIANT_PCS] UND pcs_func_global_undef +# T5-PCSSYM-NEXT: 0 NOTYPE GLOBAL DEFAULT [VARIANT_PCS] [[#]] pcs_func_global_def +# T5-PCSSYM-NEXT: 0 IFUNC GLOBAL DEFAULT [VARIANT_PCS] [[#]] pcs_ifunc_global_def +# T5-PCSSYM: Symbol table '.symtab' contains 10 entries: +# T5-PCSSYM: 0 NOTYPE LOCAL DEFAULT [VARIANT_PCS] [[#]] pcs_func_local +# T5-PCSSYM-NEXT: 0 IFUNC LOCAL DEFAULT [VARIANT_PCS] [[#]] pcs_ifunc_local +# T5-PCSSYM-NEXT: 0 NOTYPE LOCAL HIDDEN [VARIANT_PCS] [[#]] pcs_func_global_hidden +# T5-PCSSYM-NEXT: 0 IFUNC LOCAL HIDDEN [VARIANT_PCS] [[#]] pcs_ifunc_global_hidden +# T5-PCSSYM: 0 NOTYPE GLOBAL DEFAULT [VARIANT_PCS] [[#]] pcs_func_global_def +# T5-PCSSYM-NEXT: 0 NOTYPE GLOBAL DEFAULT [VARIANT_PCS] UND pcs_func_global_undef +# T5-PCSSYM-NEXT: 0 IFUNC GLOBAL DEFAULT [VARIANT_PCS] [[#]] pcs_ifunc_global_def + + +#--- test1 +## An object with a variant_pcs symbol but without a R_AARCH64_JMP_SLOT +## should not generate a DT_AARCH64_VARIANT_PCS. +.text +.global pcs_func_global_def +.variant_pcs pcs_func_global_def + +pcs_func_global_def: + ret + +#--- test2 +## An object with a variant_pcs symbol and with a R_AARCH64_JMP_SLOT +## should generate a DT_AARCH64_VARIANT_PCS. +.text +.global pcs_func_global_def +.variant_pcs pcs_func_global_def + +pcs_func_global_def: + bl pcs_func_global_def + +#--- test3 +## Same as before, but targeting a GNU IFUNC. +.text +.global pcs_ifunc_global_def +.global pcs_func_global_def +.variant_pcs pcs_ifunc_global_def +.type pcs_ifunc_global_def, %gnu_indirect_function + +pcs_func_global_def: + bl pcs_ifunc_global_def + +#--- test4 +## An object with a variant_pcs symbol and with a R_AARCH64_IRELATIVE +## should not generate a DT_AARCH64_VARIANT_PCS. +.text +.global pcs_ifunc_global_def +.global pcs_func_global_def +.variant_pcs pcs_ifunc_global_def +.type pcs_ifunc_global_def, %gnu_indirect_function + +pcs_ifunc_global_def: + bl pcs_func_global_def + +#--- test5 +## Check if STO_AARCH64_VARIANT_PCS is kept on symbol st_other for both undef, +## local, and hidden visibility. +.text +.global pcs_func_global_def, pcs_func_global_undef, pcs_func_global_hidden +.global pcs_ifunc_global_def, pcs_ifunc_global_hidden +.local pcs_func_local + +.hidden pcs_func_global_hidden, pcs_ifunc_global_hidden + +.type pcs_ifunc_global_def, %gnu_indirect_function +.type pcs_ifunc_global_hidden, %gnu_indirect_function +.type pcs_ifunc_local, %gnu_indirect_function + +.variant_pcs pcs_func_global_def +.variant_pcs pcs_func_global_undef +.variant_pcs pcs_func_global_hidden +.variant_pcs pcs_func_local +.variant_pcs pcs_ifunc_global_def +.variant_pcs pcs_ifunc_global_hidden +.variant_pcs pcs_ifunc_local + +pcs_func_global_def: +pcs_func_global_hidden: +pcs_func_local: +pcs_ifunc_global_def: +pcs_ifunc_global_hidden: +pcs_ifunc_local: + ret