mirror of
https://github.com/asmjit/asmjit.git
synced 2025-12-17 12:34:35 +03:00
Added support for mach_vm_remap() for dual mapping
mach_vm_remap() allows to create a dual mapping without having to use a file descriptor, which has to open a file or shm memory. The problem is that the recent macos version started displaying a popup message when such file is opened and that annoyed a lot of users. Thus, the initial code-path is no longer used, and mach_vm_remap() is used instead. This change only applies for x86 macs. Apple silicon doesn't allow dual mapping and instead uses MAP_JIT.
This commit is contained in:
41
.github/workflows/build-config.json
vendored
41
.github/workflows/build-config.json
vendored
@@ -12,37 +12,14 @@
|
|||||||
],
|
],
|
||||||
|
|
||||||
"tests": [
|
"tests": [
|
||||||
{
|
{ "optional": true, "cmd": ["asmjit_test_unit", "--quick"] },
|
||||||
"cmd": ["asmjit_test_unit", "--quick"],
|
{ "optional": true, "cmd": ["asmjit_test_assembler"] },
|
||||||
"optional": true
|
{ "optional": true, "cmd": ["asmjit_test_assembler", "--validate"] },
|
||||||
},
|
{ "optional": true, "cmd": ["asmjit_test_emitters"] },
|
||||||
{
|
{ "optional": true, "cmd": ["asmjit_test_execute"] },
|
||||||
"cmd": ["asmjit_test_assembler"],
|
{ "optional": true, "cmd": ["asmjit_test_compiler"] },
|
||||||
"optional": true
|
{ "optional": true, "cmd": ["asmjit_test_instinfo"] },
|
||||||
},
|
{ "optional": true, "cmd": ["asmjit_test_x86_sections"] },
|
||||||
{
|
{ "optional": true, "cmd": ["asmjit_test_perf", "--quick"] }
|
||||||
"cmd": ["asmjit_test_assembler", "--validate"],
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cmd": ["asmjit_test_emitters"],
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cmd": ["asmjit_test_compiler"],
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cmd": ["asmjit_test_instinfo"],
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cmd": ["asmjit_test_x86_sections"],
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cmd": ["asmjit_test_perf", "--quick"],
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
@@ -14,12 +14,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout"
|
- name: "Checkout"
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: "Setup node.js"
|
- name: "Setup node.js"
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: "16"
|
node-version: "*"
|
||||||
|
|
||||||
- name: "Check Enumerations"
|
- name: "Check Enumerations"
|
||||||
run: |
|
run: |
|
||||||
@@ -141,18 +141,18 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout"
|
- name: "Checkout"
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
path: "source"
|
path: "source"
|
||||||
|
|
||||||
- name: "Checkout Build Actions"
|
- name: "Checkout Build Actions"
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: build-actions/build-actions
|
repository: build-actions/build-actions
|
||||||
path: "build-actions"
|
path: "build-actions"
|
||||||
|
|
||||||
- name: "Python"
|
- name: "Python"
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
|
|
||||||
|
|||||||
@@ -666,6 +666,7 @@ if (NOT ASMJIT_EMBED)
|
|||||||
CFLAGS_REL ${ASMJIT_PRIVATE_CFLAGS_REL})
|
CFLAGS_REL ${ASMJIT_PRIVATE_CFLAGS_REL})
|
||||||
|
|
||||||
foreach(_target asmjit_test_emitters
|
foreach(_target asmjit_test_emitters
|
||||||
|
asmjit_test_execute
|
||||||
asmjit_test_x86_sections)
|
asmjit_test_x86_sections)
|
||||||
asmjit_add_target(${_target} TEST
|
asmjit_add_target(${_target} TEST
|
||||||
SOURCES test/${_target}.cpp
|
SOURCES test/${_target}.cpp
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ ASMJIT_FAVOR_SIZE const char* DebugUtils::errorAsString(Error err) noexcept {
|
|||||||
"ExpressionOverflow\0"
|
"ExpressionOverflow\0"
|
||||||
"FailedToOpenAnonymousMemory\0"
|
"FailedToOpenAnonymousMemory\0"
|
||||||
"FailedToOpenFile\0"
|
"FailedToOpenFile\0"
|
||||||
|
"ProtectionFailure\0"
|
||||||
"<Unknown>\0";
|
"<Unknown>\0";
|
||||||
|
|
||||||
static const uint16_t sErrorIndex[] = {
|
static const uint16_t sErrorIndex[] = {
|
||||||
@@ -94,7 +95,7 @@ ASMJIT_FAVOR_SIZE const char* DebugUtils::errorAsString(Error err) noexcept {
|
|||||||
247, 264, 283, 298, 314, 333, 352, 370, 392, 410, 429, 444, 460, 474, 488,
|
247, 264, 283, 298, 314, 333, 352, 370, 392, 410, 429, 444, 460, 474, 488,
|
||||||
508, 533, 551, 573, 595, 612, 629, 645, 661, 677, 694, 709, 724, 744, 764,
|
508, 533, 551, 573, 595, 612, 629, 645, 661, 677, 694, 709, 724, 744, 764,
|
||||||
784, 817, 837, 852, 869, 888, 909, 929, 943, 964, 978, 996, 1012, 1028, 1047,
|
784, 817, 837, 852, 869, 888, 909, 929, 943, 964, 978, 996, 1012, 1028, 1047,
|
||||||
1073, 1088, 1104, 1119, 1134, 1164, 1188, 1207, 1235, 1252
|
1073, 1088, 1104, 1119, 1134, 1164, 1188, 1207, 1235, 1252, 1270
|
||||||
};
|
};
|
||||||
// @EnumStringEnd@
|
// @EnumStringEnd@
|
||||||
|
|
||||||
|
|||||||
@@ -334,6 +334,10 @@ enum ErrorCode : uint32_t {
|
|||||||
//! \note This is a generic error that is used by internal filesystem API.
|
//! \note This is a generic error that is used by internal filesystem API.
|
||||||
kErrorFailedToOpenFile,
|
kErrorFailedToOpenFile,
|
||||||
|
|
||||||
|
//! Protection failure can be returned from a virtual memory allocator or when trying to change memory access
|
||||||
|
//! permissions.
|
||||||
|
kErrorProtectionFailure,
|
||||||
|
|
||||||
// @EnumValuesEnd@
|
// @EnumValuesEnd@
|
||||||
|
|
||||||
//! Count of AsmJit error codes.
|
//! Count of AsmJit error codes.
|
||||||
|
|||||||
@@ -76,8 +76,6 @@
|
|||||||
#define MAP_ANONYMOUS MAP_ANON
|
#define MAP_ANONYMOUS MAP_ANON
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define ASMJIT_ANONYMOUS_MEMORY_USE_FD
|
|
||||||
|
|
||||||
// Android NDK doesn't provide `shm_open()` and `shm_unlink()`.
|
// Android NDK doesn't provide `shm_open()` and `shm_unlink()`.
|
||||||
#if !defined(__BIONIC__) && !defined(ASMJIT_NO_SHM_OPEN)
|
#if !defined(__BIONIC__) && !defined(ASMJIT_NO_SHM_OPEN)
|
||||||
#define ASMJIT_HAS_SHM_OPEN
|
#define ASMJIT_HAS_SHM_OPEN
|
||||||
@@ -89,18 +87,60 @@
|
|||||||
#define ASMJIT_VM_SHM_DETECT 1
|
#define ASMJIT_VM_SHM_DETECT 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__APPLE__) && TARGET_OS_OSX && ASMJIT_ARCH_ARM >= 64
|
#if defined(__APPLE__) && TARGET_OS_OSX
|
||||||
|
#if ASMJIT_ARCH_X86 != 0
|
||||||
|
#define ASMJIT_ANONYMOUS_MEMORY_USE_MACH_VM_REMAP
|
||||||
|
#endif
|
||||||
|
#if ASMJIT_ARCH_ARM >= 64
|
||||||
#define ASMJIT_HAS_PTHREAD_JIT_WRITE_PROTECT_NP
|
#define ASMJIT_HAS_PTHREAD_JIT_WRITE_PROTECT_NP
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__APPLE__) && ASMJIT_ARCH_X86 == 0
|
||||||
|
#define ASMJIT_NO_DUAL_MAPPING
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(__NetBSD__) && defined(MAP_REMAPDUP) && defined(PROT_MPROTECT)
|
#if defined(__NetBSD__) && defined(MAP_REMAPDUP) && defined(PROT_MPROTECT)
|
||||||
#undef ASMJIT_ANONYMOUS_MEMORY_USE_FD
|
|
||||||
#define ASMJIT_ANONYMOUS_MEMORY_USE_REMAPDUP
|
#define ASMJIT_ANONYMOUS_MEMORY_USE_REMAPDUP
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !defined(ASMJIT_ANONYMOUS_MEMORY_USE_REMAPDUP) && \
|
||||||
|
!defined(ASMJIT_ANONYMOUS_MEMORY_USE_MACH_VM_REMAP) && \
|
||||||
|
!defined(ASMJIT_NO_DUAL_MAPPING)
|
||||||
|
#define ASMJIT_ANONYMOUS_MEMORY_USE_FD
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
|
#if defined(ASMJIT_ANONYMOUS_MEMORY_USE_MACH_VM_REMAP)
|
||||||
|
#include <mach/mach.h>
|
||||||
|
#include <mach/mach_time.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
#ifdef mig_external
|
||||||
|
mig_external
|
||||||
|
#else
|
||||||
|
extern
|
||||||
|
#endif
|
||||||
|
kern_return_t mach_vm_remap(
|
||||||
|
vm_map_t target_task,
|
||||||
|
mach_vm_address_t *target_address,
|
||||||
|
mach_vm_size_t size,
|
||||||
|
mach_vm_offset_t mask,
|
||||||
|
int flags,
|
||||||
|
vm_map_t src_task,
|
||||||
|
mach_vm_address_t src_address,
|
||||||
|
boolean_t copy,
|
||||||
|
vm_prot_t *cur_protection,
|
||||||
|
vm_prot_t *max_protection,
|
||||||
|
vm_inherit_t inheritance
|
||||||
|
);
|
||||||
|
|
||||||
|
} // {extern "C"}
|
||||||
|
#endif
|
||||||
|
|
||||||
ASMJIT_BEGIN_SUB_NAMESPACE(VirtMem)
|
ASMJIT_BEGIN_SUB_NAMESPACE(VirtMem)
|
||||||
|
|
||||||
// Virtual Memory Utilities
|
// Virtual Memory Utilities
|
||||||
@@ -757,8 +797,7 @@ static inline int mmMapJitFromMemoryFlags(MemoryFlags memoryFlags) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline bool hasDualMappingSupport() noexcept {
|
static inline bool hasDualMappingSupport() noexcept {
|
||||||
#if defined(__APPLE__) && TARGET_OS_OSX && ASMJIT_ARCH_X86 == 0
|
#if defined(ASMJIT_NO_DUAL_MAPPING)
|
||||||
// Apple platforms don't allow dual-mapping on non-x86 hardware.
|
|
||||||
return false;
|
return false;
|
||||||
#else
|
#else
|
||||||
return true;
|
return true;
|
||||||
@@ -850,6 +889,7 @@ Error protect(void* p, size_t size, MemoryFlags memoryFlags) noexcept {
|
|||||||
// Virtual Memory [Posix] - Dual Mapping
|
// Virtual Memory [Posix] - Dual Mapping
|
||||||
// =====================================
|
// =====================================
|
||||||
|
|
||||||
|
#if !defined(ASMJIT_NO_DUAL_MAPPING)
|
||||||
static Error unmapDualMapping(DualMapping* dm, size_t size) noexcept {
|
static Error unmapDualMapping(DualMapping* dm, size_t size) noexcept {
|
||||||
Error err1 = unmapMemory(dm->rx, size);
|
Error err1 = unmapMemory(dm->rx, size);
|
||||||
Error err2 = kErrorOk;
|
Error err2 = kErrorOk;
|
||||||
@@ -865,6 +905,7 @@ static Error unmapDualMapping(DualMapping* dm, size_t size) noexcept {
|
|||||||
dm->rw = nullptr;
|
dm->rw = nullptr;
|
||||||
return kErrorOk;
|
return kErrorOk;
|
||||||
}
|
}
|
||||||
|
#endif // !ASMJIT_NO_DUAL_MAPPING
|
||||||
|
|
||||||
#if defined(ASMJIT_ANONYMOUS_MEMORY_USE_REMAPDUP)
|
#if defined(ASMJIT_ANONYMOUS_MEMORY_USE_REMAPDUP)
|
||||||
static Error allocDualMappingUsingRemapdup(DualMapping* dmOut, size_t size, MemoryFlags memoryFlags) noexcept {
|
static Error allocDualMappingUsingRemapdup(DualMapping* dmOut, size_t size, MemoryFlags memoryFlags) noexcept {
|
||||||
@@ -897,16 +938,105 @@ static Error allocDualMappingUsingRemapdup(DualMapping* dmOut, size_t size, Memo
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Error allocDualMapping(DualMapping* dm, size_t size, MemoryFlags memoryFlags) noexcept {
|
#if defined(ASMJIT_ANONYMOUS_MEMORY_USE_MACH_VM_REMAP)
|
||||||
dm->rx = nullptr;
|
static Error asmjitErrorFromKernResult(kern_return_t result) noexcept {
|
||||||
dm->rw = nullptr;
|
switch (result) {
|
||||||
|
case KERN_PROTECTION_FAILURE:
|
||||||
|
return DebugUtils::errored(kErrorProtectionFailure);
|
||||||
|
case KERN_NO_SPACE:
|
||||||
|
return DebugUtils::errored(kErrorOutOfMemory);
|
||||||
|
case KERN_INVALID_ARGUMENT:
|
||||||
|
return DebugUtils::errored(kErrorInvalidArgument);
|
||||||
|
default:
|
||||||
|
return DebugUtils::errored(kErrorInvalidState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (off_t(size) <= 0)
|
static Error allocDualMappingUsingMachVmRemap(DualMapping* dmOut, size_t size, MemoryFlags memoryFlags) noexcept {
|
||||||
return DebugUtils::errored(size == 0 ? kErrorInvalidArgument : kErrorTooLarge);
|
DualMapping dm {};
|
||||||
|
|
||||||
#if defined(ASMJIT_ANONYMOUS_MEMORY_USE_REMAPDUP)
|
MemoryFlags mmapFlags = MemoryFlags::kAccessReadWrite | (memoryFlags & MemoryFlags::kMapShared);
|
||||||
return allocDualMappingUsingRemapdup(dm, size, memoryFlags);
|
ASMJIT_PROPAGATE(mapMemory(&dm.rx, size, mmapFlags));
|
||||||
#elif defined(ASMJIT_ANONYMOUS_MEMORY_USE_FD)
|
|
||||||
|
vm_prot_t curProt;
|
||||||
|
vm_prot_t maxProt;
|
||||||
|
|
||||||
|
int rwProtectFlags = VM_PROT_READ | VM_PROT_WRITE;
|
||||||
|
int rxProtectFlags = VM_PROT_READ;
|
||||||
|
|
||||||
|
if (Support::test(memoryFlags, MemoryFlags::kAccessExecute))
|
||||||
|
rxProtectFlags |= VM_PROT_EXECUTE;
|
||||||
|
|
||||||
|
kern_return_t result {};
|
||||||
|
do {
|
||||||
|
vm_map_t task = mach_task_self();
|
||||||
|
mach_vm_address_t remappedAddr {};
|
||||||
|
|
||||||
|
#if defined(VM_FLAGS_RANDOM_ADDR)
|
||||||
|
int remapFlags = VM_FLAGS_ANYWHERE | VM_FLAGS_RANDOM_ADDR;
|
||||||
|
#else
|
||||||
|
int remapFlags = VM_FLAGS_ANYWHERE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Try to remap the existing memory into a different address.
|
||||||
|
result = mach_vm_remap(
|
||||||
|
task, // target_task
|
||||||
|
&remappedAddr, // target_address
|
||||||
|
size, // size
|
||||||
|
0, // mask
|
||||||
|
remapFlags, // flags
|
||||||
|
task, // src_task
|
||||||
|
(mach_vm_address_t)dm.rx, // src_address
|
||||||
|
false, // copy
|
||||||
|
&curProt, // cur_protection
|
||||||
|
&maxProt, // max_protection
|
||||||
|
VM_INHERIT_DEFAULT); // inheritance
|
||||||
|
|
||||||
|
if (result != KERN_SUCCESS)
|
||||||
|
break;
|
||||||
|
|
||||||
|
dm.rw = (void*)remappedAddr;
|
||||||
|
|
||||||
|
// Now, try to change permissions of both map regions into RW and RX. The vm_protect()
|
||||||
|
// API is used twice as we also want to set maximum permissions, so nobody would be
|
||||||
|
// allowed to change the RX region back to RW or RWX (if RWX is allowed).
|
||||||
|
uint32_t i;
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
bool setMaximum = (i == 0);
|
||||||
|
|
||||||
|
result = vm_protect(
|
||||||
|
task, // target_task
|
||||||
|
(vm_address_t)dm.rx, // address
|
||||||
|
size, // size
|
||||||
|
setMaximum, // set_maximum
|
||||||
|
rxProtectFlags); // new_protection
|
||||||
|
|
||||||
|
if (result != KERN_SUCCESS)
|
||||||
|
break;
|
||||||
|
|
||||||
|
result = vm_protect(task, // target_task
|
||||||
|
(vm_address_t)dm.rw, // address
|
||||||
|
size, // size
|
||||||
|
setMaximum, // set_maximum
|
||||||
|
rwProtectFlags); // new_protection
|
||||||
|
|
||||||
|
if (result != KERN_SUCCESS)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
if (result != KERN_SUCCESS) {
|
||||||
|
unmapDualMapping(&dm, size);
|
||||||
|
return DebugUtils::errored(asmjitErrorFromKernResult(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
*dmOut = dm;
|
||||||
|
return kErrorOk;
|
||||||
|
}
|
||||||
|
#endif // ASMJIT_ANONYMOUS_MEMORY_USE_MACH_VM_REMAP
|
||||||
|
|
||||||
|
#if defined(ASMJIT_ANONYMOUS_MEMORY_USE_FD)
|
||||||
|
static Error allocDualMappingUsingFile(DualMapping* dm, size_t size, MemoryFlags memoryFlags) noexcept {
|
||||||
bool preferTmpOverDevShm = Support::test(memoryFlags, MemoryFlags::kMappingPreferTmp);
|
bool preferTmpOverDevShm = Support::test(memoryFlags, MemoryFlags::kMappingPreferTmp);
|
||||||
if (!preferTmpOverDevShm) {
|
if (!preferTmpOverDevShm) {
|
||||||
AnonymousMemoryStrategy strategy;
|
AnonymousMemoryStrategy strategy;
|
||||||
@@ -932,13 +1062,39 @@ Error allocDualMapping(DualMapping* dm, size_t size, MemoryFlags memoryFlags) no
|
|||||||
dm->rx = ptr[0];
|
dm->rx = ptr[0];
|
||||||
dm->rw = ptr[1];
|
dm->rw = ptr[1];
|
||||||
return kErrorOk;
|
return kErrorOk;
|
||||||
|
}
|
||||||
|
#endif // ASMJIT_ANONYMOUS_MEMORY_USE_FD
|
||||||
|
|
||||||
|
Error allocDualMapping(DualMapping* dm, size_t size, MemoryFlags memoryFlags) noexcept {
|
||||||
|
dm->rx = nullptr;
|
||||||
|
dm->rw = nullptr;
|
||||||
|
|
||||||
|
#if defined(ASMJIT_NO_DUAL_MAPPING)
|
||||||
|
DebugUtils::unused(size, memoryFlags);
|
||||||
|
return DebugUtils::errored(kErrorFeatureNotEnabled);
|
||||||
#else
|
#else
|
||||||
#error "[asmjit] VirtMem::allocDualMapping() doesn't have implementation for the target OS and compiler"
|
if (off_t(size) <= 0)
|
||||||
|
return DebugUtils::errored(size == 0 ? kErrorInvalidArgument : kErrorTooLarge);
|
||||||
|
|
||||||
|
#if defined(ASMJIT_ANONYMOUS_MEMORY_USE_REMAPDUP)
|
||||||
|
return allocDualMappingUsingRemapdup(dm, size, memoryFlags);
|
||||||
|
#elif defined(ASMJIT_ANONYMOUS_MEMORY_USE_MACH_VM_REMAP)
|
||||||
|
return allocDualMappingUsingMachVmRemap(dm, size, memoryFlags);
|
||||||
|
#elif defined(ASMJIT_ANONYMOUS_MEMORY_USE_FD)
|
||||||
|
return allocDualMappingUsingFile(dm, size, memoryFlags);
|
||||||
|
#else
|
||||||
|
#error "[asmjit] VirtMem::allocDualMapping() doesn't have implementation for the target OS or architecture"
|
||||||
#endif
|
#endif
|
||||||
|
#endif // ASMJIT_NO_DUAL_MAPPING
|
||||||
}
|
}
|
||||||
|
|
||||||
Error releaseDualMapping(DualMapping* dm, size_t size) noexcept {
|
Error releaseDualMapping(DualMapping* dm, size_t size) noexcept {
|
||||||
|
#if defined(ASMJIT_NO_DUAL_MAPPING)
|
||||||
|
DebugUtils::unused(dm, size);
|
||||||
|
return DebugUtils::errored(kErrorFeatureNotEnabled);
|
||||||
|
#else
|
||||||
return unmapDualMapping(dm, size);
|
return unmapDualMapping(dm, size);
|
||||||
|
#endif // ASMJIT_NO_DUAL_MAPPING
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1039,8 +1195,9 @@ UNIT(virt_mem) {
|
|||||||
|
|
||||||
INFO("VirtMem::hardenedRuntimeInfo():");
|
INFO("VirtMem::hardenedRuntimeInfo():");
|
||||||
INFO(" flags:");
|
INFO(" flags:");
|
||||||
INFO(" kEnabled: %s", Support::test(hardenedFlags, VirtMem::HardenedRuntimeFlags::kEnabled) ? "true" : "false");
|
INFO(" kEnabled: %s" , Support::test(hardenedFlags, VirtMem::HardenedRuntimeFlags::kEnabled ) ? "true" : "false");
|
||||||
INFO(" kMapJit: %s", Support::test(hardenedFlags, VirtMem::HardenedRuntimeFlags::kMapJit) ? "true" : "false");
|
INFO(" kMapJit: %s" , Support::test(hardenedFlags, VirtMem::HardenedRuntimeFlags::kMapJit ) ? "true" : "false");
|
||||||
|
INFO(" kDualMapping: %s", Support::test(hardenedFlags, VirtMem::HardenedRuntimeFlags::kDualMapping) ? "true" : "false");
|
||||||
}
|
}
|
||||||
|
|
||||||
ASMJIT_END_NAMESPACE
|
ASMJIT_END_NAMESPACE
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#ifndef ASMJIT_NO_JIT
|
#ifndef ASMJIT_NO_JIT
|
||||||
|
|
||||||
#include "../core/globals.h"
|
#include "../core/globals.h"
|
||||||
|
#include "../core/support.h"
|
||||||
|
|
||||||
ASMJIT_BEGIN_NAMESPACE
|
ASMJIT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
@@ -218,15 +219,28 @@ enum class HardenedRuntimeFlags : uint32_t {
|
|||||||
//! Read+Write+Execute can only be allocated with MAP_JIT flag (Apple specific, only available on Apple platforms).
|
//! Read+Write+Execute can only be allocated with MAP_JIT flag (Apple specific, only available on Apple platforms).
|
||||||
kMapJit = 0x00000002u,
|
kMapJit = 0x00000002u,
|
||||||
|
|
||||||
//! Read+Write+Executa can be allocated with dual mapping approach (one region with RW and the other with RX).
|
//! Read+Write+Execute can be allocated with dual mapping approach (one region with RW and the other with RX).
|
||||||
kDualMapping = 0x00000004u
|
kDualMapping = 0x00000004u
|
||||||
};
|
};
|
||||||
ASMJIT_DEFINE_ENUM_FLAGS(HardenedRuntimeFlags)
|
ASMJIT_DEFINE_ENUM_FLAGS(HardenedRuntimeFlags)
|
||||||
|
|
||||||
//! Hardened runtime information.
|
//! Hardened runtime information.
|
||||||
struct HardenedRuntimeInfo {
|
struct HardenedRuntimeInfo {
|
||||||
|
//! \name Members
|
||||||
|
//! \{
|
||||||
|
|
||||||
//! Hardened runtime flags.
|
//! Hardened runtime flags.
|
||||||
HardenedRuntimeFlags flags;
|
HardenedRuntimeFlags flags;
|
||||||
|
|
||||||
|
//! \}
|
||||||
|
|
||||||
|
//! \name Accessors
|
||||||
|
//! \{
|
||||||
|
|
||||||
|
//! Tests whether the hardened runtime `flag` is set.
|
||||||
|
ASMJIT_INLINE_NODEBUG bool hasFlag(HardenedRuntimeFlags flag) const noexcept { return Support::test(flags, flag); }
|
||||||
|
|
||||||
|
//! \}
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Returns runtime features provided by the OS.
|
//! Returns runtime features provided by the OS.
|
||||||
|
|||||||
@@ -2827,7 +2827,7 @@ public:
|
|||||||
ASMJIT_INST_3x(vpand, Vpand, Vec, Vec, Mem) // AVX+
|
ASMJIT_INST_3x(vpand, Vpand, Vec, Vec, Mem) // AVX+
|
||||||
ASMJIT_INST_3x(vpandd, Vpandd, Vec, Vec, Vec) // AVX512_F{kz|b32}
|
ASMJIT_INST_3x(vpandd, Vpandd, Vec, Vec, Vec) // AVX512_F{kz|b32}
|
||||||
ASMJIT_INST_3x(vpandd, Vpandd, Vec, Vec, Mem) // AVX512_F{kz|b32}
|
ASMJIT_INST_3x(vpandd, Vpandd, Vec, Vec, Mem) // AVX512_F{kz|b32}
|
||||||
ASMJIT_INST_3x(vpandn, Vpandn, Vec, Vec, Vec) // AV+
|
ASMJIT_INST_3x(vpandn, Vpandn, Vec, Vec, Vec) // AVX+
|
||||||
ASMJIT_INST_3x(vpandn, Vpandn, Vec, Vec, Mem) // AVX+
|
ASMJIT_INST_3x(vpandn, Vpandn, Vec, Vec, Mem) // AVX+
|
||||||
ASMJIT_INST_3x(vpandnd, Vpandnd, Vec, Vec, Vec) // AVX512_F{kz|b32}
|
ASMJIT_INST_3x(vpandnd, Vpandnd, Vec, Vec, Vec) // AVX512_F{kz|b32}
|
||||||
ASMJIT_INST_3x(vpandnd, Vpandnd, Vec, Vec, Mem) // AVX512_F{kz|b32}
|
ASMJIT_INST_3x(vpandnd, Vpandnd, Vec, Vec, Mem) // AVX512_F{kz|b32}
|
||||||
@@ -3186,7 +3186,7 @@ public:
|
|||||||
ASMJIT_INST_2x(vpopcntq, Vpopcntq, Vec, Mem) // AVX512_VPOPCNTDQ{kz|b64}
|
ASMJIT_INST_2x(vpopcntq, Vpopcntq, Vec, Mem) // AVX512_VPOPCNTDQ{kz|b64}
|
||||||
ASMJIT_INST_2x(vpopcntw, Vpopcntw, Vec, Vec) // AVX512_BITALG{kz|b32}
|
ASMJIT_INST_2x(vpopcntw, Vpopcntw, Vec, Vec) // AVX512_BITALG{kz|b32}
|
||||||
ASMJIT_INST_2x(vpopcntw, Vpopcntw, Vec, Mem) // AVX512_BITALG{kz|b32}
|
ASMJIT_INST_2x(vpopcntw, Vpopcntw, Vec, Mem) // AVX512_BITALG{kz|b32}
|
||||||
ASMJIT_INST_3x(vpor, Vpor, Vec, Vec, Vec) // AV+
|
ASMJIT_INST_3x(vpor, Vpor, Vec, Vec, Vec) // AVX+
|
||||||
ASMJIT_INST_3x(vpor, Vpor, Vec, Vec, Mem) // AVX+
|
ASMJIT_INST_3x(vpor, Vpor, Vec, Vec, Mem) // AVX+
|
||||||
ASMJIT_INST_3x(vpord, Vpord, Vec, Vec, Vec) // AVX512_F{kz|b32}
|
ASMJIT_INST_3x(vpord, Vpord, Vec, Vec, Vec) // AVX512_F{kz|b32}
|
||||||
ASMJIT_INST_3x(vpord, Vpord, Vec, Vec, Mem) // AVX512_F{kz|b32}
|
ASMJIT_INST_3x(vpord, Vpord, Vec, Vec, Mem) // AVX512_F{kz|b32}
|
||||||
|
|||||||
@@ -5,8 +5,24 @@
|
|||||||
|
|
||||||
#include <asmjit/core.h>
|
#include <asmjit/core.h>
|
||||||
|
|
||||||
#if ASMJIT_ARCH_X86 && !defined(ASMJIT_NO_X86) && !defined(ASMJIT_NO_JIT)
|
static void printInfo() noexcept {
|
||||||
|
printf("AsmJit Emitters Test-Suite v%u.%u.%u\n",
|
||||||
|
unsigned((ASMJIT_LIBRARY_VERSION >> 16) ),
|
||||||
|
unsigned((ASMJIT_LIBRARY_VERSION >> 8) & 0xFF),
|
||||||
|
unsigned((ASMJIT_LIBRARY_VERSION ) & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(ASMJIT_NO_JIT) && ( \
|
||||||
|
(ASMJIT_ARCH_X86 != 0 && !defined(ASMJIT_NO_X86 )) || \
|
||||||
|
(ASMJIT_ARCH_ARM == 64 && !defined(ASMJIT_NO_AARCH64)) )
|
||||||
|
|
||||||
|
#if ASMJIT_ARCH_X86 != 0
|
||||||
#include <asmjit/x86.h>
|
#include <asmjit/x86.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ASMJIT_ARCH_ARM == 64
|
||||||
|
#include <asmjit/a64.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -17,9 +33,13 @@ using namespace asmjit;
|
|||||||
// Signature of the generated function.
|
// Signature of the generated function.
|
||||||
typedef void (*SumIntsFunc)(int* dst, const int* a, const int* b);
|
typedef void (*SumIntsFunc)(int* dst, const int* a, const int* b);
|
||||||
|
|
||||||
|
// X86 Backend
|
||||||
|
// -----------
|
||||||
|
|
||||||
|
#if ASMJIT_ARCH_X86 != 0
|
||||||
// This function works with both x86::Assembler and x86::Builder. It shows how
|
// This function works with both x86::Assembler and x86::Builder. It shows how
|
||||||
// `x86::Emitter` can be used to make your code more generic.
|
// `x86::Emitter` can be used to make your code more generic.
|
||||||
static void makeRawFunc(x86::Emitter* emitter) noexcept {
|
static void generateFuncWithEmitter(x86::Emitter* emitter) noexcept {
|
||||||
// Decide which registers will be mapped to function arguments. Try changing
|
// Decide which registers will be mapped to function arguments. Try changing
|
||||||
// registers of `dst`, `src_a`, and `src_b` and see what happens in function's
|
// registers of `dst`, `src_a`, and `src_b` and see what happens in function's
|
||||||
// prolog and epilog.
|
// prolog and epilog.
|
||||||
@@ -39,8 +59,8 @@ static void makeRawFunc(x86::Emitter* emitter) noexcept {
|
|||||||
FuncFrame frame;
|
FuncFrame frame;
|
||||||
frame.init(func);
|
frame.init(func);
|
||||||
|
|
||||||
// Make XMM0 and XMM1 dirty. VEC group includes XMM|YMM|ZMM registers.
|
// Make or registers dirty.
|
||||||
frame.addDirtyRegs(x86::xmm0, x86::xmm1);
|
frame.addDirtyRegs(vec0, vec1);
|
||||||
|
|
||||||
FuncArgsAssignment args(&func); // Create arguments assignment context.
|
FuncArgsAssignment args(&func); // Create arguments assignment context.
|
||||||
args.assignAll(dst, src_a, src_b); // Assign our registers to arguments.
|
args.assignAll(dst, src_a, src_b); // Assign our registers to arguments.
|
||||||
@@ -63,7 +83,7 @@ static void makeRawFunc(x86::Emitter* emitter) noexcept {
|
|||||||
|
|
||||||
#ifndef ASMJIT_NO_COMPILER
|
#ifndef ASMJIT_NO_COMPILER
|
||||||
// This function works with x86::Compiler, provided for comparison.
|
// This function works with x86::Compiler, provided for comparison.
|
||||||
static void makeCompiledFunc(x86::Compiler* cc) noexcept {
|
static void generateFuncWithCompiler(x86::Compiler* cc) noexcept {
|
||||||
x86::Gp dst = cc->newIntPtr("dst");
|
x86::Gp dst = cc->newIntPtr("dst");
|
||||||
x86::Gp src_a = cc->newIntPtr("src_a");
|
x86::Gp src_a = cc->newIntPtr("src_a");
|
||||||
x86::Gp src_b = cc->newIntPtr("src_b");
|
x86::Gp src_b = cc->newIntPtr("src_b");
|
||||||
@@ -83,6 +103,154 @@ static void makeCompiledFunc(x86::Compiler* cc) noexcept {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static Error generateFunc(CodeHolder& code, EmitterType emitterType) noexcept {
|
||||||
|
switch (emitterType) {
|
||||||
|
case EmitterType::kAssembler: {
|
||||||
|
printf("Using x86::Assembler:\n");
|
||||||
|
x86::Assembler a(&code);
|
||||||
|
generateFuncWithEmitter(a.as<x86::Emitter>());
|
||||||
|
return kErrorOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef ASMJIT_NO_BUILDER
|
||||||
|
case EmitterType::kBuilder: {
|
||||||
|
printf("Using x86::Builder:\n");
|
||||||
|
x86::Builder cb(&code);
|
||||||
|
generateFuncWithEmitter(cb.as<x86::Emitter>());
|
||||||
|
|
||||||
|
return cb.finalize();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ASMJIT_NO_COMPILER
|
||||||
|
case EmitterType::kCompiler: {
|
||||||
|
printf("Using x86::Compiler:\n");
|
||||||
|
x86::Compiler cc(&code);
|
||||||
|
generateFuncWithCompiler(&cc);
|
||||||
|
|
||||||
|
return cc.finalize();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
default: {
|
||||||
|
printf("** FAILURE: No emitter to use **\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// AArch64 Backend
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
#if ASMJIT_ARCH_ARM == 64
|
||||||
|
// This function works with both a64::Assembler and a64::Builder. It shows how
|
||||||
|
// `a64::Emitter` can be used to make your code more generic.
|
||||||
|
static void generateFuncWithEmitter(a64::Emitter* emitter) noexcept {
|
||||||
|
// Decide which registers will be mapped to function arguments. Try changing
|
||||||
|
// registers of `dst`, `src_a`, and `src_b` and see what happens in function's
|
||||||
|
// prolog and epilog.
|
||||||
|
a64::Gp dst = a64::x0;
|
||||||
|
a64::Gp src_a = a64::x1;
|
||||||
|
a64::Gp src_b = a64::x2;
|
||||||
|
|
||||||
|
// Decide which vector registers to use. We use these to keep the code generic,
|
||||||
|
// you can switch to any other registers when needed.
|
||||||
|
a64::Vec vec0 = a64::v0;
|
||||||
|
a64::Vec vec1 = a64::v1;
|
||||||
|
a64::Vec vec2 = a64::v2;
|
||||||
|
|
||||||
|
// Create and initialize `FuncDetail` and `FuncFrame`.
|
||||||
|
FuncDetail func;
|
||||||
|
func.init(FuncSignature::build<void, int*, const int*, const int*>(), emitter->environment());
|
||||||
|
|
||||||
|
FuncFrame frame;
|
||||||
|
frame.init(func);
|
||||||
|
|
||||||
|
// Make XMM0 and XMM1 dirty. VEC group includes XMM|YMM|ZMM registers.
|
||||||
|
frame.addDirtyRegs(vec0, vec1, vec2);
|
||||||
|
|
||||||
|
FuncArgsAssignment args(&func); // Create arguments assignment context.
|
||||||
|
args.assignAll(dst, src_a, src_b); // Assign our registers to arguments.
|
||||||
|
args.updateFuncFrame(frame); // Reflect our args in FuncFrame.
|
||||||
|
frame.finalize();
|
||||||
|
|
||||||
|
// Emit prolog and allocate arguments to registers.
|
||||||
|
emitter->emitProlog(frame);
|
||||||
|
emitter->emitArgsAssignment(frame, args);
|
||||||
|
|
||||||
|
emitter->ld1(vec0.b16(), a64::ptr(src_a)); // Load 4 ints from [src_a] to vec0.
|
||||||
|
emitter->ld1(vec1.b16(), a64::ptr(src_b)); // Load 4 ints from [src_b] to vec1.
|
||||||
|
emitter->add(vec2.s4(), vec0.s4(), vec1.s4()); // Add 4 ints of vec0 and vec1 and store to vec2.
|
||||||
|
emitter->st1(vec2.b16(), a64::ptr(dst)); // Store the result (vec2) to [dst].
|
||||||
|
|
||||||
|
// Emit epilog and return.
|
||||||
|
emitter->emitEpilog(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef ASMJIT_NO_COMPILER
|
||||||
|
// This function works with x86::Compiler, provided for comparison.
|
||||||
|
static void generateFuncWithCompiler(a64::Compiler* cc) noexcept {
|
||||||
|
a64::Gp dst = cc->newIntPtr("dst");
|
||||||
|
a64::Gp src_a = cc->newIntPtr("src_a");
|
||||||
|
a64::Gp src_b = cc->newIntPtr("src_b");
|
||||||
|
a64::Vec vec0 = cc->newVecQ("vec0");
|
||||||
|
a64::Vec vec1 = cc->newVecQ("vec1");
|
||||||
|
a64::Vec vec2 = cc->newVecQ("vec2");
|
||||||
|
|
||||||
|
FuncNode* funcNode = cc->addFunc(FuncSignature::build<void, int*, const int*, const int*>());
|
||||||
|
funcNode->setArg(0, dst);
|
||||||
|
funcNode->setArg(1, src_a);
|
||||||
|
funcNode->setArg(2, src_b);
|
||||||
|
|
||||||
|
cc->ld1(vec0.b16(), a64::ptr(src_a)); // Load 4 ints from [src_a] to vec0.
|
||||||
|
cc->ld1(vec1.b16(), a64::ptr(src_b)); // Load 4 ints from [src_b] to vec1.
|
||||||
|
cc->add(vec2.s4(), vec0.s4(), vec1.s4()); // Add 4 ints of vec0 and vec1 and store to vec2.
|
||||||
|
cc->st1(vec2.b16(), a64::ptr(dst)); // Store the result (vec2) to [dst].
|
||||||
|
cc->endFunc();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static Error generateFunc(CodeHolder& code, EmitterType emitterType) noexcept {
|
||||||
|
switch (emitterType) {
|
||||||
|
case EmitterType::kAssembler: {
|
||||||
|
printf("Using a64::Assembler:\n");
|
||||||
|
a64::Assembler a(&code);
|
||||||
|
generateFuncWithEmitter(a.as<a64::Emitter>());
|
||||||
|
return kErrorOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef ASMJIT_NO_BUILDER
|
||||||
|
case EmitterType::kBuilder: {
|
||||||
|
printf("Using a64::Builder:\n");
|
||||||
|
a64::Builder cb(&code);
|
||||||
|
generateFuncWithEmitter(cb.as<a64::Emitter>());
|
||||||
|
|
||||||
|
return cb.finalize();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ASMJIT_NO_COMPILER
|
||||||
|
case EmitterType::kCompiler: {
|
||||||
|
printf("Using a64::Compiler:\n");
|
||||||
|
a64::Compiler cc(&code);
|
||||||
|
generateFuncWithCompiler(&cc);
|
||||||
|
|
||||||
|
return cc.finalize();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
default: {
|
||||||
|
printf("** FAILURE: No emitter to use **\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Testing
|
||||||
|
// -------
|
||||||
|
|
||||||
static uint32_t testFunc(JitRuntime& rt, EmitterType emitterType) noexcept {
|
static uint32_t testFunc(JitRuntime& rt, EmitterType emitterType) noexcept {
|
||||||
#ifndef ASMJIT_NO_LOGGING
|
#ifndef ASMJIT_NO_LOGGING
|
||||||
FileLogger logger(stdout);
|
FileLogger logger(stdout);
|
||||||
@@ -96,57 +264,18 @@ static uint32_t testFunc(JitRuntime& rt, EmitterType emitterType) noexcept {
|
|||||||
code.setLogger(&logger);
|
code.setLogger(&logger);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Error err = kErrorOk;
|
Error err = generateFunc(code, emitterType);
|
||||||
switch (emitterType) {
|
|
||||||
case EmitterType::kAssembler: {
|
|
||||||
printf("Using x86::Assembler:\n");
|
|
||||||
x86::Assembler a(&code);
|
|
||||||
makeRawFunc(a.as<x86::Emitter>());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef ASMJIT_NO_BUILDER
|
|
||||||
case EmitterType::kBuilder: {
|
|
||||||
printf("Using x86::Builder:\n");
|
|
||||||
x86::Builder cb(&code);
|
|
||||||
makeRawFunc(cb.as<x86::Emitter>());
|
|
||||||
|
|
||||||
err = cb.finalize();
|
|
||||||
if (err) {
|
if (err) {
|
||||||
printf("** FAILURE: x86::Builder::finalize() failed (%s) **\n", DebugUtils::errorAsString(err));
|
printf("** FAILURE: Failed to generate a function: %s **\n", DebugUtils::errorAsString(err));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ASMJIT_NO_COMPILER
|
|
||||||
case EmitterType::kCompiler: {
|
|
||||||
printf("Using x86::Compiler:\n");
|
|
||||||
x86::Compiler cc(&code);
|
|
||||||
makeCompiledFunc(&cc);
|
|
||||||
|
|
||||||
err = cc.finalize();
|
|
||||||
if (err) {
|
|
||||||
printf("** FAILURE: x86::Compiler::finalize() failed (%s) **\n", DebugUtils::errorAsString(err));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
default: {
|
|
||||||
printf("** FAILURE: No emitter to use **\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the code generated to the runtime.
|
// Add the code generated to the runtime.
|
||||||
SumIntsFunc fn;
|
SumIntsFunc fn;
|
||||||
err = rt.add(&fn, &code);
|
err = rt.add(&fn, &code);
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
printf("** FAILURE: JitRuntime::add() failed (%s) **\n", DebugUtils::errorAsString(err));
|
printf("** FAILURE: JitRuntime::add() failed: %s **\n", DebugUtils::errorAsString(err));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,27 +289,24 @@ static uint32_t testFunc(JitRuntime& rt, EmitterType emitterType) noexcept {
|
|||||||
printf("Result = { %d %d %d %d }\n\n", out[0], out[1], out[2], out[3]);
|
printf("Result = { %d %d %d %d }\n\n", out[0], out[1], out[2], out[3]);
|
||||||
|
|
||||||
rt.release(fn);
|
rt.release(fn);
|
||||||
return !(out[0] == 5 && out[1] == 8 && out[2] == 4 && out[3] == 9);
|
return out[0] == 5 && out[1] == 8 && out[2] == 4 && out[3] == 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
printf("AsmJit Emitters Test-Suite v%u.%u.%u\n",
|
printInfo();
|
||||||
unsigned((ASMJIT_LIBRARY_VERSION >> 16) ),
|
|
||||||
unsigned((ASMJIT_LIBRARY_VERSION >> 8) & 0xFF),
|
|
||||||
unsigned((ASMJIT_LIBRARY_VERSION ) & 0xFF));
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
JitRuntime rt;
|
JitRuntime rt;
|
||||||
unsigned nFailed = 0;
|
unsigned nFailed = 0;
|
||||||
|
|
||||||
nFailed += testFunc(rt, EmitterType::kAssembler);
|
nFailed += !testFunc(rt, EmitterType::kAssembler);
|
||||||
|
|
||||||
#ifndef ASMJIT_NO_BUILDER
|
#ifndef ASMJIT_NO_BUILDER
|
||||||
nFailed += testFunc(rt, EmitterType::kBuilder);
|
nFailed += !testFunc(rt, EmitterType::kBuilder);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef ASMJIT_NO_COMPILER
|
#ifndef ASMJIT_NO_COMPILER
|
||||||
nFailed += testFunc(rt, EmitterType::kCompiler);
|
nFailed += !testFunc(rt, EmitterType::kCompiler);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!nFailed)
|
if (!nFailed)
|
||||||
@@ -192,7 +318,8 @@ int main() {
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
int main() {
|
int main() {
|
||||||
printf("AsmJit X86 Emitter Test is disabled on non-x86 hosts or when compiled with ASMJIT_NO_JIT option\n\n");
|
printInfo();
|
||||||
|
printf("\nThis test is currently disabled - no JIT or no support for the target architecture\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif // ASMJIT_ARCH_X86 && !ASMJIT_NO_X86 && !ASMJIT_NO_JIT
|
#endif // ASMJIT_ARCH_X86 && !ASMJIT_NO_X86 && !ASMJIT_NO_JIT
|
||||||
|
|||||||
103
test/asmjit_test_execute.cpp
Normal file
103
test/asmjit_test_execute.cpp
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
// This file is part of AsmJit project <https://asmjit.com>
|
||||||
|
//
|
||||||
|
// See asmjit.h or LICENSE.md for license and copyright information
|
||||||
|
// SPDX-License-Identifier: Zlib
|
||||||
|
|
||||||
|
#include <asmjit/core.h>
|
||||||
|
|
||||||
|
static void printInfo() noexcept {
|
||||||
|
printf("AsmJit Execute Test-Suite v%u.%u.%u\n",
|
||||||
|
unsigned((ASMJIT_LIBRARY_VERSION >> 16) ),
|
||||||
|
unsigned((ASMJIT_LIBRARY_VERSION >> 8) & 0xFF),
|
||||||
|
unsigned((ASMJIT_LIBRARY_VERSION ) & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(ASMJIT_NO_JIT) && ( \
|
||||||
|
(ASMJIT_ARCH_X86 != 0 && !defined(ASMJIT_NO_X86 )) || \
|
||||||
|
(ASMJIT_ARCH_ARM == 64 && !defined(ASMJIT_NO_AARCH64)) )
|
||||||
|
|
||||||
|
#if ASMJIT_ARCH_X86 != 0
|
||||||
|
#include <asmjit/x86.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ASMJIT_ARCH_ARM == 64
|
||||||
|
#include <asmjit/a64.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
using namespace asmjit;
|
||||||
|
|
||||||
|
// Signature of the generated function.
|
||||||
|
typedef void (*EmptyFunc)(void);
|
||||||
|
|
||||||
|
// Generate Empty Function
|
||||||
|
// -----------------------
|
||||||
|
|
||||||
|
#if ASMJIT_ARCH_X86 != 0
|
||||||
|
static void generateEmptyFunc(CodeHolder& code) noexcept {
|
||||||
|
x86::Assembler a(&code);
|
||||||
|
a.ret();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ASMJIT_ARCH_ARM == 64
|
||||||
|
static void generateEmptyFunc(CodeHolder& code) noexcept {
|
||||||
|
a64::Assembler a(&code);
|
||||||
|
a.ret(a64::x30);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Testing
|
||||||
|
// -------
|
||||||
|
|
||||||
|
static void executeEmptyFunc(JitRuntime& rt) noexcept {
|
||||||
|
CodeHolder code;
|
||||||
|
code.init(rt.environment(), rt.cpuFeatures());
|
||||||
|
|
||||||
|
EmptyFunc fn;
|
||||||
|
|
||||||
|
generateEmptyFunc(code);
|
||||||
|
Error err = rt.add(&fn, &code);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
printf("** FAILURE: JitRuntime::add() failed: %s **\n", DebugUtils::errorAsString(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn();
|
||||||
|
|
||||||
|
rt.release(&fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
printInfo();
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
{
|
||||||
|
printf("Trying to execute empty function with JitRuntime (default settings)\n");
|
||||||
|
JitRuntime rt;
|
||||||
|
executeEmptyFunc(rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VirtMem::hardenedRuntimeInfo().hasFlag(VirtMem::HardenedRuntimeFlags::kDualMapping)) {
|
||||||
|
printf("Trying to execute empty function with JitRuntime (dual-mapped)\n");
|
||||||
|
JitAllocator::CreateParams params {};
|
||||||
|
params.options |= JitAllocatorOptions::kUseDualMapping;
|
||||||
|
JitRuntime rt;
|
||||||
|
executeEmptyFunc(rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are here we were successful, otherwise the process would crash.
|
||||||
|
printf("** SUCCESS **\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
int main() {
|
||||||
|
printInfo();
|
||||||
|
printf("\nThis test is currently disabled - no JIT or no support for the target architecture\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif // ASMJIT_ARCH_X86 && !ASMJIT_NO_X86 && !ASMJIT_NO_JIT
|
||||||
Reference in New Issue
Block a user