asmjit_test_environment.cpp (9952B)
1 // This file is part of AsmJit project <https://asmjit.com> 2 // 3 // See <asmjit/core.h> or LICENSE.md for license and copyright information 4 // SPDX-License-Identifier: Zlib 5 6 #include <asmjit/core.h> 7 8 #if !defined(ASMJIT_NO_X86) && ASMJIT_ARCH_X86 != 0 9 #include <asmjit/x86.h> 10 #endif 11 12 #if !defined(ASMJIT_NO_AARCH64) && ASMJIT_ARCH_ARM == 64 13 #include <asmjit/a64.h> 14 #endif 15 16 #include "../commons/asmjitutils.h" 17 18 using namespace asmjit; 19 20 static void print_app_info() { 21 printf("AsmJit Environment Test v%u.%u.%u [Arch=%s] [Mode=%s]\n\n", 22 unsigned((ASMJIT_LIBRARY_VERSION >> 16) ), 23 unsigned((ASMJIT_LIBRARY_VERSION >> 8) & 0xFF), 24 unsigned((ASMJIT_LIBRARY_VERSION ) & 0xFF), 25 asmjit_arch_as_string(Arch::kHost), 26 asmjit_build_type() 27 ); 28 29 printf("This application can be used to verify AsmJit build options and to verify the\n"); 30 printf("environment where it runs. For example to check CPU extensions available, system\n"); 31 printf("hardening (RWX restrictions), large page support, and virtual memory allocations.\n"); 32 printf("\n"); 33 } 34 35 const char* stringify_bool(bool b) noexcept { return b ? "true" : "false"; }; 36 const char* stringify_result(Error err) noexcept { return err == Error::kOk ? "success" : DebugUtils::error_as_string(err); }; 37 38 using VoidFunc = void (ASMJIT_CDECL*)(void); 39 40 #if !defined(ASMJIT_NO_JIT) 41 42 #if !defined(ASMJIT_NO_X86) && ASMJIT_ARCH_X86 != 0 43 #define TEST_ENVIRONMENT_HAS_JIT 44 45 static void emit_void_function(CodeHolder& code) noexcept { 46 x86::Assembler a(&code); 47 a.ret(); 48 } 49 #endif 50 51 #if !defined(ASMJIT_NO_AARCH64) && ASMJIT_ARCH_ARM == 64 52 #define TEST_ENVIRONMENT_HAS_JIT 53 54 static void emit_void_function(CodeHolder& code) noexcept { 55 a64::Assembler a(&code); 56 a.ret(a64::x30); 57 } 58 #endif 59 60 #if defined(TEST_ENVIRONMENT_HAS_JIT) 61 static void* offset_pointer(void* ptr, size_t offset) noexcept { 62 return static_cast<void*>(static_cast<uint8_t*>(ptr) + offset); 63 } 64 65 static size_t write_empty_function_at(void* ptr, size_t size) noexcept { 66 printf(" Write JIT code at addr : %p\n", ptr); 67 68 CodeHolder code; 69 Error err = code.init(Environment::host()); 70 if (err != Error::kOk) { 71 printf( "Failed to initialize CodeHolder (%s)\n", DebugUtils::error_as_string(err)); 72 return 0; 73 } 74 75 emit_void_function(code); 76 code.flatten(); 77 code.copy_flattened_data(ptr, size); 78 79 return code.code_size(); 80 } 81 82 static void flush_instruction_cache(void* ptr, size_t size) noexcept { 83 printf(" Flush JIT code at addr : %p [size=%zu]\n", ptr, size); 84 VirtMem::flush_instruction_cache(ptr, size); 85 } 86 87 static void invoke_void_function(void* ptr) noexcept { 88 printf(" Invoke JIT code at addr : %p\n", ptr); 89 90 // In case it crashes, we want to have the output flushed. 91 fflush(stdout); 92 93 VoidFunc func = reinterpret_cast<VoidFunc>(ptr); 94 func(); 95 } 96 #endif 97 98 static void print_virt_mem_info_and_test_execution() noexcept { 99 using MemoryFlags = VirtMem::MemoryFlags; 100 using HardenedRuntimeInfo = VirtMem::HardenedRuntimeInfo; 101 using HardenedRuntimeFlags = VirtMem::HardenedRuntimeFlags; 102 103 // Size of a virtual memory allocation. 104 constexpr size_t kVMemAllocSize = 65536; 105 106 // Offset to the first function to execute (must be greater than 8 for UBSAN to work). 107 [[maybe_unused]] 108 constexpr size_t kVirtFuncOffset = 64; 109 110 size_t large_page_size = VirtMem::large_page_size(); 111 HardenedRuntimeInfo rti = VirtMem::hardened_runtime_info(); 112 113 printf("Large/Huge Pages Info:\n"); 114 printf(" Large pages supported : %s\n", stringify_bool(large_page_size != 0u)); 115 if (large_page_size >= 1024 * 1024) { 116 printf(" Large page size : %zu MiB\n", large_page_size / (1024u * 1024u)); 117 } 118 else if (large_page_size) { 119 printf(" Large page size : %zu KiB\n", large_page_size / 1024u); 120 } 121 printf("\n"); 122 123 printf("Hardened Environment Info:\n"); 124 printf(" Hardening was detected : %s\n", stringify_bool(rti.has_flag(HardenedRuntimeFlags::kEnabled ))); 125 printf(" MAP_JIT is available : %s\n", stringify_bool(rti.has_flag(HardenedRuntimeFlags::kMapJit ))); 126 printf(" DualMapping is available: %s\n", stringify_bool(rti.has_flag(HardenedRuntimeFlags::kDualMapping))); 127 printf("\n"); 128 129 if (!rti.has_flag(HardenedRuntimeFlags::kEnabled)) { 130 printf("Virtual Memory Allocation (RWX):\n"); 131 132 void* ptr = nullptr; 133 Error result = VirtMem::alloc(&ptr, kVMemAllocSize, MemoryFlags::kAccessRWX); 134 printf(" Alloc virt memory (RWX) : %s\n", stringify_result(result)); 135 136 if (result == Error::kOk) { 137 #if defined(TEST_ENVIRONMENT_HAS_JIT) 138 void* func_ptr = offset_pointer(ptr, kVirtFuncOffset); 139 size_t func_size = write_empty_function_at(func_ptr, kVMemAllocSize); 140 141 if (func_size) { 142 flush_instruction_cache(func_ptr, func_size); 143 invoke_void_function(func_ptr); 144 } 145 #endif // TEST_ENVIRONMENT_HAS_JIT 146 147 result = VirtMem::release(ptr, kVMemAllocSize); 148 printf(" Release virt memory : %s\n", stringify_result(result)); 149 } 150 151 printf("\n"); 152 } 153 154 { 155 printf("Virtual Memory Allocation (RW - Flipping Permissions RW<->RX):\n"); 156 157 void* ptr = nullptr; 158 Error result = VirtMem::alloc(&ptr, kVMemAllocSize, MemoryFlags::kAccessRW | MemoryFlags::kMMapMaxAccessRWX); 159 printf(" Alloc virt memory (RW) : %s (allocation uses kMMapMaxAccessRWX)\n", stringify_result(result)); 160 161 if (result == Error::kOk) { 162 #if defined(TEST_ENVIRONMENT_HAS_JIT) 163 void* func_ptr = offset_pointer(ptr, kVirtFuncOffset); 164 size_t func_size = write_empty_function_at(func_ptr, kVMemAllocSize); 165 #endif // TEST_ENVIRONMENT_HAS_JIT 166 167 result = VirtMem::protect(ptr, kVMemAllocSize, MemoryFlags::kAccessRX); 168 printf(" Protect virt memory (RX): %s\n", stringify_result(result)); 169 170 #if defined(TEST_ENVIRONMENT_HAS_JIT) 171 if (func_size) { 172 flush_instruction_cache(func_ptr, func_size); 173 invoke_void_function(func_ptr); 174 } 175 #endif // TEST_ENVIRONMENT_HAS_JIT 176 177 result = VirtMem::protect(ptr, kVMemAllocSize, MemoryFlags::kAccessRW); 178 printf(" Protect virt memory (RW): %s\n", stringify_result(result)); 179 180 result = VirtMem::release(ptr, kVMemAllocSize); 181 printf(" Release virt memory (RW): %s\n", stringify_result(result)); 182 } 183 184 printf("\n"); 185 } 186 187 if (rti.has_flag(HardenedRuntimeFlags::kMapJit)) { 188 printf("Virtual Memory Allocation (MAP_JIT):\n"); 189 190 void* ptr = nullptr; 191 Error result = VirtMem::alloc(&ptr, kVMemAllocSize, MemoryFlags::kAccessRWX | MemoryFlags::kMMapEnableMapJit); 192 printf(" Alloc virt mem (RWX) : %s (allocation uses kMMapEnableMapJit)\n", stringify_result(result)); 193 194 if (result == Error::kOk) { 195 printf(" Protect JIT Memory (RW) : (per-thread protection)\n"); 196 VirtMem::protect_jit_memory(VirtMem::ProtectJitAccess::kReadWrite); 197 198 #if defined(TEST_ENVIRONMENT_HAS_JIT) 199 void* func_ptr = offset_pointer(ptr, kVirtFuncOffset); 200 size_t func_size = write_empty_function_at(func_ptr, kVMemAllocSize); 201 #endif // TEST_ENVIRONMENT_HAS_JIT 202 203 printf(" Protect JIT Memory (RX) : (per-thread protection)\n"); 204 VirtMem::protect_jit_memory(VirtMem::ProtectJitAccess::kReadExecute); 205 206 #if defined(TEST_ENVIRONMENT_HAS_JIT) 207 if (func_size) { 208 flush_instruction_cache(func_ptr, func_size); 209 invoke_void_function(func_ptr); 210 } 211 #endif // TEST_ENVIRONMENT_HAS_JIT 212 213 result = VirtMem::release(ptr, kVMemAllocSize); 214 printf(" Release virt memory : %s\n", stringify_result(result)); 215 } 216 217 printf("\n"); 218 } 219 220 if (rti.has_flag(HardenedRuntimeFlags::kDualMapping)) { 221 printf("Virtual Memory Allocation (Dual Mapping):\n"); 222 223 VirtMem::DualMapping dm {}; 224 Error result = VirtMem::alloc_dual_mapping(Out(dm), kVMemAllocSize, MemoryFlags::kAccessRWX); 225 printf(" Alloc dual mem (RW+RX) : %s\n", stringify_result(result)); 226 227 if (result == Error::kOk) { 228 #if defined(TEST_ENVIRONMENT_HAS_JIT) 229 size_t func_size = write_empty_function_at(offset_pointer(dm.rw, kVirtFuncOffset), kVMemAllocSize); 230 if (func_size) { 231 flush_instruction_cache(offset_pointer(dm.rx, kVirtFuncOffset), func_size); 232 invoke_void_function(offset_pointer(dm.rx, kVirtFuncOffset)); 233 } 234 #endif // TEST_ENVIRONMENT_HAS_JIT 235 236 result = VirtMem::release_dual_mapping(dm, kVMemAllocSize); 237 printf(" Release dual mem (RW+RX): %s\n", stringify_result(result)); 238 } 239 240 printf("\n"); 241 } 242 } 243 244 #if defined(TEST_ENVIRONMENT_HAS_JIT) 245 static void print_jit_runtime_info_and_test_execution_with_params(const JitAllocator::CreateParams* params, const char* params_name) noexcept { 246 printf("JitRuntime (%s):\n", params_name); 247 248 JitRuntime rt(params); 249 CodeHolder code; 250 251 Error result = code.init(rt.environment()); 252 printf(" CodeHolder init result : %s\n", stringify_result(result)); 253 254 if (result != Error::kOk) { 255 return; 256 } 257 258 emit_void_function(code); 259 VoidFunc fn; 260 261 result = rt.add(&fn, &code); 262 printf(" Runtime.add() result : %s\n", stringify_result(result)); 263 264 if (result == Error::kOk) { 265 invoke_void_function((void*)fn); 266 267 result = rt.release(fn); 268 printf(" Runtime.release() result: %s\n", stringify_result(result)); 269 } 270 271 printf("\n"); 272 } 273 274 static void print_jit_runtime_info_and_test_execution() noexcept { 275 print_jit_runtime_info_and_test_execution_with_params(nullptr, "<no params>"); 276 277 if (VirtMem::large_page_size()) { 278 JitAllocator::CreateParams p{}; 279 p.options = JitAllocatorOptions::kUseLargePages; 280 281 print_jit_runtime_info_and_test_execution_with_params(&p, "large pages"); 282 } 283 } 284 #endif // TEST_ENVIRONMENT_HAS_JIT 285 286 #endif // !ASMJIT_NO_JIT 287 288 int main() { 289 print_app_info(); 290 print_build_options(); 291 print_cpu_info(); 292 293 #if !defined(ASMJIT_NO_JIT) 294 print_virt_mem_info_and_test_execution(); 295 #endif // !ASMJIT_NO_JIT 296 297 #if !defined(ASMJIT_NO_JIT) && defined(TEST_ENVIRONMENT_HAS_JIT) 298 print_jit_runtime_info_and_test_execution(); 299 #endif // !ASMJIT_NO_JIT && TEST_ENVIRONMENT_HAS_JIT 300 301 return 0; 302 }