From 1908ee2b1eb38fbbd6d3e926d93a664506a2965f Mon Sep 17 00:00:00 2001 From: purplerain Date: Thu, 4 Apr 2024 10:34:08 +0000 Subject: [PATCH] Enable PAC in addition to BTI on arm64 such that JIT code matches the default branch protection provided by our base compiler --- .../gallium/auxiliary/gallivm/lp_bld_init.c | 46 ++++++++-------- .../gallium/auxiliary/gallivm/lp_bld_misc.cpp | 54 +++++++++++++++++++ .../gallium/auxiliary/gallivm/lp_bld_misc.h | 4 ++ 3 files changed, 83 insertions(+), 21 deletions(-) diff --git a/lib/mesa/src/gallium/auxiliary/gallivm/lp_bld_init.c b/lib/mesa/src/gallium/auxiliary/gallivm/lp_bld_init.c index 24d08239..ea8a22f7 100644 --- a/lib/mesa/src/gallium/auxiliary/gallivm/lp_bld_init.c +++ b/lib/mesa/src/gallium/auxiliary/gallivm/lp_bld_init.c @@ -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 #include -#include -#if LLVM_VERSION_MAJOR >= 7 -#include -#endif #include #if GALLIVM_USE_NEW_PASS == 1 #include #elif GALLIVM_HAVE_CORO == 1 +#include +#if LLVM_VERSION_MAJOR >= 7 +#include +#endif #if LLVM_VERSION_MAJOR <= 8 && (DETECT_ARCH_AARCH64 || DETECT_ARCH_ARM || DETECT_ARCH_S390 || DETECT_ARCH_MIPS64) #include #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; } diff --git a/lib/mesa/src/gallium/auxiliary/gallivm/lp_bld_misc.cpp b/lib/mesa/src/gallium/auxiliary/gallivm/lp_bld_misc.cpp index 5e7a30a6..b50a1742 100644 --- a/lib/mesa/src/gallium/auxiliary/gallivm/lp_bld_misc.cpp +++ b/lib/mesa/src/gallium/auxiliary/gallivm/lp_bld_misc.cpp @@ -62,6 +62,7 @@ #include #include #include +#include #if LLVM_VERSION_MAJOR >= 15 #include #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(); +} diff --git a/lib/mesa/src/gallium/auxiliary/gallivm/lp_bld_misc.h b/lib/mesa/src/gallium/auxiliary/gallivm/lp_bld_misc.h index fa0ce901..fd6a4c34 100644 --- a/lib/mesa/src/gallium/auxiliary/gallivm/lp_bld_misc.h +++ b/lib/mesa/src/gallium/auxiliary/gallivm/lp_bld_misc.h @@ -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