Enable PAC in addition to BTI on arm64 such that JIT code matches the default branch protection provided by our base compiler

This commit is contained in:
purplerain 2024-04-04 10:34:08 +00:00
parent 128113431e
commit 1908ee2b1e
Signed by: purplerain
GPG Key ID: F42C07F07E2E35B7
3 changed files with 83 additions and 21 deletions

View File

@ -27,7 +27,7 @@
#include "util/detect.h"
#include "pipe/p_compiler.h"
#include "util/compiler.h"
#include "util/macros.h"
#include "util/u_cpu_detect.h"
#include "util/u_debug.h"
@ -42,14 +42,14 @@
#include <llvm/Config/llvm-config.h>
#include <llvm-c/Analysis.h>
#include <llvm-c/Transforms/Scalar.h>
#if LLVM_VERSION_MAJOR >= 7
#include <llvm-c/Transforms/Utils.h>
#endif
#include <llvm-c/BitWriter.h>
#if GALLIVM_USE_NEW_PASS == 1
#include <llvm-c/Transforms/PassBuilder.h>
#elif GALLIVM_HAVE_CORO == 1
#include <llvm-c/Transforms/Scalar.h>
#if LLVM_VERSION_MAJOR >= 7
#include <llvm-c/Transforms/Utils.h>
#endif
#if LLVM_VERSION_MAJOR <= 8 && (DETECT_ARCH_AARCH64 || DETECT_ARCH_ARM || DETECT_ARCH_S390 || DETECT_ARCH_MIPS64)
#include <llvm-c/Transforms/IPO.h>
#endif
@ -85,7 +85,7 @@ static const struct debug_named_value lp_bld_debug_flags[] = {
DEBUG_GET_ONCE_FLAGS_OPTION(gallivm_debug, "GALLIVM_DEBUG", lp_bld_debug_flags, 0)
static boolean gallivm_initialized = FALSE;
static bool gallivm_initialized = false;
unsigned lp_native_vector_width;
@ -112,7 +112,7 @@ enum LLVM_CodeGenOpt_Level {
* relevant optimization passes.
* \return TRUE for success, FALSE for failure
*/
static boolean
static bool
create_pass_manager(struct gallivm_state *gallivm)
{
#if GALLIVM_USE_NEW_PASS == 0
@ -121,7 +121,7 @@ create_pass_manager(struct gallivm_state *gallivm)
gallivm->passmgr = LLVMCreateFunctionPassManagerForModule(gallivm->module);
if (!gallivm->passmgr)
return FALSE;
return false;
#if GALLIVM_HAVE_CORO == 1
gallivm->cgpassmgr = LLVMCreatePassManager();
@ -191,7 +191,7 @@ create_pass_manager(struct gallivm_state *gallivm)
LLVMAddCoroCleanupPass(gallivm->passmgr);
#endif
#endif
return TRUE;
return true;
}
/**
@ -266,7 +266,7 @@ gallivm_free_code(struct gallivm_state *gallivm)
}
static boolean
static bool
init_gallivm_engine(struct gallivm_state *gallivm)
{
if (1) {
@ -316,10 +316,10 @@ init_gallivm_engine(struct gallivm_state *gallivm)
free(engine_data_layout);
}
return TRUE;
return true;
fail:
return FALSE;
return false;
}
@ -327,7 +327,7 @@ fail:
* Allocate gallivm LLVM objects.
* \return TRUE for success, FALSE for failure
*/
static boolean
static bool
init_gallivm_state(struct gallivm_state *gallivm, const char *name,
LLVMContextRef context, struct lp_cached_code *cache)
{
@ -335,7 +335,7 @@ init_gallivm_state(struct gallivm_state *gallivm, const char *name,
assert(!gallivm->module);
if (!lp_build_init())
return FALSE;
return false;
gallivm->context = context;
gallivm->cache = cache;
@ -360,6 +360,10 @@ init_gallivm_state(struct gallivm_state *gallivm, const char *name,
lp_set_module_stack_alignment_override(gallivm->module, 4);
#endif
#if DETECT_ARCH_AARCH64
lp_set_module_branch_protection(gallivm->module);
#endif
gallivm->builder = LLVMCreateBuilderInContext(gallivm->context);
if (!gallivm->builder)
goto fail;
@ -404,7 +408,7 @@ init_gallivm_state(struct gallivm_state *gallivm, const char *name,
gallivm->target = LLVMCreateTargetData(layout);
if (!gallivm->target) {
return FALSE;
return false;
}
}
@ -412,12 +416,12 @@ init_gallivm_state(struct gallivm_state *gallivm, const char *name,
goto fail;
lp_build_coro_declare_malloc_hooks(gallivm);
return TRUE;
return true;
fail:
gallivm_free_ir(gallivm);
gallivm_free_code(gallivm);
return FALSE;
return false;
}
unsigned
@ -433,12 +437,12 @@ lp_build_init_native_width(void)
return lp_native_vector_width;
}
boolean
bool
lp_build_init(void)
{
lp_build_init_native_width();
if (gallivm_initialized)
return TRUE;
return true;
/* LLVMLinkIn* are no-ops at runtime. They just ensure the respective
@ -475,9 +479,9 @@ lp_build_init(void)
}
#endif
gallivm_initialized = TRUE;
gallivm_initialized = true;
return TRUE;
return true;
}

View File

@ -62,6 +62,7 @@
#include <llvm/Support/PrettyStackTrace.h>
#include <llvm/ExecutionEngine/ObjectCache.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/CodeGen/SelectionDAGNodes.h>
#if LLVM_VERSION_MAJOR >= 15
#include <llvm/Support/MemoryBuffer.h>
#endif
@ -100,6 +101,8 @@
#include "lp_bld_misc.h"
#include "lp_bld_debug.h"
static void lp_run_atexit_for_destructors(void);
namespace {
class LLVMEnsureMultithreaded {
@ -147,6 +150,7 @@ static void init_native_targets()
}
}
#endif
lp_run_atexit_for_destructors();
}
extern "C" void
@ -366,7 +370,11 @@ lp_build_create_jit_compiler_for_module(LLVMExecutionEngineRef *OutJIT,
builder.setEngineKind(EngineKind::JIT)
.setErrorStr(&Error)
.setTargetOptions(options)
#if LLVM_VERSION_MAJOR >= 18
.setOptLevel((CodeGenOptLevel)OptLevel);
#else
.setOptLevel((CodeGenOpt::Level)OptLevel);
#endif
#if DETECT_OS_WINDOWS
/*
@ -408,6 +416,13 @@ lp_build_create_jit_compiler_for_module(LLVMExecutionEngineRef *OutJIT,
* so we do not use llvm::sys::getHostCPUFeatures to detect cpu features
* but using util_get_cpu_caps() instead.
*/
#if DETECT_ARCH_X86_64
/*
* Without this, on some "buggy" qemu cpu setup, LLVM could crash
* if LLVM detects the wrong CPU type.
*/
MAttrs.push_back("+64bit");
#endif
MAttrs.push_back(util_get_cpu_caps()->has_sse ? "+sse" : "-sse" );
MAttrs.push_back(util_get_cpu_caps()->has_sse2 ? "+sse2" : "-sse2" );
MAttrs.push_back(util_get_cpu_caps()->has_sse3 ? "+sse3" : "-sse3" );
@ -619,3 +634,42 @@ lp_set_module_stack_alignment_override(LLVMModuleRef MRef, unsigned align)
M->setOverrideStackAlignment(align);
#endif
}
extern "C" void
lp_set_module_branch_protection(LLVMModuleRef MRef)
{
/* Enable standard (bti+pac-ret) branch protection */
llvm::Module *M = llvm::unwrap(MRef);
M->addModuleFlag(llvm::Module::Override, "branch-target-enforcement", 1);
M->addModuleFlag(llvm::Module::Override, "sign-return-address", 1);
}
using namespace llvm;
class GallivmRunAtExitForStaticDestructors : public SDNode
{
public:
/* getSDVTList (protected) calls getValueTypeList (private), which contains static variables. */
GallivmRunAtExitForStaticDestructors(): SDNode(0, 0, DebugLoc(), getSDVTList(MVT::Other))
{
}
};
static void
lp_run_atexit_for_destructors(void)
{
/* LLVM >= 16 registers static variable destructors on the first compile, which gcc
* implements by calling atexit there. Before that, u_queue registers its atexit
* handler to kill all threads. Since exit() runs atexit handlers in the reverse order,
* the LLVM destructors are called first while shader compiler threads may still be
* running, which crashes in LLVM in SelectionDAG.cpp.
*
* The solution is to run the code that declares the LLVM static variables first,
* so that atexit for LLVM is registered first and u_queue is registered after that,
* which ensures that all u_queue threads are terminated before LLVM destructors are
* called.
*
* This just executes the code that declares static variables.
*/
GallivmRunAtExitForStaticDestructors();
}

View File

@ -94,6 +94,10 @@ lp_free_objcache(void *objcache);
void
lp_set_module_stack_alignment_override(LLVMModuleRef M, unsigned align);
void
lp_set_module_branch_protection(LLVMModuleRef M);
#ifdef __cplusplus
}
#endif