mirror of
https://github.com/asmjit/asmjit.git
synced 2025-12-16 20:17:05 +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:
43
.github/workflows/build-config.json
vendored
43
.github/workflows/build-config.json
vendored
@@ -12,37 +12,14 @@
|
||||
],
|
||||
|
||||
"tests": [
|
||||
{
|
||||
"cmd": ["asmjit_test_unit", "--quick"],
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"cmd": ["asmjit_test_assembler"],
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"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
|
||||
}
|
||||
{ "optional": true, "cmd": ["asmjit_test_unit", "--quick"] },
|
||||
{ "optional": true, "cmd": ["asmjit_test_assembler"] },
|
||||
{ "optional": true, "cmd": ["asmjit_test_assembler", "--validate"] },
|
||||
{ "optional": true, "cmd": ["asmjit_test_emitters"] },
|
||||
{ "optional": true, "cmd": ["asmjit_test_execute"] },
|
||||
{ "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"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
20
.github/workflows/build.yml
vendored
20
.github/workflows/build.yml
vendored
@@ -14,12 +14,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: "Setup node.js"
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "16"
|
||||
node-version: "*"
|
||||
|
||||
- name: "Check Enumerations"
|
||||
run: |
|
||||
@@ -131,28 +131,28 @@ jobs:
|
||||
- { title: "openbsd" , host: "macos-12" , arch: "x86-64" , cc: "clang" , conf: "Release", vm: "openbsd", vm_ver: "7.4" , defs: "ASMJIT_TEST=1" }
|
||||
- { title: "openbsd" , host: "ubuntu-latest" , arch: "arm64" , cc: "clang" , conf: "Release", vm: "openbsd", vm_ver: "7.4" , defs: "ASMJIT_TEST=1" }
|
||||
|
||||
- { title: "debian" , host: "ubuntu-latest" , arch: "arm/v7" , cc: "clang" , conf: "Release", vm: "debian:unstable", defs: "ASMJIT_TEST=1" }
|
||||
- { title: "debian" , host: "ubuntu-latest" , arch: "arm64" , cc: "clang" , conf: "Release", vm: "debian:unstable", defs: "ASMJIT_TEST=1" }
|
||||
- { title: "debian" , host: "ubuntu-latest" , arch: "riscv64", cc: "clang" , conf: "Release", vm: "debian:unstable", defs: "ASMJIT_TEST=1" }
|
||||
- { title: "debian" , host: "ubuntu-latest" , arch: "ppc64le", cc: "clang" , conf: "Release", vm: "debian:unstable", defs: "ASMJIT_TEST=1" }
|
||||
- { title: "debian" , host: "ubuntu-latest" , arch: "arm/v7" , cc: "clang" , conf: "Release", vm: "debian:unstable", defs: "ASMJIT_TEST=1" }
|
||||
- { title: "debian" , host: "ubuntu-latest" , arch: "arm64" , cc: "clang" , conf: "Release", vm: "debian:unstable", defs: "ASMJIT_TEST=1" }
|
||||
- { title: "debian" , host: "ubuntu-latest" , arch: "riscv64", cc: "clang" , conf: "Release", vm: "debian:unstable", defs: "ASMJIT_TEST=1" }
|
||||
- { title: "debian" , host: "ubuntu-latest" , arch: "ppc64le", cc: "clang" , conf: "Release", vm: "debian:unstable", defs: "ASMJIT_TEST=1" }
|
||||
|
||||
name: "${{matrix.title}}/${{matrix.arch}}, ${{matrix.cc}} ${{matrix.conf}}"
|
||||
runs-on: "${{matrix.host}}"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: "source"
|
||||
|
||||
- name: "Checkout Build Actions"
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: build-actions/build-actions
|
||||
path: "build-actions"
|
||||
|
||||
- name: "Python"
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.x"
|
||||
|
||||
|
||||
@@ -666,6 +666,7 @@ if (NOT ASMJIT_EMBED)
|
||||
CFLAGS_REL ${ASMJIT_PRIVATE_CFLAGS_REL})
|
||||
|
||||
foreach(_target asmjit_test_emitters
|
||||
asmjit_test_execute
|
||||
asmjit_test_x86_sections)
|
||||
asmjit_add_target(${_target} TEST
|
||||
SOURCES test/${_target}.cpp
|
||||
|
||||
@@ -87,6 +87,7 @@ ASMJIT_FAVOR_SIZE const char* DebugUtils::errorAsString(Error err) noexcept {
|
||||
"ExpressionOverflow\0"
|
||||
"FailedToOpenAnonymousMemory\0"
|
||||
"FailedToOpenFile\0"
|
||||
"ProtectionFailure\0"
|
||||
"<Unknown>\0";
|
||||
|
||||
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,
|
||||
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,
|
||||
1073, 1088, 1104, 1119, 1134, 1164, 1188, 1207, 1235, 1252
|
||||
1073, 1088, 1104, 1119, 1134, 1164, 1188, 1207, 1235, 1252, 1270
|
||||
};
|
||||
// @EnumStringEnd@
|
||||
|
||||
|
||||
@@ -334,6 +334,10 @@ enum ErrorCode : uint32_t {
|
||||
//! \note This is a generic error that is used by internal filesystem API.
|
||||
kErrorFailedToOpenFile,
|
||||
|
||||
//! Protection failure can be returned from a virtual memory allocator or when trying to change memory access
|
||||
//! permissions.
|
||||
kErrorProtectionFailure,
|
||||
|
||||
// @EnumValuesEnd@
|
||||
|
||||
//! Count of AsmJit error codes.
|
||||
|
||||
@@ -76,8 +76,6 @@
|
||||
#define MAP_ANONYMOUS MAP_ANON
|
||||
#endif
|
||||
|
||||
#define ASMJIT_ANONYMOUS_MEMORY_USE_FD
|
||||
|
||||
// Android NDK doesn't provide `shm_open()` and `shm_unlink()`.
|
||||
#if !defined(__BIONIC__) && !defined(ASMJIT_NO_SHM_OPEN)
|
||||
#define ASMJIT_HAS_SHM_OPEN
|
||||
@@ -89,18 +87,60 @@
|
||||
#define ASMJIT_VM_SHM_DETECT 1
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) && TARGET_OS_OSX && ASMJIT_ARCH_ARM >= 64
|
||||
#define ASMJIT_HAS_PTHREAD_JIT_WRITE_PROTECT_NP
|
||||
#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
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) && ASMJIT_ARCH_X86 == 0
|
||||
#define ASMJIT_NO_DUAL_MAPPING
|
||||
#endif
|
||||
|
||||
#if defined(__NetBSD__) && defined(MAP_REMAPDUP) && defined(PROT_MPROTECT)
|
||||
#undef ASMJIT_ANONYMOUS_MEMORY_USE_FD
|
||||
#define ASMJIT_ANONYMOUS_MEMORY_USE_REMAPDUP
|
||||
#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
|
||||
|
||||
#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)
|
||||
|
||||
// Virtual Memory Utilities
|
||||
@@ -757,8 +797,7 @@ static inline int mmMapJitFromMemoryFlags(MemoryFlags memoryFlags) noexcept {
|
||||
}
|
||||
|
||||
static inline bool hasDualMappingSupport() noexcept {
|
||||
#if defined(__APPLE__) && TARGET_OS_OSX && ASMJIT_ARCH_X86 == 0
|
||||
// Apple platforms don't allow dual-mapping on non-x86 hardware.
|
||||
#if defined(ASMJIT_NO_DUAL_MAPPING)
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
@@ -850,6 +889,7 @@ Error protect(void* p, size_t size, MemoryFlags memoryFlags) noexcept {
|
||||
// Virtual Memory [Posix] - Dual Mapping
|
||||
// =====================================
|
||||
|
||||
#if !defined(ASMJIT_NO_DUAL_MAPPING)
|
||||
static Error unmapDualMapping(DualMapping* dm, size_t size) noexcept {
|
||||
Error err1 = unmapMemory(dm->rx, size);
|
||||
Error err2 = kErrorOk;
|
||||
@@ -865,6 +905,7 @@ static Error unmapDualMapping(DualMapping* dm, size_t size) noexcept {
|
||||
dm->rw = nullptr;
|
||||
return kErrorOk;
|
||||
}
|
||||
#endif // !ASMJIT_NO_DUAL_MAPPING
|
||||
|
||||
#if defined(ASMJIT_ANONYMOUS_MEMORY_USE_REMAPDUP)
|
||||
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
|
||||
|
||||
Error allocDualMapping(DualMapping* dm, size_t size, MemoryFlags memoryFlags) noexcept {
|
||||
dm->rx = nullptr;
|
||||
dm->rw = nullptr;
|
||||
#if defined(ASMJIT_ANONYMOUS_MEMORY_USE_MACH_VM_REMAP)
|
||||
static Error asmjitErrorFromKernResult(kern_return_t result) noexcept {
|
||||
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)
|
||||
return DebugUtils::errored(size == 0 ? kErrorInvalidArgument : kErrorTooLarge);
|
||||
static Error allocDualMappingUsingMachVmRemap(DualMapping* dmOut, size_t size, MemoryFlags memoryFlags) noexcept {
|
||||
DualMapping dm {};
|
||||
|
||||
#if defined(ASMJIT_ANONYMOUS_MEMORY_USE_REMAPDUP)
|
||||
return allocDualMappingUsingRemapdup(dm, size, memoryFlags);
|
||||
#elif defined(ASMJIT_ANONYMOUS_MEMORY_USE_FD)
|
||||
MemoryFlags mmapFlags = MemoryFlags::kAccessReadWrite | (memoryFlags & MemoryFlags::kMapShared);
|
||||
ASMJIT_PROPAGATE(mapMemory(&dm.rx, size, mmapFlags));
|
||||
|
||||
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);
|
||||
if (!preferTmpOverDevShm) {
|
||||
AnonymousMemoryStrategy strategy;
|
||||
@@ -932,13 +1062,39 @@ Error allocDualMapping(DualMapping* dm, size_t size, MemoryFlags memoryFlags) no
|
||||
dm->rx = ptr[0];
|
||||
dm->rw = ptr[1];
|
||||
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
|
||||
#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 // ASMJIT_NO_DUAL_MAPPING
|
||||
}
|
||||
|
||||
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);
|
||||
#endif // ASMJIT_NO_DUAL_MAPPING
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1039,8 +1195,9 @@ UNIT(virt_mem) {
|
||||
|
||||
INFO("VirtMem::hardenedRuntimeInfo():");
|
||||
INFO(" flags:");
|
||||
INFO(" kEnabled: %s", Support::test(hardenedFlags, VirtMem::HardenedRuntimeFlags::kEnabled) ? "true" : "false");
|
||||
INFO(" kMapJit: %s", Support::test(hardenedFlags, VirtMem::HardenedRuntimeFlags::kMapJit) ? "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(" kDualMapping: %s", Support::test(hardenedFlags, VirtMem::HardenedRuntimeFlags::kDualMapping) ? "true" : "false");
|
||||
}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#ifndef ASMJIT_NO_JIT
|
||||
|
||||
#include "../core/globals.h"
|
||||
#include "../core/support.h"
|
||||
|
||||
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).
|
||||
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
|
||||
};
|
||||
ASMJIT_DEFINE_ENUM_FLAGS(HardenedRuntimeFlags)
|
||||
|
||||
//! Hardened runtime information.
|
||||
struct HardenedRuntimeInfo {
|
||||
//! \name Members
|
||||
//! \{
|
||||
|
||||
//! Hardened runtime 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.
|
||||
|
||||
@@ -2827,7 +2827,7 @@ public:
|
||||
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, 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(vpandnd, Vpandnd, Vec, Vec, Vec) // 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(vpopcntw, Vpopcntw, Vec, Vec) // 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(vpord, Vpord, Vec, Vec, Vec) // AVX512_F{kz|b32}
|
||||
ASMJIT_INST_3x(vpord, Vpord, Vec, Vec, Mem) // AVX512_F{kz|b32}
|
||||
|
||||
@@ -5,8 +5,24 @@
|
||||
|
||||
#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>
|
||||
#endif
|
||||
|
||||
#if ASMJIT_ARCH_ARM == 64
|
||||
#include <asmjit/a64.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -17,9 +33,13 @@ using namespace asmjit;
|
||||
// Signature of the generated function.
|
||||
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
|
||||
// `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
|
||||
// registers of `dst`, `src_a`, and `src_b` and see what happens in function's
|
||||
// prolog and epilog.
|
||||
@@ -39,8 +59,8 @@ static void makeRawFunc(x86::Emitter* emitter) noexcept {
|
||||
FuncFrame frame;
|
||||
frame.init(func);
|
||||
|
||||
// Make XMM0 and XMM1 dirty. VEC group includes XMM|YMM|ZMM registers.
|
||||
frame.addDirtyRegs(x86::xmm0, x86::xmm1);
|
||||
// Make or registers dirty.
|
||||
frame.addDirtyRegs(vec0, vec1);
|
||||
|
||||
FuncArgsAssignment args(&func); // Create arguments assignment context.
|
||||
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
|
||||
// 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 src_a = cc->newIntPtr("src_a");
|
||||
x86::Gp src_b = cc->newIntPtr("src_b");
|
||||
@@ -83,6 +103,154 @@ static void makeCompiledFunc(x86::Compiler* cc) noexcept {
|
||||
}
|
||||
#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 {
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
FileLogger logger(stdout);
|
||||
@@ -96,49 +264,10 @@ static uint32_t testFunc(JitRuntime& rt, EmitterType emitterType) noexcept {
|
||||
code.setLogger(&logger);
|
||||
#endif
|
||||
|
||||
Error err = kErrorOk;
|
||||
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) {
|
||||
printf("** FAILURE: x86::Builder::finalize() failed (%s) **\n", DebugUtils::errorAsString(err));
|
||||
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;
|
||||
}
|
||||
Error err = generateFunc(code, emitterType);
|
||||
if (err) {
|
||||
printf("** FAILURE: Failed to generate a function: %s **\n", DebugUtils::errorAsString(err));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Add the code generated to the runtime.
|
||||
@@ -146,7 +275,7 @@ static uint32_t testFunc(JitRuntime& rt, EmitterType emitterType) noexcept {
|
||||
err = rt.add(&fn, &code);
|
||||
|
||||
if (err) {
|
||||
printf("** FAILURE: JitRuntime::add() failed (%s) **\n", DebugUtils::errorAsString(err));
|
||||
printf("** FAILURE: JitRuntime::add() failed: %s **\n", DebugUtils::errorAsString(err));
|
||||
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]);
|
||||
|
||||
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() {
|
||||
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));
|
||||
printInfo();
|
||||
printf("\n");
|
||||
|
||||
JitRuntime rt;
|
||||
unsigned nFailed = 0;
|
||||
|
||||
nFailed += testFunc(rt, EmitterType::kAssembler);
|
||||
nFailed += !testFunc(rt, EmitterType::kAssembler);
|
||||
|
||||
#ifndef ASMJIT_NO_BUILDER
|
||||
nFailed += testFunc(rt, EmitterType::kBuilder);
|
||||
nFailed += !testFunc(rt, EmitterType::kBuilder);
|
||||
#endif
|
||||
|
||||
#ifndef ASMJIT_NO_COMPILER
|
||||
nFailed += testFunc(rt, EmitterType::kCompiler);
|
||||
nFailed += !testFunc(rt, EmitterType::kCompiler);
|
||||
#endif
|
||||
|
||||
if (!nFailed)
|
||||
@@ -192,7 +318,8 @@ int main() {
|
||||
}
|
||||
#else
|
||||
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;
|
||||
}
|
||||
#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