diff --git a/llvm/docs/PointerAuth.md b/docs/PointerAuth.md --- a/llvm/docs/PointerAuth.md +++ b/docs/PointerAuth.md @@ -300,3 +300,45 @@ * [``llvm.ptrauth.resign``](#llvm-ptrauth-resign): ``AUT*+PAC*``. These are represented as a single pseudo-instruction in the backend to guarantee that the intermediate raw pointer value is not spilled and attackable. + +#### Assembly Representation + +At the assembly level, +[Authenticated Relocations](#authenticated-global-relocation) are represented +using the ``@AUTH`` modifier: + +```asm + .quad _target@AUTH(,[,addr]) +``` + +where: +* ``key`` is the Armv8.3-A key identifier (``ia``, ``ib``, ``da``, ``db``) +* ``discriminator`` is the 16-bit unsigned discriminator value +* ``addr`` signifies that the authenticated pointer is address-discriminated + (that is, that the relocation's target address is to be blended into the + ``discriminator`` before it is used in the sign operation. + +For example: +```asm + _authenticated_reference_to_sym: + .quad _sym@AUTH(db,0) + _authenticated_reference_to_sym_addr_disc: + .quad _sym@AUTH(ia,12,addr) +``` + +#### ELF Object File Representation + +At the binary object file level, +[Authenticated Relocations](#authenticated-global-relocation) are represented +using the ``ARM64_RELOC_AUTHENTICATED_POINTER`` relocation kind (with value +``0xE100``). + +The signing schema is encoded in the place of relocation to be applied +as follows: + +``` +| 63 | 62 | 61:60 | 59:48 | 47:32 | 31:0 | +| ----------------- | -------- | -------- | -------- | ------------- | ------------------- | +| address diversity | reserved | key | reserved | discriminator | reserved for addend | +``` + diff --git a/llvm/include/llvm/BinaryFormat/ELFRelocs/AArch64.def b/include/llvm/BinaryFormat/ELFRelocs/AArch64.def --- a/llvm/include/llvm/BinaryFormat/ELFRelocs/AArch64.def +++ b/include/llvm/BinaryFormat/ELFRelocs/AArch64.def @@ -134,6 +134,7 @@ ELF_RELOC(R_AARCH64_TLS_TPREL64, 0x406) ELF_RELOC(R_AARCH64_TLSDESC, 0x407) ELF_RELOC(R_AARCH64_IRELATIVE, 0x408) +ELF_RELOC(R_AARCH64_AUTH_ABS64, 0xe100) // ELF_RELOC(R_AARCH64_P32_NONE, 0) ELF_RELOC(R_AARCH64_P32_ABS32, 0x001) diff --git a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp --- a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp +++ b/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp @@ -180,6 +180,8 @@ bool showMatchError(SMLoc Loc, unsigned ErrCode, uint64_t ErrorInfo, OperandVector &Operands); + bool parseAuthExpr(const MCExpr *&Res, SMLoc &EndLoc); + bool parseDirectiveArch(SMLoc L); bool parseDirectiveArchExtension(SMLoc L); bool parseDirectiveCPU(SMLoc L); @@ -324,6 +326,8 @@ unsigned validateTargetOperandClass(MCParsedAsmOperand &Op, unsigned Kind) override; + bool parsePrimaryExpr(const MCExpr *&Res, SMLoc &EndLoc) override; + static bool classifySymbolRef(const MCExpr *Expr, AArch64MCExpr::VariantKind &ELFRefKind, MCSymbolRefExpr::VariantKind &DarwinRefKind, @@ -7451,6 +7455,113 @@ return false; } +bool AArch64AsmParser::parsePrimaryExpr(const MCExpr *&Res, SMLoc &EndLoc) { + // Try @AUTH expressions: they're more complex than the usual symbol variants. + if (!parseAuthExpr(Res, EndLoc)) + return false; + return getParser().parsePrimaryExpr(Res, EndLoc, nullptr); +} + +/// parseAuthExpr +/// ::= _sym@AUTH(ib,123[,addr]) +/// ::= (_sym + 5)@AUTH(ib,123[,addr]) +/// ::= (_sym - 5)@AUTH(ib,123[,addr]) +bool AArch64AsmParser::parseAuthExpr(const MCExpr *&Res, SMLoc &EndLoc) { + MCAsmParser &Parser = getParser(); + MCContext &Ctx = getContext(); + + AsmToken Tok = Parser.getTok(); + + // Look for '_sym@AUTH' ... + if (Tok.is(AsmToken::Identifier) && Tok.getIdentifier().endswith("@AUTH")) { + StringRef SymName = Tok.getIdentifier().drop_back(strlen("@AUTH")); + Res = MCSymbolRefExpr::create(Ctx.getOrCreateSymbol(SymName), Ctx); + + Parser.Lex(); // Eat the identifier. + } else { + // ... or look for a more complex symbol reference, such as ... + SmallVector Tokens; + + // ... '"_long sym"@AUTH' ... + if (Tok.is(AsmToken::String)) + Tokens.resize(2); + // ... or '(_sym + 5)@AUTH'. + else if (Tok.is(AsmToken::LParen)) + Tokens.resize(6); + else + return true; + + if (Parser.getLexer().peekTokens(Tokens) != Tokens.size()) + return true; + + // In either case, the expression ends with '@' 'AUTH'. + if (Tokens[Tokens.size()-2].isNot(AsmToken::At) || + Tokens[Tokens.size()-1].isNot(AsmToken::Identifier) || + Tokens[Tokens.size()-1].getIdentifier() != "AUTH") + return true; + + if (Tok.is(AsmToken::String)) { + StringRef SymName; + if (Parser.parseIdentifier(SymName)) + return true; + Res = MCSymbolRefExpr::create(Ctx.getOrCreateSymbol(SymName), Ctx); + } else { + if (Parser.parsePrimaryExpr(Res, EndLoc, nullptr)) + return true; + } + + Parser.Lex(); // '@' + Parser.Lex(); // 'AUTH' + } + + // At this point, we encountered "@AUTH". There is no fallback anymore. + if (parseToken(AsmToken::LParen, "expected '(' after @AUTH expression")) + return true; + + if (Parser.getTok().isNot(AsmToken::Identifier)) + return TokError("expected key name in @AUTH expression"); + + StringRef KeyStr = Parser.getTok().getIdentifier(); + auto KeyIDOrNone = AArch64StringToPACKeyID(KeyStr); + if (!KeyIDOrNone) + return TokError("invalid key '" + KeyStr + "' in @AUTH expression"); + Parser.Lex(); + + if (parseToken(AsmToken::Comma, "expected ',' after key in @AUTH expression")) + return true; + + if (Parser.getTok().isNot(AsmToken::Integer)) + return TokError( + "expected integer discriminator after key in @AUTH expression"); + int64_t Discriminator = Parser.getTok().getIntVal(); + + if (!isUInt<16>(Discriminator)) + return TokError("too wide integer discriminator '" + itostr(Discriminator) + + "'in @AUTH expression"); + Parser.Lex(); + + bool UseAddressDiversity = false; + if (Parser.getTok().is(AsmToken::Comma)) { + Parser.Lex(); + if (Parser.getTok().isNot(AsmToken::Identifier) || + Parser.getTok().getIdentifier() != "addr") + return TokError( + "expected 'addr' after discriminator in @AUTH expression"); + UseAddressDiversity = true; + Parser.Lex(); + } + + EndLoc = Parser.getTok().getEndLoc(); + if (parseToken(AsmToken::RParen, + "expected ')' at the end of @AUTH expression")) + return true; + + Res = AArch64AuthMCExpr::create(Res, Discriminator, *KeyIDOrNone, + UseAddressDiversity, Ctx); + + return false; +} + bool AArch64AsmParser::classifySymbolRef(const MCExpr *Expr, AArch64MCExpr::VariantKind &ELFRefKind, diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp b/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp +++ b/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp @@ -321,10 +321,32 @@ if (Value & 0x3) Ctx.reportError(Fixup.getLoc(), "fixup not sufficiently aligned"); return (Value >> 2) & 0x3ffffff; + case FK_Data_8: + if (TheTriple.isOSBinFormatELF()) { + AArch64MCExpr::VariantKind RefKind = + static_cast(Target.getRefKind()); + AArch64MCExpr::VariantKind SymLoc = AArch64MCExpr::getSymbolLoc(RefKind); + if (SymLoc == AArch64AuthMCExpr::VK_AUTH || + SymLoc == AArch64AuthMCExpr::VK_AUTHADDR) { + if (!isInt<32>(Value)) + Ctx.reportError(Fixup.getLoc(), "too wide addend '" + + itostr(Value) + + "' in auth relocation"); + + auto *Expr = cast(Fixup.getValue()); + uint16_t Discriminator = Expr->getDiscriminator(); + AArch64PACKey::ID Key = Expr->getKey(); + + return (uint32_t(Value) << 0) | + (uint64_t(Discriminator) << 32) | + (uint64_t(Key) << 60) | + (uint64_t(Expr->hasAddressDiversity()) << 63); + } + } + LLVM_FALLTHROUGH; case FK_Data_1: case FK_Data_2: case FK_Data_4: - case FK_Data_8: case FK_SecRel_2: case FK_SecRel_4: return Value; @@ -393,7 +415,9 @@ MutableArrayRef Data, uint64_t Value, bool IsResolved, const MCSubtargetInfo *STI) const { - if (!Value) + // For FK_Data_8, the fixup might correspond to VK_AUTH or VK_AUTHADDR symbol kind. + // So, need to call adjustFixupValue even if the value is zero. + if (!Value && Fixup.getTargetKind() != FK_Data_8) return; // Doesn't change encoding. unsigned Kind = Fixup.getKind(); if (Kind >= FirstLiteralRelocationKind) @@ -404,6 +428,9 @@ int64_t SignedValue = static_cast(Value); // Apply any target-specific value adjustments. Value = adjustFixupValue(Fixup, Target, Value, Ctx, TheTriple, IsResolved); + // The fixup is not related to VK_AUTH or VK_AUTHADDR. Skip. + if (!Value && Fixup.getTargetKind() == FK_Data_8) + return; // Doesn't change encoding. // Shift the value into position. Value <<= Info.TargetOffset; diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp b/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp +++ b/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp @@ -207,8 +207,12 @@ "ILP32 8 byte absolute data " "relocation not supported (LP64 eqv: ABS64)"); return ELF::R_AARCH64_NONE; - } else + } else { + if (RefKind == AArch64MCExpr::VK_AUTH || + RefKind == AArch64MCExpr::VK_AUTHADDR) + return ELF::R_AARCH64_AUTH_ABS64; return ELF::R_AARCH64_ABS64; + } case AArch64::fixup_aarch64_add_imm12: if (RefKind == AArch64MCExpr::VK_DTPREL_HI12) return R_CLS(TLSLD_ADD_DTPREL_HI12); diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.h b/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.h --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.h +++ b/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.h @@ -14,6 +14,7 @@ #ifndef LLVM_LIB_TARGET_AARCH64_MCTARGETDESC_AARCH64MCEXPR_H #define LLVM_LIB_TARGET_AARCH64_MCTARGETDESC_AARCH64MCEXPR_H +#include "Utils/AArch64BaseInfo.h" #include "llvm/MC/MCExpr.h" #include "llvm/Support/ErrorHandling.h" @@ -34,6 +35,8 @@ VK_TPREL = 0x007, VK_TLSDESC = 0x008, VK_SECREL = 0x009, + VK_AUTH = 0x00a, + VK_AUTHADDR = 0x00b, VK_SymLocBits = 0x00f, // Variants specifying which part of the final address calculation is @@ -116,6 +119,7 @@ const MCExpr *Expr; const VariantKind Kind; +protected: explicit AArch64MCExpr(const MCExpr *Expr, VariantKind Kind) : Expr(Expr), Kind(Kind) {} @@ -171,6 +175,51 @@ return E->getKind() == MCExpr::Target; } }; + +class AArch64AuthMCExpr : public AArch64MCExpr { + uint16_t Discriminator; + AArch64PACKey::ID Key; + + explicit AArch64AuthMCExpr(const MCExpr *Expr, uint16_t Discriminator, + AArch64PACKey::ID Key, bool HasAddressDiversity) + : AArch64MCExpr(Expr, HasAddressDiversity ? VK_AUTHADDR : VK_AUTH), + Discriminator(Discriminator), Key(Key) {} + +public: + /// @name Construction + /// @{ + + static const AArch64AuthMCExpr * + create(const MCExpr *Expr, uint16_t Discriminator, AArch64PACKey::ID Key, + bool HasAddressDiversity, MCContext &Ctx); + + /// @} + /// @name Accessors + /// @{ + + AArch64PACKey::ID getKey() const { return Key; } + uint16_t getDiscriminator() const { return Discriminator; } + bool hasAddressDiversity() const { return getKind() == VK_AUTHADDR; } + + /// @} + + void printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const override; + + void visitUsedExpr(MCStreamer &Streamer) const override; + + MCFragment *findAssociatedFragment() const override; + + bool evaluateAsRelocatableImpl(MCValue &Res, const MCAsmLayout *Layout, + const MCFixup *Fixup) const override; + + static bool classof(const MCExpr *E) { + return E->getKind() == MCExpr::Target; + } + + static bool classof(const AArch64MCExpr *E) { + return E->getKind() == VK_AUTH || E->getKind() == VK_AUTHADDR; + } +}; } // end namespace llvm #endif diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.cpp b/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.cpp --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.cpp +++ b/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.cpp @@ -154,3 +154,50 @@ fixELFSymbolsInTLSFixupsImpl(getSubExpr(), Asm); } + +//===----------------------------------------------------------------------===// + +const AArch64AuthMCExpr *AArch64AuthMCExpr::create(const MCExpr *Expr, + uint16_t Discriminator, + AArch64PACKey::ID Key, + bool HasAddressDiversity, + MCContext &Ctx) { + return new (Ctx) + AArch64AuthMCExpr(Expr, Discriminator, Key, HasAddressDiversity); +} + +void AArch64AuthMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const { + bool WrapSubExprInParens = !isa(getSubExpr()); + if (WrapSubExprInParens) + OS << '('; + getSubExpr()->print(OS, MAI); + if (WrapSubExprInParens) + OS << ')'; + + OS << "@AUTH(" << AArch64PACKeyIDToString(Key) << ',' << Discriminator; + if (hasAddressDiversity()) + OS << ",addr"; + OS << ')'; +} + +void AArch64AuthMCExpr::visitUsedExpr(MCStreamer &Streamer) const { + Streamer.visitUsedExpr(*getSubExpr()); +} + +MCFragment *AArch64AuthMCExpr::findAssociatedFragment() const { + llvm_unreachable("FIXME: what goes here?"); +} + +bool AArch64AuthMCExpr::evaluateAsRelocatableImpl(MCValue &Res, + const MCAsmLayout *Layout, + const MCFixup *Fixup) const { + if (!getSubExpr()->evaluateAsRelocatable(Res, Layout, Fixup)) + return false; + + if (Res.getSymB()) + report_fatal_error("Auth relocation can't reference two symbols"); + + Res = MCValue::get(Res.getSymA(), nullptr, Res.getConstant(), getKind()); + + return true; +} diff --git a/test/MC/AArch64/arm64-ptrauth-elf-reloc.s b/test/MC/AArch64/arm64-ptrauth-elf-reloc.s new file mode 100644 --- /dev/null +++ b/test/MC/AArch64/arm64-ptrauth-elf-reloc.s @@ -0,0 +1,137 @@ +// RUN: llvm-mc -triple=arm64-linux < %s | \ +// RUN: FileCheck %s --check-prefix=ASM + +// RUN: llvm-mc -triple=arm64-linux -filetype=obj < %s | \ +// RUN: llvm-readobj --expand-relocs --sections --section-relocations --section-data - | \ +// RUN: FileCheck %s --check-prefix=RELOC + + +// RELOC: Sections [ +// RELOC-LABEL: Section { +// RELOC-LABEL: Section { +// RELOC-LABEL: Section { +// RELOC-LABEL: Section { + +// Check signing schema encoding in place of relocation to be applied + +// RELOC-NEXT: Index: 3 +// RELOC-NEXT: Name: .test +// RELOC-LABEL: SectionData ( +// VVVVVVVV addend, not needed for rela +// VV reserved +// RELOC-NEXT: 0000: 00000000 2A000000 +// ^^^^ discriminator +// ^^ 0 no addr diversity 0 reserved 00 ia key 0000 reserved +// RELOC-NEXT: 0010: 00000000 00000010 +// ^^^^ discriminator +// ^^ 0 no addr diversity 0 reserved 01 ib key 0000 reserved +// RELOC-NEXT: 0020: 00000000 050000A0 +// ^^^^ discriminator +// ^^ 1 addr diversity 0 reserved 10 da key 0000 reserved +// RELOC-NEXT: 0030: 00000000 FFFF00B0 +// ^^^^ discriminator +// ^^ 1 addr diversity 0 reserved 11 db key 0000 reserved +// RELOC-NEXT: 0040: 00000000 00000000 +// ^^^^ discriminator +// ^^ 0 no addr diversity 0 reserved 00 ia key 0000 reserved +// RELOC-NEXT: 0050: 00000000 00DE0010 +// ^^^^ discriminator +// ^^ 0 no addr diversity 0 reserved 01 ib key 0000 reserved +// RELOC-NEXT: 0060: 00000000 FF0000B0 +// ^^^^ discriminator +// ^^ 1 addr diversity 0 reserved 11 db key 0000 reserved +// RELOC-NEXT: 0070: 00000000 10000000 +// ^^^^ discriminator +// ^^ 0 no addr diversity 0 reserved 00 ia key 0000 reserved +// RELOC-LABEL: Section { + +// Check relocations themselves + +// RELOC-NEXT: Index: 4 +// RELOC-NEXT: Name: .rela.test + +.section .test +.p2align 3 + +// RELOC-LABEL: Relocations [ +// RELOC-NEXT: Relocation { +// RELOC-NEXT: Offset: 0x0 +// RELOC-NEXT: Type: R_AARCH64_AUTH_ABS64 (57600) +// RELOC-NEXT: Symbol: _g0 +// RELOC-NEXT: Addend: 0x0 +// RELOC-NEXT: } +// RELOC-NEXT: Relocation { +// RELOC-NEXT: Offset: 0x10 +// RELOC-NEXT: Type: R_AARCH64_AUTH_ABS64 (57600) +// RELOC-NEXT: Symbol: _g1 +// RELOC-NEXT: Addend: 0x0 +// RELOC-NEXT: } +// RELOC-NEXT: Relocation { +// RELOC-NEXT: Offset: 0x20 +// RELOC-NEXT: Type: R_AARCH64_AUTH_ABS64 (57600) +// RELOC-NEXT: Symbol: _g2 +// RELOC-NEXT: Addend: 0x0 +// RELOC-NEXT: } +// RELOC-NEXT: Relocation { +// RELOC-NEXT: Offset: 0x30 +// RELOC-NEXT: Type: R_AARCH64_AUTH_ABS64 (57600) +// RELOC-NEXT: Symbol: _g3 +// RELOC-NEXT: Addend: 0x0 +// RELOC-NEXT: } +// RELOC-NEXT: Relocation { +// RELOC-NEXT: Offset: 0x40 +// RELOC-NEXT: Type: R_AARCH64_AUTH_ABS64 (57600) +// RELOC-NEXT: Symbol: _g4 +// RELOC-NEXT: Addend: 0x7 +// RELOC-NEXT: } +// RELOC-NEXT: Relocation { +// RELOC-NEXT: Offset: 0x50 +// RELOC-NEXT: Type: R_AARCH64_AUTH_ABS64 (57600) +// RELOC-NEXT: Symbol: _g5 +// RELOC-NEXT: Addend: 0xFFFFFFFFFFFFFFFD +// RELOC-NEXT: } +// RELOC-NEXT: Relocation { +// RELOC-NEXT: Offset: 0x60 +// RELOC-NEXT: Type: R_AARCH64_AUTH_ABS64 (57600) +// RELOC-NEXT: Symbol: _g 6 +// RELOC-NEXT: Addend: 0x0 +// RELOC-NEXT: } +// RELOC-NEXT: Relocation { +// RELOC-NEXT: Offset: 0x70 +// RELOC-NEXT: Type: R_AARCH64_AUTH_ABS64 (57600) +// RELOC-NEXT: Symbol: _g 7 +// RELOC-NEXT: Addend: 0x7 +// RELOC-NEXT: } +// RELOC-NEXT: ] + +// ASM: .xword _g0@AUTH(ia,42) +.quad _g0@AUTH(ia,42) +.quad 0 + +// ASM: .xword _g1@AUTH(ib,0) +.quad _g1@AUTH(ib,0) +.quad 0 + +// ASM: .xword _g2@AUTH(da,5,addr) +.quad _g2@AUTH(da,5,addr) +.quad 0 + +// ASM: .xword _g3@AUTH(db,65535,addr) +.quad _g3@AUTH(db,0xffff,addr) +.quad 0 + +// ASM: .xword (_g4+7)@AUTH(ia,0) +.quad (_g4 + 7)@AUTH(ia,0) +.quad 0 + +// ASM: .xword (_g5-3)@AUTH(ib,56832) +.quad (_g5 - 3)@AUTH(ib,0xde00) +.quad 0 + +// ASM: .xword "_g 6"@AUTH(db,255,addr) +.quad "_g 6"@AUTH(db,0xff,addr) +.quad 0 + +// ASM: .xword ("_g 7"+7)@AUTH(ia,16) +.quad ("_g 7" + 7)@AUTH(ia,16) +.quad 0