odin-blend2d

Odin bindings to Blend2D
Log | Files | Refs | README | LICENSE

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 }