diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp index 28e0fce6a6f49..86f509f3fd78a 100644 --- a/lld/ELF/Arch/AArch64.cpp +++ b/lld/ELF/Arch/AArch64.cpp @@ -202,11 +202,16 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s, case R_AARCH64_LD64_GOT_LO12_NC: case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: return R_GOT; + case R_AARCH64_AUTH_LD64_GOT_LO12_NC: + case R_AARCH64_AUTH_GOT_ADD_LO12_NC: + return R_AARCH64_AUTH_GOT; case R_AARCH64_LD64_GOTPAGE_LO15: return R_AARCH64_GOT_PAGE; case R_AARCH64_ADR_GOT_PAGE: case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: return R_AARCH64_GOT_PAGE_PC; + case R_AARCH64_AUTH_ADR_GOT_PAGE: + return R_AARCH64_AUTH_GOT_PAGE_PC; case R_AARCH64_GOTPCREL32: case R_AARCH64_GOT_LD_PREL19: return R_GOT_PC; @@ -258,6 +263,7 @@ int64_t AArch64::getImplicitAddend(const uint8_t *buf, RelType type) const { return read64(ctx, buf + 8); case R_AARCH64_NONE: case R_AARCH64_GLOB_DAT: + case R_AARCH64_AUTH_GLOB_DAT: case R_AARCH64_JUMP_SLOT: return 0; case R_AARCH64_ABS16: @@ -529,9 +535,11 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel, write32(ctx, loc, val); break; case R_AARCH64_ADD_ABS_LO12_NC: + case R_AARCH64_AUTH_GOT_ADD_LO12_NC: write32Imm12(loc, val); break; case R_AARCH64_ADR_GOT_PAGE: + case R_AARCH64_AUTH_ADR_GOT_PAGE: case R_AARCH64_ADR_PREL_PG_HI21: case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: case R_AARCH64_TLSDESC_ADR_PAGE21: @@ -581,6 +589,7 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel, break; case R_AARCH64_LDST64_ABS_LO12_NC: case R_AARCH64_LD64_GOT_LO12_NC: + case R_AARCH64_AUTH_LD64_GOT_LO12_NC: case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC: case R_AARCH64_TLSDESC_LD64_LO12: diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index 3b48fbe07bb08..ccc7cf8c6e2de 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -754,6 +754,7 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r, case R_ARM_SBREL: return r.sym->getVA(ctx, a) - getARMStaticBase(*r.sym); case R_GOT: + case R_AARCH64_AUTH_GOT: case R_RELAX_TLS_GD_TO_IE_ABS: return r.sym->getGotVA(ctx) + a; case R_LOONGARCH_GOT: @@ -781,6 +782,7 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r, case R_RELAX_TLS_GD_TO_IE_GOT_OFF: return r.sym->getGotOffset(ctx) + a; case R_AARCH64_GOT_PAGE_PC: + case R_AARCH64_AUTH_GOT_PAGE_PC: case R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC: return getAArch64Page(r.sym->getGotVA(ctx) + a) - getAArch64Page(p); case R_AARCH64_GOT_PAGE: diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index d40348a7b30d8..54f3e4f1e8c21 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -210,8 +210,9 @@ static bool needsPlt(RelExpr expr) { } bool lld::elf::needsGot(RelExpr expr) { - return oneof( expr); } @@ -933,14 +934,26 @@ void elf::addGotEntry(Ctx &ctx, Symbol &sym) { // If preemptible, emit a GLOB_DAT relocation. if (sym.isPreemptible) { - ctx.mainPart->relaDyn->addReloc({ctx.target->gotRel, ctx.in.got.get(), off, + RelType gotRel = ctx.target->gotRel; + if (sym.hasFlag(NEEDS_GOT_AUTH)) { + assert(ctx.arg.emachine == EM_AARCH64); + gotRel = R_AARCH64_AUTH_GLOB_DAT; + } + ctx.mainPart->relaDyn->addReloc({gotRel, ctx.in.got.get(), off, DynamicReloc::AgainstSymbol, sym, 0, R_ABS}); return; } // Otherwise, the value is either a link-time constant or the load base - // plus a constant. + // plus a constant. Signed GOT requires dynamic relocation. + if (sym.hasFlag(NEEDS_GOT_AUTH)) { + ctx.in.got->getPartition(ctx).relaDyn->addReloc( + {R_AARCH64_AUTH_RELATIVE, ctx.in.got.get(), off, + DynamicReloc::AddendOnlyWithTargetVA, sym, 0, R_ABS}); + return; + } + if (!ctx.arg.isPic || isAbsolute(sym)) ctx.in.got->addConstant({R_ABS, ctx.target->symbolicRel, off, 0, &sym}); else @@ -994,10 +1007,11 @@ bool RelocationScanner::isStaticLinkTimeConstant(RelExpr e, RelType type, // These expressions always compute a constant if (oneof(e)) return true; @@ -1111,7 +1125,17 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset, } else if (!sym.isTls() || ctx.arg.emachine != EM_LOONGARCH) { // Many LoongArch TLS relocs reuse the R_LOONGARCH_GOT type, in which // case the NEEDS_GOT flag shouldn't get set. - sym.setFlags(NEEDS_GOT); + bool needsGotAuth = + (expr == R_AARCH64_AUTH_GOT || expr == R_AARCH64_AUTH_GOT_PAGE_PC); + if (!sym.hasFlag(NEEDS_GOT)) { + sym.setFlags(NEEDS_GOT); + if (needsGotAuth) + sym.setFlags(NEEDS_GOT_AUTH); + } else if (needsGotAuth != sym.hasFlag(NEEDS_GOT_AUTH)) { + fatal("both AUTH and non-AUTH GOT entries for '" + sym.getName() + + "' requested, but only one type of GOT entry per symbol is " + "supported"); + } } } else if (needsPlt(expr)) { sym.setFlags(NEEDS_PLT); diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h index 64e67c2c96820..20d88de402ac1 100644 --- a/lld/ELF/Relocations.h +++ b/lld/ELF/Relocations.h @@ -86,7 +86,9 @@ enum RelExpr { // of a relocation type, there are some relocations whose semantics are // unique to a target. Such relocation are marked with R_. R_AARCH64_GOT_PAGE_PC, + R_AARCH64_AUTH_GOT_PAGE_PC, R_AARCH64_GOT_PAGE, + R_AARCH64_AUTH_GOT, R_AARCH64_PAGE_PC, R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC, R_AARCH64_TLSDESC_PAGE, diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h index 339f32e05f162..470bae23832d7 100644 --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -54,6 +54,7 @@ enum { NEEDS_TLSGD_TO_IE = 1 << 6, NEEDS_GOT_DTPREL = 1 << 7, NEEDS_TLSIE = 1 << 8, + NEEDS_GOT_AUTH = 1 << 9, }; // The base class for real symbol classes. diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index 7a344635a1cb5..b349cf82c78e4 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -664,6 +664,8 @@ void GotSection::addConstant(const Relocation &r) { relocations.push_back(r); } void GotSection::addEntry(const Symbol &sym) { assert(sym.auxIdx == ctx.symAux.size() - 1); ctx.symAux.back().gotIdx = numEntries++; + if (sym.hasFlag(NEEDS_GOT_AUTH)) + authEntries.push_back({(numEntries - 1) * ctx.arg.wordsize, sym.isFunc()}); } bool GotSection::addTlsDescEntry(const Symbol &sym) { @@ -728,6 +730,23 @@ void GotSection::writeTo(uint8_t *buf) { return; ctx.target->writeGotHeader(buf); ctx.target->relocateAlloc(*this, buf); + for (const AuthEntryInfo &authEntry : authEntries) { + // https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst#default-signing-schema + // Signed GOT entries use the IA key for symbols of type STT_FUNC and the + // DA key for all other symbol types, with the address of the GOT entry as + // the modifier. The static linker must encode the signing schema into the + // GOT slot. + // + // https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst#encoding-the-signing-schema + // If address diversity is set and the discriminator + // is 0 then modifier = Place + uint8_t *dest = buf + authEntry.offset; + uint64_t key = authEntry.isSymbolFunc ? /*IA*/ 0b00 : /*DA*/ 0b10; + uint64_t addrDiversity = 1; + write64(ctx, dest, (addrDiversity << 63) | (key << 60)); + } + ctx.target->writeGotHeader(buf); + ctx.target->relocateAlloc(*this, buf); } static uint64_t getMipsPageAddr(uint64_t addr) { diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h index 3573767671feb..79af4e3971df5 100644 --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -131,6 +131,11 @@ class GotSection final : public SyntheticSection { size_t numEntries = 0; uint32_t tlsIndexOff = -1; uint64_t size = 0; + struct AuthEntryInfo { + size_t offset; + bool isSymbolFunc; + }; + SmallVector authEntries; }; // .note.GNU-stack section. diff --git a/lld/test/ELF/aarch64-auth-got-relocations.s b/lld/test/ELF/aarch64-auth-got-relocations.s new file mode 100644 index 0000000000000..f04e3d953388c --- /dev/null +++ b/lld/test/ELF/aarch64-auth-got-relocations.s @@ -0,0 +1,94 @@ +# REQUIRES: aarch64 + +# RUN: rm -rf %t && split-file %s %t && cd %t + +# RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux %p/Inputs/shared.s -o a.o +# RUN: ld.lld -shared a.o -o a.so + +#--- ok.s + +# RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux ok.s -o ok.o + +# RUN: ld.lld ok.o a.so -pie -o external +# RUN: llvm-readelf -r -S -x .got external | FileCheck %s --check-prefix=EXTERNAL + +# RUN: ld.lld ok.o a.o -pie -o local +# RUN: llvm-readelf -r -S -x .got -s local | FileCheck %s --check-prefix=LOCAL + +# EXTERNAL: Offset Info Type Symbol's Value Symbol's Name + Addend +# EXTERNAL-NEXT: 0000000000020380 000000010000e201 R_AARCH64_AUTH_GLOB_DAT 0000000000000000 bar + 0 +# EXTERNAL-NEXT: 0000000000020388 000000020000e201 R_AARCH64_AUTH_GLOB_DAT 0000000000000000 zed + 0 + +## Symbol's values for bar and zed are equal since they contain no content (see Inputs/shared.s) +# LOCAL: Offset Info Type Symbol's Value Symbol's Name + Addend +# LOCAL-NEXT: 0000000000020320 0000000000000411 R_AARCH64_AUTH_RELATIVE 10260 +# LOCAL-NEXT: 0000000000020328 0000000000000411 R_AARCH64_AUTH_RELATIVE 10260 + +# EXTERNAL: Hex dump of section '.got': +# EXTERNAL-NEXT: 0x00020380 00000000 00000080 00000000 000000a0 +# ^^ +# 0b10000000 bit 63 address diversity = true, bits 61..60 key = IA +# ^^ +# 0b10100000 bit 63 address diversity = true, bits 61..60 key = DA + +# LOCAL: Symbol table '.symtab' contains {{.*}} entries: +# LOCAL: Num: Value Size Type Bind Vis Ndx Name +# LOCAL: 0000000000010260 0 FUNC GLOBAL DEFAULT 6 bar +# LOCAL: 0000000000010260 0 NOTYPE GLOBAL DEFAULT 6 zed + +# LOCAL: Hex dump of section '.got': +# LOCAL-NEXT: 0x00020320 00000000 00000080 00000000 000000a0 +# ^^ +# 0b10000000 bit 63 address diversity = true, bits 61..60 key = IA +# ^^ +# 0b10100000 bit 63 address diversity = true, bits 61..60 key = DA + +# RUN: llvm-objdump -d external | FileCheck %s --check-prefix=EXTERNAL-ASM + +# EXTERNAL-ASM: <_start>: +# EXTERNAL-ASM-NEXT: adrp x0, 0x20000 +# EXTERNAL-ASM-NEXT: ldr x0, [x0, #0x380] +# EXTERNAL-ASM-NEXT: adrp x1, 0x20000 +# EXTERNAL-ASM-NEXT: add x1, x1, #0x380 +# EXTERNAL-ASM-NEXT: adrp x0, 0x20000 +# EXTERNAL-ASM-NEXT: ldr x0, [x0, #0x388] +# EXTERNAL-ASM-NEXT: adrp x1, 0x20000 +# EXTERNAL-ASM-NEXT: add x1, x1, #0x388 + +# RUN: llvm-objdump -d local | FileCheck %s --check-prefix=LOCAL-ASM + +# LOCAL-ASM: <_start>: +# LOCAL-ASM-NEXT: adrp x0, 0x20000 +# LOCAL-ASM-NEXT: ldr x0, [x0, #0x320] +# LOCAL-ASM-NEXT: adrp x1, 0x20000 +# LOCAL-ASM-NEXT: add x1, x1, #0x320 +# LOCAL-ASM-NEXT: adrp x0, 0x20000 +# LOCAL-ASM-NEXT: ldr x0, [x0, #0x328] +# LOCAL-ASM-NEXT: adrp x1, 0x20000 +# LOCAL-ASM-NEXT: add x1, x1, #0x328 + +.globl _start +_start: + adrp x0, :got_auth:bar + ldr x0, [x0, :got_auth_lo12:bar] + adrp x1, :got_auth:bar + add x1, x1, :got_auth_lo12:bar + adrp x0, :got_auth:zed + ldr x0, [x0, :got_auth_lo12:zed] + adrp x1, :got_auth:zed + add x1, x1, :got_auth_lo12:zed + +#--- err.s + +# RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux err.s -o err.o + +# RUN: not ld.lld err.o a.so -pie -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR + +# ERR: error: both AUTH and non-AUTH GOT entries for 'bar' requested, but only one type of GOT entry per symbol is supported + +.globl _start +_start: + adrp x0, :got_auth:bar + ldr x0, [x0, :got_auth_lo12:bar] + adrp x0, :got:bar + ldr x0, [x0, :got_lo12:bar]