Added indirect branch protection to function abstraction

* Adds a new entry to FuncAttributes
  * Adds a new API to FuncFrame
This commit is contained in:
kobalicek
2023-12-19 12:35:15 +01:00
parent 416f735696
commit 7c10a14d34
5 changed files with 62 additions and 7 deletions

View File

@@ -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<RegGroup, RegGroup::kGp, RegGroup::kVec>{}) {

View File

@@ -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<typename T>
ASMJIT_INLINE_NODEBUG T* userDataAsPtr() const noexcept { return static_cast<T*>(_userDataPtr); }
//! Returns user data casted to `int64_t`.

View File

@@ -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.

View File

@@ -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()) {

View File

@@ -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<void>(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);
func();
return true;
}
};
// x86::Compiler - X86Test_JumpMerge
// =================================
@@ -4362,6 +4391,7 @@ void compiler_add_x86_tests(TestApp& app) {
// Base tests.
app.addT<X86Test_NoCode>();
app.addT<X86Test_NoAlign>();
app.addT<X86Test_IndirectBranchProtection>();
app.addT<X86Test_AlignBase>();
// Jump tests.