diff --git a/src/asmjit/arm/a64emithelper.cpp b/src/asmjit/arm/a64emithelper.cpp index 1e8da61..f57a9e1 100644 --- a/src/asmjit/arm/a64emithelper.cpp +++ b/src/asmjit/arm/a64emithelper.cpp @@ -312,6 +312,12 @@ ASMJIT_FAVOR_SIZE Error EmitHelper::emitProlog(const FuncFrame& frame) { { Inst::kIdStr_v, Inst::kIdStp_v } }}; + // Emit: 'bti' (indirect branch protection). + if (frame.hasIndirectBranchProtection()) { + // TODO: The instruction is not available at the moment (would be ABI break). + // ASMJIT_PROPAGATE(emitter->bti()); + } + uint32_t adjustInitialOffset = pei.sizeTotal; for (RegGroup group : Support::EnumValues{}) { diff --git a/src/asmjit/core/builder.h b/src/asmjit/core/builder.h index c7aca96..3bf530c 100644 --- a/src/asmjit/core/builder.h +++ b/src/asmjit/core/builder.h @@ -546,7 +546,7 @@ public: uint8_t _reserved1; }; - //! Data that can have different meaning dependning on \ref NodeType. + //! Data that can have different meaning depending on \ref NodeType. union { //! Data useful by any node type. AnyData _any; @@ -694,8 +694,9 @@ public: //! Returns user data casted to `T*`. //! - //! User data is decicated to be used only by AsmJit users and not touched by the library. The data has a pointer - //! size so you can either store a pointer or `intptr_t` value through `setUserDataAsIntPtr()`. + //! User data is dedicated to be used only by AsmJit users and not touched by the library. The data is of a pointer + //! size so you can either store a pointer or `int64_t` value through `setUserDataAsPtr()`, `setUserDataAsInt64()` + //! and `setUserDataAsUInt64()`. template ASMJIT_INLINE_NODEBUG T* userDataAsPtr() const noexcept { return static_cast(_userDataPtr); } //! Returns user data casted to `int64_t`. diff --git a/src/asmjit/core/func.h b/src/asmjit/core/func.h index 33b2196..d9c2d9b 100644 --- a/src/asmjit/core/func.h +++ b/src/asmjit/core/func.h @@ -717,6 +717,11 @@ enum class FuncAttributes : uint32_t { kHasFuncCalls = 0x00000020u, //! Function has aligned save/restore of vector registers. kAlignedVecSR = 0x00000040u, + //! Function must begin with an instruction that marks a start of a branch or function. + //! + //! * `ENDBR32/ENDBR64` instruction is inserted at the beginning of the function (X86, X86_64). + //! * `BTI` instruction is inserted at the beginning of the function (AArch64) + kIndirectBranchProtection = 0x00000080u, //! FuncFrame is finalized and can be used by prolog/epilog inserter (PEI). kIsFinalized = 0x00000800u, @@ -1072,11 +1077,18 @@ public: //! Tests whether the function calls other functions. ASMJIT_INLINE_NODEBUG bool hasFuncCalls() const noexcept { return hasAttribute(FuncAttributes::kHasFuncCalls); } - //! Sets `kFlagHasCalls` to true. + //! Sets `FuncAttributes::kHasFuncCalls` to true. ASMJIT_INLINE_NODEBUG void setFuncCalls() noexcept { addAttributes(FuncAttributes::kHasFuncCalls); } - //! Sets `kFlagHasCalls` to false. + //! Sets `FuncAttributes::kHasFuncCalls` to false. ASMJIT_INLINE_NODEBUG void resetFuncCalls() noexcept { clearAttributes(FuncAttributes::kHasFuncCalls); } + //! Tests whether the function uses indirect branch protection, see \ref FuncAttributes::kIndirectBranchProtection. + ASMJIT_INLINE_NODEBUG bool hasIndirectBranchProtection() const noexcept { return hasAttribute(FuncAttributes::kIndirectBranchProtection); } + //! Enabled indirect branch protection (sets `FuncAttributes::kIndirectBranchProtection` attribute to true). + ASMJIT_INLINE_NODEBUG void setIndirectBranchProtection() noexcept { addAttributes(FuncAttributes::kIndirectBranchProtection); } + //! Disables indirect branch protection (sets `FuncAttributes::kIndirectBranchProtection` attribute to false). + ASMJIT_INLINE_NODEBUG void resetIndirectBranchProtection() noexcept { clearAttributes(FuncAttributes::kIndirectBranchProtection); } + //! Tests whether the function has AVX enabled. ASMJIT_INLINE_NODEBUG bool isAvxEnabled() const noexcept { return hasAttribute(FuncAttributes::kX86_AVXEnabled); } //! Enables AVX use. diff --git a/src/asmjit/x86/x86emithelper.cpp b/src/asmjit/x86/x86emithelper.cpp index ed8eae1..05a0540 100644 --- a/src/asmjit/x86/x86emithelper.cpp +++ b/src/asmjit/x86/x86emithelper.cpp @@ -425,6 +425,12 @@ ASMJIT_FAVOR_SIZE Error EmitHelper::emitProlog(const FuncFrame& frame) { Gp gpReg = zsp; // General purpose register (temporary). Gp saReg = zsp; // Stack-arguments base pointer. + // Emit: 'endbr32' or 'endbr64' (indirect branch protection). + if (frame.hasIndirectBranchProtection()) { + InstId instId = emitter->is32Bit() ? Inst::kIdEndbr32 : Inst::kIdEndbr64; + ASMJIT_PROPAGATE(emitter->emit(instId)); + } + // Emit: 'push zbp' // 'mov zbp, zsp'. if (frame.hasPreservedFP()) { diff --git a/test/asmjit_test_compiler_x86.cpp b/test/asmjit_test_compiler_x86.cpp index 2539d16..0854f38 100644 --- a/test/asmjit_test_compiler_x86.cpp +++ b/test/asmjit_test_compiler_x86.cpp @@ -238,8 +238,8 @@ public: } }; -// x86::Compiler - X86Test_AlignNone -// ================================= +// x86::Compiler - X86Test_NoAlign +// =============================== class X86Test_NoAlign : public X86TestCase { public: @@ -267,6 +267,35 @@ public: } }; +// x86::Compiler - X86Test_IndirectBranchProtection +// ================================================ + +class X86Test_IndirectBranchProtection : public X86TestCase { +public: + X86Test_IndirectBranchProtection() : X86TestCase("IndirectBranchProtection") {} + + static void add(TestApp& app) { + app.add(new X86Test_IndirectBranchProtection()); + } + + virtual void compile(x86::Compiler& cc) { + FuncNode* func = cc.addFunc(FuncSignatureT(CallConvId::kHost)); + func->addAttributes(FuncAttributes::kIndirectBranchProtection); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + DebugUtils::unused(result, expect); + + typedef void (*Func)(void); + Func func = ptr_as_func(_func); + + func(); + return true; + } +}; + + // x86::Compiler - X86Test_JumpMerge // ================================= @@ -4362,6 +4391,7 @@ void compiler_add_x86_tests(TestApp& app) { // Base tests. app.addT(); app.addT(); + app.addT(); app.addT(); // Jump tests.