mirror of
https://github.com/lifting-bits/remill
synced 2026-06-21 13:56:07 +00:00
Aarch64 tester (#128)
* Added in more aarch64 instructions. Fixed some x86 instructions. The x86 test cases now exercise each test through every possible combination of flags. * Adding missing files * Another missing file * Rename file * Fixup some macros * IPR * More improvements on the test runner * Test runner fixes related to me not being familiar with aarch64 assembly * Fixing default data layout * Trying to use llc to compile bitcode to aarch64 assembly. wth. * Revert back to using the CMAKE_BC_COMPILER for building the test assembly file instead of the whole CMAKE_LL_COMPILER stuff, now that I've adjusted cxx-common to use the right build target for aarch64. * Documentation updates. Fixes for aarch64. * Making progress. The native tests can run, but the first lifted test faults. Not yet sure why. * Weirdest issue is happening on aarch64. A pointer argument is being compiled to an integer, and that's really screwing things up. * Add caching of the libraries path to the main cmakelists to avoid having to re-run build.sh all the time when the TRAILOBITS_LIBRARIES env var is not globally defined. Experimenting with trying to force the semantics to be compiled using the x86_64 target, regardless of host arch, or modelled arch of the semantics. This is to try to get around the issue where a single-element struct containing a pointer is lowered into a uintptr_t when passed by value as an argument on aarch64. * Alright, falling back on handling this problem in the lifter (for now, at least). Really not ideal. * Test runner works afaict
This commit is contained in:
+28
-11
@@ -27,9 +27,12 @@ set(REMILL_SOURCE_DIR "${PROJECT_SOURCE_DIR}")
|
||||
#
|
||||
|
||||
if (DEFINED ENV{TRAILOFBITS_LIBRARIES})
|
||||
set(LIBRARY_REPOSITORY_ROOT $ENV{TRAILOFBITS_LIBRARIES})
|
||||
include("${LIBRARY_REPOSITORY_ROOT}/cmake/repository.cmake")
|
||||
set(LIBRARY_REPOSITORY_ROOT $ENV{TRAILOFBITS_LIBRARIES}
|
||||
CACHE PATH "Location of cxx-common libraries.")
|
||||
endif ()
|
||||
|
||||
if (DEFINED LIBRARY_REPOSITORY_ROOT)
|
||||
include("${LIBRARY_REPOSITORY_ROOT}/cmake/repository.cmake")
|
||||
message(STATUS "Using the following library repository: ${LIBRARY_REPOSITORY_ROOT}")
|
||||
else ()
|
||||
message(STATUS "Using system libraries")
|
||||
@@ -155,9 +158,9 @@ set(REMILL_LLVM_VERSION "${LLVM_MAJOR_VERSION}.${LLVM_MINOR_VERSION}")
|
||||
# target settings
|
||||
#
|
||||
|
||||
list(APPEND PROJECT_DEFINITIONS "INSTALL_SEMANTICS_DIR=\"${CMAKE_INSTALL_PREFIX}/share/remill/${REMILL_LLVM_VERSION}/semantics/\"")
|
||||
list(APPEND PROJECT_DEFINITIONS "BUILD_SEMANTICS_DIR_X86=\"${CMAKE_CURRENT_BINARY_DIR}/remill/Arch/X86/Runtime/\"")
|
||||
list(APPEND PROJECT_DEFINITIONS "BUILD_SEMANTICS_DIR_AARCH64=\"${CMAKE_CURRENT_BINARY_DIR}/remill/Arch/AArch64/Runtime/\"")
|
||||
list(APPEND PROJECT_DEFINITIONS "REMILL_INSTALL_SEMANTICS_DIR=\"${CMAKE_INSTALL_PREFIX}/share/remill/${REMILL_LLVM_VERSION}/semantics/\"")
|
||||
list(APPEND PROJECT_DEFINITIONS "REMILL_BUILD_SEMANTICS_DIR_X86=\"${CMAKE_CURRENT_BINARY_DIR}/remill/Arch/X86/Runtime/\"")
|
||||
list(APPEND PROJECT_DEFINITIONS "REMILL_BUILD_SEMANTICS_DIR_AARCH64=\"${CMAKE_CURRENT_BINARY_DIR}/remill/Arch/AArch64/Runtime/\"")
|
||||
|
||||
add_library(${PROJECT_NAME} STATIC
|
||||
|
||||
@@ -201,10 +204,24 @@ add_subdirectory(remill/Arch/AArch64/Runtime)
|
||||
# tools
|
||||
add_subdirectory(tools)
|
||||
|
||||
# only enable tests when compiling under x64
|
||||
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
if(UNIX AND NOT APPLE)
|
||||
set(LINUX TRUE)
|
||||
add_subdirectory(tests/X86)
|
||||
endif ()
|
||||
if(UNIX AND NOT APPLE)
|
||||
set(LINUX TRUE)
|
||||
else()
|
||||
set(LINUX FALSE)
|
||||
endif ()
|
||||
|
||||
if (LINUX)
|
||||
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
|
||||
# only enable x86 tests when compiling under x64
|
||||
if ("${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "x86_64")
|
||||
add_subdirectory(tests/X86)
|
||||
endif ()
|
||||
|
||||
# only enable aarch64 tests when compiling under aarch64.
|
||||
if ("${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "aarch64")
|
||||
add_subdirectory(tests/AArch64)
|
||||
endif ()
|
||||
|
||||
endif ()
|
||||
endif (LINUX)
|
||||
|
||||
@@ -12,7 +12,16 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set(DEFAULT_BC_COMPILER_FLAGS "-std=gnu++11 -emit-llvm -Wno-unknown-warning-option -Wall -Wshadow -Wconversion -Wpadded -pedantic -Wshorten-64-to-32 -Wno-gnu-anonymous-struct -Wno-return-type-c-linkage -Wno-gnu-zero-variadic-macro-arguments -Wno-nested-anon-types -Wno-extended-offsetof -Wno-gnu-statement-expression -Wno-c99-extensions -Wno-ignored-attributes -mtune=generic -fno-vectorize -fno-slp-vectorize -ffreestanding -fno-common -fno-builtin -fno-exceptions -fno-rtti -fno-asynchronous-unwind-tables -Wno-unneeded-internal-declaration -Wno-unused-function")
|
||||
set(DEFAULT_BC_COMPILER_FLAGS "-std=gnu++11 -emit-llvm -Wno-unknown-warning-option -Wall -Wshadow -Wconversion -Wpadded -pedantic -Wshorten-64-to-32 -Wno-gnu-anonymous-struct -Wno-return-type-c-linkage -Wno-gnu-zero-variadic-macro-arguments -Wno-nested-anon-types -Wno-extended-offsetof -Wno-gnu-statement-expression -Wno-c99-extensions -Wno-ignored-attributes -mtune=generic -fno-vectorize -fno-slp-vectorize -ffreestanding -fno-common -fno-builtin -fno-exceptions -fno-rtti -fno-asynchronous-unwind-tables -Wno-unneeded-internal-declaration -Wno-unused-function ")
|
||||
|
||||
# Disable process-specific vectorizations. In practice these don't really matter
|
||||
# as they more related to codegen.
|
||||
if ("${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "x86_64")
|
||||
set(DEFAULT_BC_COMPILER_FLAGS "${DEFAULT_BC_COMPILER_FLAGS} -mno-sse -mno-avx -mno-3dnow")
|
||||
|
||||
elseif ("${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "aarch64")
|
||||
set(DEFAULT_BC_COMPILER_FLAGS "${DEFAULT_BC_COMPILER_FLAGS} -mno-hvx -mno-hvx-double")
|
||||
endif ()
|
||||
|
||||
if (NOT CMAKE_BC_COMPILE_OBJECT)
|
||||
if (NOT DEFINED CMAKE_BC_COMPILER)
|
||||
@@ -42,9 +51,6 @@ function (add_runtime target_name)
|
||||
if ("${macro_parameter}" STREQUAL "SOURCES")
|
||||
set(state "${macro_parameter}")
|
||||
continue ()
|
||||
elseif ("${macro_parameter}" STREQUAL "ARCH")
|
||||
set(state "${macro_parameter}")
|
||||
continue ()
|
||||
elseif ("${macro_parameter}" STREQUAL "ADDRESS_SIZE")
|
||||
set(state "${macro_parameter}")
|
||||
continue ()
|
||||
@@ -54,10 +60,6 @@ function (add_runtime target_name)
|
||||
set_source_files_properties("${macro_parameter}" PROPERTIES LANGUAGE BC)
|
||||
list(APPEND source_file_list "${macro_parameter}")
|
||||
|
||||
elseif ("${state}" STREQUAL "ARCH")
|
||||
list(APPEND options "-target ${macro_parameter}")
|
||||
set(arch_found True)
|
||||
|
||||
elseif ("${state}" STREQUAL "ADDRESS_SIZE")
|
||||
if (NOT "${macro_parameter}" MATCHES "^[0-9]+$")
|
||||
message(SEND_ERROR "Invalid ADDRESS_SIZE parameter passed to add_runtime")
|
||||
@@ -76,10 +78,6 @@ function (add_runtime target_name)
|
||||
set_source_files_properties("${source_file}" PROPERTIES LANGUAGE BC)
|
||||
endforeach ()
|
||||
|
||||
if (NOT arch_found)
|
||||
message(SEND_ERROR "Missing target architecture.")
|
||||
endif ()
|
||||
|
||||
if (NOT address_size_bits_found)
|
||||
message(SEND_ERROR "Missing address size.")
|
||||
endif ()
|
||||
|
||||
@@ -42,12 +42,12 @@ The following is an example of the `__remill_basic_block` function for X86.
|
||||
|
||||
```C++
|
||||
// Instructions will be lifted into clones of this function.
|
||||
Memory *__remill_basic_block(Memory *memory, State &state, addr_t curr_pc) {
|
||||
Memory *__remill_basic_block(addr_t curr_pc, State &state, Memory *memory) {
|
||||
|
||||
...
|
||||
|
||||
auto &EAX = state.gpr.rax.dword;
|
||||
auto &EBX = state.gpr.rbx.dword;\
|
||||
auto &EBX = state.gpr.rbx.dword;
|
||||
auto &ESP = state.gpr.rsp.dword;
|
||||
|
||||
...
|
||||
@@ -82,7 +82,7 @@ The decoder initialized the `name` field with `"EBX"`, and the lifter can look u
|
||||
In spirit, the lifted code for the instructions in our running example looks like the following C++ code.
|
||||
|
||||
```C++
|
||||
void __remill_sub_804b7a3(Memory *memory, State *state, addr_t pc) {
|
||||
void __remill_sub_804b7a3(addr_t pc, State *state, Memory *memory) {
|
||||
auto &EIP = state.gpr.rip.dword;
|
||||
auto &EAX = state.gpr.rax.dword;
|
||||
auto &EBX = state.gpr.rbx.dword;
|
||||
@@ -92,21 +92,21 @@ void __remill_sub_804b7a3(Memory *memory, State *state, addr_t pc) {
|
||||
|
||||
// mov eax, 0x1
|
||||
EIP += 5;
|
||||
MOV<R32W, I32>(state, &memory, &EAX, 1);
|
||||
memory = MOV<R32W, I32>(memory, state, &EAX, 1);
|
||||
|
||||
// push ebx
|
||||
EIP += 1;
|
||||
PUSH<R32>(state, &memory, EBX);
|
||||
memory = PUSH<R32>(memory, state, EBX);
|
||||
|
||||
// mov ebx, dword [esp+0x8]
|
||||
EIP += 4;
|
||||
MOV<R32W, M32>(state, &memory, &EBX, ESP + 0x8);
|
||||
memory = MOV<R32W, M32>(memory, state, &EBX, ESP + 0x8);
|
||||
|
||||
// int 0x80
|
||||
EIP += 2;
|
||||
INT_IMMb<I8>(state, &memory, 0x80);
|
||||
memory = INT_IMMb<I8>(memory, state, 0x80);
|
||||
|
||||
return __remill_interrupt_call(memory, state, EIP)
|
||||
return __remill_interrupt_call(EIP, state, memory)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -146,24 +146,21 @@ The spiritual lifted code makes one function call per lifted instruction, where
|
||||
|
||||
|
||||
```C++
|
||||
void __remill_sub_804b7a3(Memory *memory, State *state, addr_t pc) {
|
||||
void __remill_sub_804b7a3(addr_t pc, State *state, Memory *memory) {
|
||||
auto &EIP = state.gpr.rip.dword;
|
||||
auto &EAX = state.gpr.rax.dword;
|
||||
auto &EBX = state.gpr.rbx.dword;
|
||||
auto &ESP = state.gpr.rsp.dword;
|
||||
auto &SS_BASE = 0;
|
||||
|
||||
// mov eax, 0x1
|
||||
EAX = 1;
|
||||
|
||||
// push ebx
|
||||
ESP -= 4;
|
||||
addr_t push_addr = ESP + SS_BASE;
|
||||
memory = __remill_write_memory_32(memory, push_addr, EBX);
|
||||
memory = __remill_write_memory_32(memory, ESP, EBX);
|
||||
|
||||
// mov ebx, dword [esp+0x8]
|
||||
addr_t read_addr = __remill_compute_address(ESP + 0x8, SS);
|
||||
EBX = __remill_read_memory_32(memory, read_addr);
|
||||
EBX = __remill_read_memory_32(memory, ESP + 0x8);
|
||||
|
||||
// int 0x80
|
||||
state.hyper_call = AsyncHyperCall::kX86IntN;
|
||||
@@ -171,7 +168,7 @@ void __remill_sub_804b7a3(Memory *memory, State *state, addr_t pc) {
|
||||
|
||||
EIP = pc + 12;
|
||||
|
||||
return __remill_async_hyper_call(state, memory, EIP)
|
||||
return __remill_async_hyper_call(EIP, state, memory)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -191,9 +188,9 @@ The `__remill_async_hyper_call` instruction instructs the "runtime" that an expl
|
||||
|
||||
All Remill control-flow intrinsics and Remill lifted basic block functions share the same argument structure:
|
||||
|
||||
1. A pointer to the opaque `Memory` structure.
|
||||
1. The program counter on entry to the lifted basic block.
|
||||
2. A pointer to the `State` structure.
|
||||
3. The program counter on entry to the lifted basic block.
|
||||
3. A pointer to the opaque `Memory` structure.
|
||||
|
||||
In the case of the `__remill_async_hyper_call`, the third argument, the program counter address, is computed to be the address following the `int 0x80` instruction.
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/* Auto-generated file! Don't modify! */
|
||||
|
||||
mov x2, #0
|
||||
ldrb w1, [x28, #1129]
|
||||
orr x2, x2, x1, LSL 31
|
||||
ldrb w1, [x28, #1131]
|
||||
orr x2, x2, x1, LSL 30
|
||||
ldrb w1, [x28, #1133]
|
||||
orr x2, x2, x1, LSL 29
|
||||
ldrb w1, [x28, #1135]
|
||||
orr x2, x2, x1, LSL 28
|
||||
str x2, [x28, #1072]
|
||||
msr nzcv, x2
|
||||
ldr x1, [x28, #1080]
|
||||
msr fpcr, x1
|
||||
ldr x1, [x28, #1088]
|
||||
msr fpsr, x1
|
||||
ldr x1, [x28, #1104]
|
||||
msr tpidr_el0, x1
|
||||
ldr x0, [x28, #544]
|
||||
ldr x1, [x28, #560]
|
||||
ldr x2, [x28, #576]
|
||||
ldr x3, [x28, #592]
|
||||
ldr x4, [x28, #608]
|
||||
ldr x5, [x28, #624]
|
||||
ldr x6, [x28, #640]
|
||||
ldr x7, [x28, #656]
|
||||
ldr x8, [x28, #672]
|
||||
ldr x9, [x28, #688]
|
||||
ldr x10, [x28, #704]
|
||||
ldr x11, [x28, #720]
|
||||
ldr x12, [x28, #736]
|
||||
ldr x13, [x28, #752]
|
||||
ldr x14, [x28, #768]
|
||||
ldr x15, [x28, #784]
|
||||
ldr x16, [x28, #800]
|
||||
ldr x17, [x28, #816]
|
||||
ldr x18, [x28, #832]
|
||||
ldr x19, [x28, #848]
|
||||
ldr x20, [x28, #864]
|
||||
ldr x21, [x28, #880]
|
||||
ldr x22, [x28, #896]
|
||||
ldr x23, [x28, #912]
|
||||
ldr x24, [x28, #928]
|
||||
ldr x25, [x28, #944]
|
||||
ldr x26, [x28, #960]
|
||||
ldr x27, [x28, #976]
|
||||
ldr x30, [x28, #1024]
|
||||
ldr x29, [x28, #1040]
|
||||
mov sp, x29
|
||||
ldr x29, [x28, #1008]
|
||||
@@ -0,0 +1,63 @@
|
||||
/* Auto-generated file! Don't modify! */
|
||||
|
||||
str x0, [x28, #544]
|
||||
str x1, [x28, #560]
|
||||
str x2, [x28, #576]
|
||||
str x3, [x28, #592]
|
||||
str x4, [x28, #608]
|
||||
str x5, [x28, #624]
|
||||
str x6, [x28, #640]
|
||||
str x7, [x28, #656]
|
||||
str x8, [x28, #672]
|
||||
str x9, [x28, #688]
|
||||
str x10, [x28, #704]
|
||||
str x11, [x28, #720]
|
||||
str x12, [x28, #736]
|
||||
str x13, [x28, #752]
|
||||
str x14, [x28, #768]
|
||||
str x15, [x28, #784]
|
||||
str x16, [x28, #800]
|
||||
str x17, [x28, #816]
|
||||
str x18, [x28, #832]
|
||||
str x19, [x28, #848]
|
||||
str x20, [x28, #864]
|
||||
str x21, [x28, #880]
|
||||
str x22, [x28, #896]
|
||||
str x23, [x28, #912]
|
||||
str x24, [x28, #928]
|
||||
str x25, [x28, #944]
|
||||
str x26, [x28, #960]
|
||||
str x27, [x28, #976]
|
||||
str x29, [x28, #1008]
|
||||
str x30, [x28, #1024]
|
||||
mov x29, sp
|
||||
str x29, [x28, #1040]
|
||||
mov x29, #1
|
||||
strb w29, [x28, #1129]
|
||||
b.mi 1f
|
||||
strb wzr, [x28, #1129]
|
||||
1:
|
||||
strb w29, [x28, #1131]
|
||||
b.eq 1f
|
||||
strb wzr, [x28, #1131]
|
||||
1:
|
||||
strb w29, [x28, #1133]
|
||||
b.cs 1f
|
||||
strb wzr, [x28, #1133]
|
||||
1:
|
||||
strb w29, [x28, #1135]
|
||||
b.vs 1f
|
||||
strb wzr, [x28, #1135]
|
||||
1:
|
||||
ldr x29, [x28, #1008]
|
||||
mrs x1, nzcv
|
||||
str x1, [x28, #1072]
|
||||
mrs x1, fpcr
|
||||
str x1, [x28, #1080]
|
||||
mrs x1, fpsr
|
||||
str x1, [x28, #1088]
|
||||
mrs x1, tpidr_el0
|
||||
str x1, [x28, #1104]
|
||||
mrs x1, tpidrro_el0
|
||||
str x1, [x28, #1120]
|
||||
ldr x1, [x28, #560]
|
||||
+104
-60
@@ -130,11 +130,14 @@ void AArch64Arch::PrepareModule(llvm::Module *mod) const {
|
||||
switch (os_name) {
|
||||
case kOSLinux:
|
||||
triple.setOS(llvm::Triple::Linux);
|
||||
triple.setEnvironment(llvm::Triple::GNU);
|
||||
triple.setVendor(llvm::Triple::PC);
|
||||
triple.setObjectFormat(llvm::Triple::ELF);
|
||||
|
||||
switch (arch_name) {
|
||||
case kArchAArch64LittleEndian:
|
||||
triple.setArch(llvm::Triple::aarch64);
|
||||
dl = "e-m:e-i64:64-i128:128-n32:64-S128";
|
||||
dl = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128";
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -283,15 +286,20 @@ static void AddShiftRegOperand(Instruction &inst, RegClass rclass,
|
||||
RegUsage rtype, RegNum reg_num,
|
||||
Operand::ShiftRegister::Shift shift_type,
|
||||
uint64_t shift_size) {
|
||||
Operand op;
|
||||
op.type = Operand::kTypeShiftRegister;
|
||||
if (!shift_size) {
|
||||
AddRegOperand(inst, kActionRead, rclass, rtype, reg_num);
|
||||
} else {
|
||||
Operand op;
|
||||
op.type = Operand::kTypeShiftRegister;
|
||||
|
||||
op.shift_reg.reg = Reg(kActionRead, rclass, rtype, reg_num);
|
||||
op.shift_reg.shift_op = shift_type;
|
||||
op.shift_reg.shift_size = shift_size;
|
||||
op.shift_reg.reg = Reg(kActionRead, rclass, rtype, reg_num);
|
||||
op.shift_reg.shift_op = shift_type;
|
||||
op.shift_reg.shift_size = shift_size;
|
||||
|
||||
op.size = op.shift_reg.reg.size;
|
||||
op.action = Operand::kActionRead;
|
||||
op.size = op.shift_reg.reg.size;
|
||||
op.action = Operand::kActionRead;
|
||||
inst.operands.push_back(op);
|
||||
}
|
||||
}
|
||||
|
||||
enum ImmType {
|
||||
@@ -312,7 +320,7 @@ static void AddImmOperand(Instruction &inst, uint64_t val,
|
||||
}
|
||||
|
||||
static void AddPCRegOp(Instruction &inst, Operand::Action action, int64_t disp,
|
||||
Operand::Address::Kind opKind) {
|
||||
Operand::Address::Kind op_kind) {
|
||||
Operand op;
|
||||
op.type = Operand::kTypeAddress;
|
||||
op.size = 64;
|
||||
@@ -320,7 +328,7 @@ static void AddPCRegOp(Instruction &inst, Operand::Action action, int64_t disp,
|
||||
op.addr.base_reg.name = "PC";
|
||||
op.addr.base_reg.size = 64;
|
||||
op.addr.displacement = disp;
|
||||
op.addr.kind = opKind;
|
||||
op.addr.kind = op_kind;
|
||||
op.action = action;
|
||||
inst.operands.push_back(op);
|
||||
}
|
||||
@@ -897,7 +905,6 @@ bool TryDecodeMOVK_32_MOVEWIDE(const InstData &data, Instruction &inst) {
|
||||
if ((data.hw >> 1) & 1) {
|
||||
return false; // if sf == '0' && hw<1> == '1' then UnallocatedEncoding();
|
||||
}
|
||||
|
||||
AddRegOperand(inst, kActionReadWrite, kRegW, kUseAsValue, data.Rd);
|
||||
AddImmOperand(inst, data.imm16.uimm);
|
||||
AddImmOperand(inst, data.hw << 4, kUnsigned, 8); // pos = UInt(hw:'0000');
|
||||
@@ -933,12 +940,16 @@ bool TryDecodeMOVN_64_MOVEWIDE(const InstData &data, Instruction &inst) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ADR <Xd>, <label>
|
||||
bool TryDecodeADR_ONLY_PCRELADDR(const InstData &data, Instruction &inst) {
|
||||
AddRegOperand(inst, kActionWrite, kRegX, kUseAsValue, data.Rd);
|
||||
AddPCDisp(inst, static_cast<int64_t>(data.immhi_immlo.simm21));
|
||||
return false;
|
||||
}
|
||||
|
||||
// ADRP <Xd>, <label>
|
||||
bool TryDecodeADRP_ONLY_PCRELADDR(const InstData &data, Instruction &inst) {
|
||||
// writes to a register
|
||||
AddRegOperand(inst, kActionWrite, kRegX, kUseAsValue, data.Rd);
|
||||
|
||||
// The label is shifted left with 12 bits of zero and then added to `PC`.
|
||||
AddPCDisp(inst, static_cast<int64_t>(data.immhi_immlo.simm21) << 12ULL);
|
||||
return true;
|
||||
}
|
||||
@@ -1070,28 +1081,55 @@ static bool ShiftImmediate(uint64_t &value, uint8_t shift) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ADD <Wd|WSP>, <Wn|WSP>, #<imm>{, <shift>}
|
||||
bool TryDecodeADD_32_ADDSUB_IMM(const InstData &data, Instruction &inst) {
|
||||
auto imm = data.imm12.uimm;
|
||||
if (!ShiftImmediate(imm, data.shift)) {
|
||||
return false;
|
||||
}
|
||||
AddRegOperand(inst, kActionWrite, kRegW, kUseAsAddress, data.Rd);
|
||||
AddRegOperand(inst, kActionRead, kRegW, kUseAsAddress, data.Rn);
|
||||
AddImmOperand(inst, imm);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ADD <Xd|SP>, <Xn|SP>, #<imm>{, <shift>}
|
||||
bool TryDecodeADD_64_ADDSUB_IMM(const InstData &data, Instruction &inst) {
|
||||
auto imm = data.imm12.uimm;
|
||||
if (!ShiftImmediate(imm, data.shift)) {
|
||||
return false;
|
||||
}
|
||||
AddRegOperand(inst, kActionWrite, kRegX, kUseAsValue, data.Rd);
|
||||
AddRegOperand(inst, kActionRead, kRegX, kUseAsValue, data.Rn);
|
||||
AddRegOperand(inst, kActionWrite, kRegX, kUseAsAddress, data.Rd);
|
||||
AddRegOperand(inst, kActionRead, kRegX, kUseAsAddress, data.Rn);
|
||||
AddImmOperand(inst, imm);
|
||||
return true;
|
||||
}
|
||||
|
||||
// SUB <Wd>, <Wn>, <Wm>{, <shift> #<amount>}
|
||||
bool TryDecodeSUB_32_ADDSUB_SHIFT(const InstData &data, Instruction &inst) {
|
||||
if (1 & (data.imm6.uimm >> 5)) {
|
||||
return false; // `if sf == '0' && imm6<5> == '1' then ReservedValue();`.
|
||||
}
|
||||
auto shift_type = static_cast<Shift>(data.shift);
|
||||
if (shift_type == kShiftROR) {
|
||||
return false; // Shift type '11' is a reserved value.
|
||||
}
|
||||
AddRegOperand(inst, kActionWrite, kRegW, kUseAsValue, data.Rd);
|
||||
AddRegOperand(inst, kActionRead, kRegW, kUseAsValue, data.Rn);
|
||||
AddShiftRegOperand(inst, kRegW, kUseAsValue, data.Rm,
|
||||
GetOperandShift(shift_type), data.imm6.uimm);
|
||||
return true;
|
||||
}
|
||||
|
||||
// SUB <Xd>, <Xn>, <Xm>{, <shift> #<amount>}
|
||||
bool TryDecodeSUB_64_ADDSUB_SHIFT(const InstData &data, Instruction &inst) {
|
||||
AddRegOperand(inst, kActionWrite, kRegX, kUseAsValue, data.Rd);
|
||||
AddRegOperand(inst, kActionRead, kRegX, kUseAsValue, data.Rn);
|
||||
|
||||
auto shift_type = static_cast<Shift>(data.shift);
|
||||
if (shift_type == kShiftROR) {
|
||||
return false; // Shift type '11' is a reserved value.
|
||||
}
|
||||
AddShiftRegOperand(inst, kRegW, kUseAsValue, data.Rm,
|
||||
AddRegOperand(inst, kActionWrite, kRegX, kUseAsValue, data.Rd);
|
||||
AddRegOperand(inst, kActionRead, kRegX, kUseAsValue, data.Rn);
|
||||
AddShiftRegOperand(inst, kRegX, kUseAsValue, data.Rm,
|
||||
GetOperandShift(shift_type), data.imm6.uimm);
|
||||
return true;
|
||||
}
|
||||
@@ -1099,11 +1137,11 @@ bool TryDecodeSUB_64_ADDSUB_SHIFT(const InstData &data, Instruction &inst) {
|
||||
// CMP <Wn>, <Wm>{, <shift> #<amount>}
|
||||
bool TryDecodeCMP_SUBS_32_ADDSUB_SHIFT(const InstData &data,
|
||||
Instruction &inst) {
|
||||
AddRegOperand(inst, kActionRead, kRegW, kUseAsValue, data.Rn);
|
||||
auto shift_type = static_cast<Shift>(data.shift);
|
||||
if (shift_type == kShiftROR) {
|
||||
return false; // Shift type '11' is a reserved value.
|
||||
}
|
||||
AddRegOperand(inst, kActionRead, kRegW, kUseAsValue, data.Rn);
|
||||
AddShiftRegOperand(inst, kRegW, kUseAsValue, data.Rm,
|
||||
GetOperandShift(shift_type), data.imm6.uimm);
|
||||
return true;
|
||||
@@ -1112,11 +1150,11 @@ bool TryDecodeCMP_SUBS_32_ADDSUB_SHIFT(const InstData &data,
|
||||
// CMP <Xn>, <Xm>{, <shift> #<amount>}
|
||||
bool TryDecodeCMP_SUBS_64_ADDSUB_SHIFT(const InstData &data,
|
||||
Instruction &inst) {
|
||||
AddRegOperand(inst, kActionRead, kRegX, kUseAsValue, data.Rn);
|
||||
auto shift_type = static_cast<Shift>(data.shift);
|
||||
if (shift_type == kShiftROR) {
|
||||
return false; // Shift type '11' is a reserved value.
|
||||
}
|
||||
AddRegOperand(inst, kActionRead, kRegX, kUseAsValue, data.Rn);
|
||||
AddShiftRegOperand(inst, kRegX, kUseAsValue, data.Rm,
|
||||
GetOperandShift(shift_type), data.imm6.uimm);
|
||||
return true;
|
||||
@@ -1174,7 +1212,7 @@ bool TryDecodeB_ONLY_CONDBRANCH(const InstData &data, Instruction &inst) {
|
||||
|
||||
// STRB <Wt>, [<Xn|SP>{, #<pimm>}]
|
||||
bool TryDecodeSTRB_32_LDST_POS(const InstData &data, Instruction &inst) {
|
||||
AddRegOperand(inst, kActionRead, kRegX, kUseAsValue, data.Rt);
|
||||
AddRegOperand(inst, kActionRead, kRegW, kUseAsValue, data.Rt);
|
||||
AddBasePlusOffsetMemOp(inst, kActionWrite, 8, data.Rn,
|
||||
data.imm12.uimm);
|
||||
return true;
|
||||
@@ -1182,7 +1220,7 @@ bool TryDecodeSTRB_32_LDST_POS(const InstData &data, Instruction &inst) {
|
||||
|
||||
// LDRB <Wt>, [<Xn|SP>{, #<pimm>}]
|
||||
bool TryDecodeLDRB_32_LDST_POS(const InstData &data, Instruction &inst) {
|
||||
AddRegOperand(inst, kActionRead, kRegX, kUseAsValue, data.Rt);
|
||||
AddRegOperand(inst, kActionRead, kRegW, kUseAsValue, data.Rt);
|
||||
AddBasePlusOffsetMemOp(inst, kActionRead, 8, data.Rn,
|
||||
data.imm12.uimm);
|
||||
return true;
|
||||
@@ -1196,40 +1234,43 @@ bool TryDecodeASR_SBFM_64M_BITFIELD(const InstData &data, Instruction &inst) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ADD <Xd>, <Xn>, <Xm>{, <shift> #<amount>}
|
||||
bool TryDecodeADD_64_ADDSUB_SHIFT(const InstData &data, Instruction &inst) {
|
||||
AddRegOperand(inst, kActionWrite, kRegX, kUseAsValue, data.Rd);
|
||||
AddRegOperand(inst, kActionRead, kRegX, kUseAsValue, data.Rn);
|
||||
// ADD <Wd>, <Wn>, <Wm>{, <shift> #<amount>}
|
||||
bool TryDecodeADD_32_ADDSUB_SHIFT(const InstData &data, Instruction &inst) {
|
||||
if (1 & (data.imm6.uimm >> 5)) {
|
||||
return false; // if sf == '0' && imm6<5> == '1' then ReservedValue();.
|
||||
}
|
||||
auto shift_type = static_cast<Shift>(data.shift);
|
||||
if (shift_type == kShiftROR) {
|
||||
return false; // Shift type '11' is a reserved value.
|
||||
}
|
||||
AddRegOperand(inst, kActionWrite, kRegW, kUseAsValue, data.Rd);
|
||||
AddRegOperand(inst, kActionRead, kRegW, kUseAsValue, data.Rn);
|
||||
AddShiftRegOperand(inst, kRegW, kUseAsValue, data.Rm,
|
||||
GetOperandShift(shift_type), data.imm6.uimm);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ADD <Xd>, <Xn>, <Xm>{, <shift> #<amount>}
|
||||
bool TryDecodeADD_64_ADDSUB_SHIFT(const InstData &data, Instruction &inst) {
|
||||
auto shift_type = static_cast<Shift>(data.shift);
|
||||
if (shift_type == kShiftROR) {
|
||||
return false; // Shift type '11' is a reserved value.
|
||||
}
|
||||
AddRegOperand(inst, kActionWrite, kRegX, kUseAsValue, data.Rd);
|
||||
AddRegOperand(inst, kActionRead, kRegX, kUseAsValue, data.Rn);
|
||||
AddShiftRegOperand(inst, kRegX, kUseAsValue, data.Rm,
|
||||
GetOperandShift(shift_type), data.imm6.uimm);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename S>
|
||||
S ShiftNotImmediate(S input, S count) {
|
||||
S result = 0;
|
||||
result = static_cast<S>(input << count);
|
||||
return ~result;
|
||||
}
|
||||
|
||||
// MOV <Wd>, #<imm>
|
||||
bool TryDecodeMOV_MOVN_32_MOVEWIDE(const InstData &data, Instruction &inst) {
|
||||
if ((data.hw & 0x2) != 0) {
|
||||
return false;
|
||||
}
|
||||
return TryDecodeMOVN_32_MOVEWIDE(data, inst);
|
||||
}
|
||||
|
||||
AddRegOperand(inst, kActionWrite, kRegW, kUseAsValue, data.Rd);
|
||||
|
||||
auto pos = static_cast<uint32_t>(data.hw << 4);
|
||||
auto imm16 = data.imm16.uimm;
|
||||
auto fixedImm = ShiftNotImmediate<uint32_t>(imm16, pos);
|
||||
AddImmOperand(inst, fixedImm, kUnsigned, 32);
|
||||
|
||||
return true;
|
||||
// MOV <Xd>, #<imm>
|
||||
bool TryDecodeMOV_MOVN_64_MOVEWIDE(const InstData &data, Instruction &inst) {
|
||||
return TryDecodeMOVN_64_MOVEWIDE(data, inst);
|
||||
}
|
||||
|
||||
// STRH <Wt>, [<Xn|SP>{, #<pimm>}]
|
||||
@@ -1240,23 +1281,26 @@ bool TryDecodeSTRH_32_LDST_POS(const InstData &data, Instruction &inst) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// EOR <Wd>, <Wn>, <Wm>{, <shift> #<amount>}
|
||||
bool TryDecodeEOR_32_LOG_SHIFT(const InstData &data, Instruction &inst) {
|
||||
if (1 & (data.imm6.uimm >> 5)) {
|
||||
return false; // `if sf == '0' && imm6<5> == '1' then ReservedValue();`.
|
||||
}
|
||||
auto shift_type = GetOperandShift(static_cast<Shift>(data.shift));
|
||||
AddRegOperand(inst, kActionWrite, kRegW, kUseAsValue, data.Rd);
|
||||
AddRegOperand(inst, kActionRead, kRegW, kUseAsValue, data.Rn);
|
||||
AddShiftRegOperand(inst, kRegW, kUseAsValue, data.Rm,
|
||||
shift_type, data.imm6.uimm);
|
||||
return true;
|
||||
}
|
||||
|
||||
// EOR <Xd>, <Xn>, <Xm>{, <shift> #<amount>}
|
||||
bool TryDecodeEOR_64_LOG_SHIFT(const InstData &data, Instruction &inst) {
|
||||
auto shift_type = GetOperandShift(static_cast<Shift>(data.shift));
|
||||
AddRegOperand(inst, kActionWrite, kRegX, kUseAsValue, data.Rd);
|
||||
AddRegOperand(inst, kActionRead, kRegX, kUseAsValue, data.Rn);
|
||||
|
||||
Shift shift_type = static_cast<Shift>(data.shift);
|
||||
|
||||
// Create a shift register operand for the second source value.
|
||||
Operand op;
|
||||
op.type = Operand::kTypeShiftRegister;
|
||||
op.size = kPCWidth;
|
||||
op.action = Operand::kActionRead;
|
||||
op.shift_reg.reg = Reg(kActionRead, kRegX, kUseAsValue, data.Rm);
|
||||
op.shift_reg.shift_op = GetOperandShift(shift_type);
|
||||
op.shift_reg.shift_size = data.imm6.uimm;
|
||||
inst.operands.push_back(op);
|
||||
|
||||
AddShiftRegOperand(inst, kRegX, kUseAsValue, data.Rm,
|
||||
shift_type, data.imm6.uimm);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1381,7 +1425,7 @@ bool TryDecodeMOV_ORR_64_LOG_IMM(const InstData &data, Instruction &inst) {
|
||||
true, 64, &wmask, nullptr)) {
|
||||
return false;
|
||||
}
|
||||
AddRegOperand(inst, kActionWrite, kRegW, kUseAsAddress, data.Rd);
|
||||
AddRegOperand(inst, kActionWrite, kRegX, kUseAsAddress, data.Rd);
|
||||
AddImmOperand(inst, wmask, kUnsigned, 64);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -10507,44 +10507,6 @@ bool TryDecodeLDLAR_LR64_LDSTEXCL(const InstData &, Instruction &) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ADD ADD_32_addsub_shift:
|
||||
// 0 x Rd 0
|
||||
// 1 x Rd 1
|
||||
// 2 x Rd 2
|
||||
// 3 x Rd 3
|
||||
// 4 x Rd 4
|
||||
// 5 x Rn 0
|
||||
// 6 x Rn 1
|
||||
// 7 x Rn 2
|
||||
// 8 x Rn 3
|
||||
// 9 x Rn 4
|
||||
// 10 x imm6 0
|
||||
// 11 x imm6 1
|
||||
// 12 x imm6 2
|
||||
// 13 x imm6 3
|
||||
// 14 x imm6 4
|
||||
// 15 x imm6 5
|
||||
// 16 x Rm 0
|
||||
// 17 x Rm 1
|
||||
// 18 x Rm 2
|
||||
// 19 x Rm 3
|
||||
// 20 x Rm 4
|
||||
// 21 0
|
||||
// 22 x shift 0
|
||||
// 23 x shift 1
|
||||
// 24 1
|
||||
// 25 1
|
||||
// 26 0
|
||||
// 27 1
|
||||
// 28 0
|
||||
// 29 0 S 0
|
||||
// 30 0 op 0
|
||||
// 31 0 sf 0
|
||||
// ADD <Wd>, <Wn>, <Wm>{, <shift> #<amount>}
|
||||
bool TryDecodeADD_32_ADDSUB_SHIFT(const InstData &, Instruction &) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// SSHLL SSHLL_asimdshf_L:
|
||||
// 0 x Rd 0
|
||||
// 1 x Rd 1
|
||||
@@ -22971,44 +22933,6 @@ bool TryDecodeSDIV_64_DP_2SRC(const InstData &, Instruction &) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ADD ADD_32_addsub_imm:
|
||||
// 0 x Rd 0
|
||||
// 1 x Rd 1
|
||||
// 2 x Rd 2
|
||||
// 3 x Rd 3
|
||||
// 4 x Rd 4
|
||||
// 5 x Rn 0
|
||||
// 6 x Rn 1
|
||||
// 7 x Rn 2
|
||||
// 8 x Rn 3
|
||||
// 9 x Rn 4
|
||||
// 10 x imm12 0
|
||||
// 11 x imm12 1
|
||||
// 12 x imm12 2
|
||||
// 13 x imm12 3
|
||||
// 14 x imm12 4
|
||||
// 15 x imm12 5
|
||||
// 16 x imm12 6
|
||||
// 17 x imm12 7
|
||||
// 18 x imm12 8
|
||||
// 19 x imm12 9
|
||||
// 20 x imm12 10
|
||||
// 21 x imm12 11
|
||||
// 22 x shift 0
|
||||
// 23 x shift 1
|
||||
// 24 1
|
||||
// 25 0
|
||||
// 26 0
|
||||
// 27 0
|
||||
// 28 1
|
||||
// 29 0 S 0
|
||||
// 30 0 op 0
|
||||
// 31 0 sf 0
|
||||
// ADD <Wd|WSP>, <Wn|WSP>, #<imm>{, <shift>}
|
||||
bool TryDecodeADD_32_ADDSUB_IMM(const InstData &, Instruction &) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// RBIT RBIT_32_dp_1src:
|
||||
// 0 x Rd 0
|
||||
// 1 x Rd 1
|
||||
@@ -44631,44 +44555,6 @@ bool TryDecodeSTP_Q_LDSTPAIR_OFF(const InstData &, Instruction &) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ADR ADR_only_pcreladdr:
|
||||
// 0 x Rd 0
|
||||
// 1 x Rd 1
|
||||
// 2 x Rd 2
|
||||
// 3 x Rd 3
|
||||
// 4 x Rd 4
|
||||
// 5 x immhi 0
|
||||
// 6 x immhi 1
|
||||
// 7 x immhi 2
|
||||
// 8 x immhi 3
|
||||
// 9 x immhi 4
|
||||
// 10 x immhi 5
|
||||
// 11 x immhi 6
|
||||
// 12 x immhi 7
|
||||
// 13 x immhi 8
|
||||
// 14 x immhi 9
|
||||
// 15 x immhi 10
|
||||
// 16 x immhi 11
|
||||
// 17 x immhi 12
|
||||
// 18 x immhi 13
|
||||
// 19 x immhi 14
|
||||
// 20 x immhi 15
|
||||
// 21 x immhi 16
|
||||
// 22 x immhi 17
|
||||
// 23 x immhi 18
|
||||
// 24 0
|
||||
// 25 0
|
||||
// 26 0
|
||||
// 27 0
|
||||
// 28 1
|
||||
// 29 x immlo 0
|
||||
// 30 x immlo 1
|
||||
// 31 0 op 0
|
||||
// ADR <Xd>, <label>
|
||||
bool TryDecodeADR_ONLY_PCRELADDR(const InstData &, Instruction &) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// LDSMINAB LDSMINAB_32_memop:
|
||||
// 0 x Rt 0
|
||||
// 1 x Rt 1
|
||||
@@ -46075,44 +45961,6 @@ bool TryDecodeLDURH_32_LDST_UNSCALED(const InstData &, Instruction &) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// SUB SUB_32_addsub_shift:
|
||||
// 0 x Rd 0
|
||||
// 1 x Rd 1
|
||||
// 2 x Rd 2
|
||||
// 3 x Rd 3
|
||||
// 4 x Rd 4
|
||||
// 5 x Rn 0
|
||||
// 6 x Rn 1
|
||||
// 7 x Rn 2
|
||||
// 8 x Rn 3
|
||||
// 9 x Rn 4
|
||||
// 10 x imm6 0
|
||||
// 11 x imm6 1
|
||||
// 12 x imm6 2
|
||||
// 13 x imm6 3
|
||||
// 14 x imm6 4
|
||||
// 15 x imm6 5
|
||||
// 16 x Rm 0
|
||||
// 17 x Rm 1
|
||||
// 18 x Rm 2
|
||||
// 19 x Rm 3
|
||||
// 20 x Rm 4
|
||||
// 21 0
|
||||
// 22 x shift 0
|
||||
// 23 x shift 1
|
||||
// 24 1
|
||||
// 25 1
|
||||
// 26 0
|
||||
// 27 1
|
||||
// 28 0
|
||||
// 29 0 S 0
|
||||
// 30 1 op 0
|
||||
// 31 0 sf 0
|
||||
// SUB <Wd>, <Wn>, <Wm>{, <shift> #<amount>}
|
||||
bool TryDecodeSUB_32_ADDSUB_SHIFT(const InstData &, Instruction &) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// SRI SRI_asisdshf_R:
|
||||
// 0 x Rd 0
|
||||
// 1 x Rd 1
|
||||
@@ -51813,44 +51661,6 @@ bool TryDecodeFMLS_ASIMDSAME_ONLY(const InstData &, Instruction &) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// EOR EOR_32_log_shift:
|
||||
// 0 x Rd 0
|
||||
// 1 x Rd 1
|
||||
// 2 x Rd 2
|
||||
// 3 x Rd 3
|
||||
// 4 x Rd 4
|
||||
// 5 x Rn 0
|
||||
// 6 x Rn 1
|
||||
// 7 x Rn 2
|
||||
// 8 x Rn 3
|
||||
// 9 x Rn 4
|
||||
// 10 x imm6 0
|
||||
// 11 x imm6 1
|
||||
// 12 x imm6 2
|
||||
// 13 x imm6 3
|
||||
// 14 x imm6 4
|
||||
// 15 x imm6 5
|
||||
// 16 x Rm 0
|
||||
// 17 x Rm 1
|
||||
// 18 x Rm 2
|
||||
// 19 x Rm 3
|
||||
// 20 x Rm 4
|
||||
// 21 0 N 0
|
||||
// 22 x shift 0
|
||||
// 23 x shift 1
|
||||
// 24 0
|
||||
// 25 1
|
||||
// 26 0
|
||||
// 27 1
|
||||
// 28 0
|
||||
// 29 0 opc 0
|
||||
// 30 1 opc 1
|
||||
// 31 0 sf 0
|
||||
// EOR <Wd>, <Wn>, <Wm>{, <shift> #<amount>}
|
||||
bool TryDecodeEOR_32_LOG_SHIFT(const InstData &, Instruction &) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// LDSMAXA LDSMAXA_32_memop:
|
||||
// 0 x Rt 0
|
||||
// 1 x Rt 1
|
||||
@@ -55955,44 +55765,6 @@ bool TryDecodeUADDL_ASIMDDIFF_L(const InstData &, Instruction &) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// MOVN MOV_MOVN_64_movewide:
|
||||
// 0 x Rd 0
|
||||
// 1 x Rd 1
|
||||
// 2 x Rd 2
|
||||
// 3 x Rd 3
|
||||
// 4 x Rd 4
|
||||
// 5 x imm16 0
|
||||
// 6 x imm16 1
|
||||
// 7 x imm16 2
|
||||
// 8 x imm16 3
|
||||
// 9 x imm16 4
|
||||
// 10 x imm16 5
|
||||
// 11 x imm16 6
|
||||
// 12 x imm16 7
|
||||
// 13 x imm16 8
|
||||
// 14 x imm16 9
|
||||
// 15 x imm16 10
|
||||
// 16 x imm16 11
|
||||
// 17 x imm16 12
|
||||
// 18 x imm16 13
|
||||
// 19 x imm16 14
|
||||
// 20 x imm16 15
|
||||
// 21 x hw 0
|
||||
// 22 x hw 1
|
||||
// 23 1
|
||||
// 24 0
|
||||
// 25 1
|
||||
// 26 0
|
||||
// 27 0
|
||||
// 28 1
|
||||
// 29 0 opc 0
|
||||
// 30 0 opc 1
|
||||
// 31 1 sf 0
|
||||
// MOV <Xd>, #<imm>
|
||||
bool TryDecodeMOV_MOVN_64_MOVEWIDE(const InstData &, Instruction &) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TRN2 TRN2_asimdperm_only:
|
||||
// 0 x Rd 0
|
||||
// 1 x Rd 1
|
||||
|
||||
@@ -29,101 +29,101 @@ extern "C" {
|
||||
// Note: These variables MUST be defined for all architectures.
|
||||
auto &STATE = state;
|
||||
auto &MEMORY = *memory;
|
||||
auto &PC = state.gpr.PC.qword;
|
||||
auto &PC = state.gpr.pc.qword;
|
||||
auto &BRANCH_TAKEN = branch_taken;
|
||||
|
||||
// `PC` should already have the correct value, but it's nice to make sure
|
||||
// that `curr_pc` is used throughout, as it helps with certain downstream
|
||||
// uses to be able to depend on the optimizer not eliminating `curr_pc`.
|
||||
PC = curr_pc;
|
||||
auto &WPC = state.gpr.PC.dword;
|
||||
auto &WPC = state.gpr.pc.dword;
|
||||
|
||||
auto &W0 = state.gpr.X0.dword;
|
||||
auto &W1 = state.gpr.X1.dword;
|
||||
auto &W2 = state.gpr.X2.dword;
|
||||
auto &W3 = state.gpr.X3.dword;
|
||||
auto &W0 = state.gpr.x0.dword;
|
||||
auto &W1 = state.gpr.x1.dword;
|
||||
auto &W2 = state.gpr.x2.dword;
|
||||
auto &W3 = state.gpr.x3.dword;
|
||||
|
||||
auto &W4 = state.gpr.X4.dword;
|
||||
auto &W5 = state.gpr.X5.dword;
|
||||
auto &W6 = state.gpr.X6.dword;
|
||||
auto &W7 = state.gpr.X7.dword;
|
||||
auto &W4 = state.gpr.x4.dword;
|
||||
auto &W5 = state.gpr.x5.dword;
|
||||
auto &W6 = state.gpr.x6.dword;
|
||||
auto &W7 = state.gpr.x7.dword;
|
||||
|
||||
auto &W8 = state.gpr.X8.dword;
|
||||
auto &W9 = state.gpr.X9.dword;
|
||||
auto &W10 = state.gpr.X10.dword;
|
||||
auto &W11 = state.gpr.X11.dword;
|
||||
auto &W8 = state.gpr.x8.dword;
|
||||
auto &W9 = state.gpr.x9.dword;
|
||||
auto &W10 = state.gpr.x10.dword;
|
||||
auto &W11 = state.gpr.x11.dword;
|
||||
|
||||
auto &W12 = state.gpr.X12.dword;
|
||||
auto &W13 = state.gpr.X13.dword;
|
||||
auto &W14 = state.gpr.X14.dword;
|
||||
auto &W15 = state.gpr.X15.dword;
|
||||
auto &W12 = state.gpr.x12.dword;
|
||||
auto &W13 = state.gpr.x13.dword;
|
||||
auto &W14 = state.gpr.x14.dword;
|
||||
auto &W15 = state.gpr.x15.dword;
|
||||
|
||||
auto &W16 = state.gpr.X16.dword;
|
||||
auto &W17 = state.gpr.X17.dword;
|
||||
auto &W18 = state.gpr.X18.dword;
|
||||
auto &W19 = state.gpr.X19.dword;
|
||||
auto &W16 = state.gpr.x16.dword;
|
||||
auto &W17 = state.gpr.x17.dword;
|
||||
auto &W18 = state.gpr.x18.dword;
|
||||
auto &W19 = state.gpr.x19.dword;
|
||||
|
||||
auto &W20 = state.gpr.X20.dword;
|
||||
auto &W21 = state.gpr.X21.dword;
|
||||
auto &W22 = state.gpr.X22.dword;
|
||||
auto &W23 = state.gpr.X23.dword;
|
||||
auto &W20 = state.gpr.x20.dword;
|
||||
auto &W21 = state.gpr.x21.dword;
|
||||
auto &W22 = state.gpr.x22.dword;
|
||||
auto &W23 = state.gpr.x23.dword;
|
||||
|
||||
auto &W24 = state.gpr.X24.dword;
|
||||
auto &W25 = state.gpr.X25.dword;
|
||||
auto &W26 = state.gpr.X26.dword;
|
||||
auto &W27 = state.gpr.X27.dword;
|
||||
auto &W24 = state.gpr.x24.dword;
|
||||
auto &W25 = state.gpr.x25.dword;
|
||||
auto &W26 = state.gpr.x26.dword;
|
||||
auto &W27 = state.gpr.x27.dword;
|
||||
|
||||
auto &W28 = state.gpr.X28.dword;
|
||||
auto &W29 = state.gpr.X29.dword;
|
||||
auto &W30 = state.gpr.X30.dword;
|
||||
auto &W28 = state.gpr.x28.dword;
|
||||
auto &W29 = state.gpr.x29.dword;
|
||||
auto &W30 = state.gpr.x30.dword;
|
||||
|
||||
auto &X0 = state.gpr.X0.qword;
|
||||
auto &X1 = state.gpr.X1.qword;
|
||||
auto &X2 = state.gpr.X2.qword;
|
||||
auto &X3 = state.gpr.X2.qword;
|
||||
auto &X0 = state.gpr.x0.qword;
|
||||
auto &X1 = state.gpr.x1.qword;
|
||||
auto &X2 = state.gpr.x2.qword;
|
||||
auto &X3 = state.gpr.x2.qword;
|
||||
|
||||
auto &X4 = state.gpr.X4.qword;
|
||||
auto &X5 = state.gpr.X5.qword;
|
||||
auto &X6 = state.gpr.X6.qword;
|
||||
auto &X7 = state.gpr.X7.qword;
|
||||
auto &X4 = state.gpr.x4.qword;
|
||||
auto &X5 = state.gpr.x5.qword;
|
||||
auto &X6 = state.gpr.x6.qword;
|
||||
auto &X7 = state.gpr.x7.qword;
|
||||
|
||||
auto &X8 = state.gpr.X8.qword;
|
||||
auto &X9 = state.gpr.X9.qword;
|
||||
auto &X10 = state.gpr.X10.qword;
|
||||
auto &X11 = state.gpr.X11.qword;
|
||||
auto &X8 = state.gpr.x8.qword;
|
||||
auto &X9 = state.gpr.x9.qword;
|
||||
auto &X10 = state.gpr.x10.qword;
|
||||
auto &X11 = state.gpr.x11.qword;
|
||||
|
||||
auto &X12 = state.gpr.X12.qword;
|
||||
auto &X13 = state.gpr.X13.qword;
|
||||
auto &X14 = state.gpr.X14.qword;
|
||||
auto &X15 = state.gpr.X15.qword;
|
||||
auto &X12 = state.gpr.x12.qword;
|
||||
auto &X13 = state.gpr.x13.qword;
|
||||
auto &X14 = state.gpr.x14.qword;
|
||||
auto &X15 = state.gpr.x15.qword;
|
||||
|
||||
auto &X16 = state.gpr.X16.qword;
|
||||
auto &X17 = state.gpr.X17.qword;
|
||||
auto &X18 = state.gpr.X18.qword;
|
||||
auto &X19 = state.gpr.X19.qword;
|
||||
auto &X16 = state.gpr.x16.qword;
|
||||
auto &X17 = state.gpr.x17.qword;
|
||||
auto &X18 = state.gpr.x18.qword;
|
||||
auto &X19 = state.gpr.x19.qword;
|
||||
|
||||
auto &X20 = state.gpr.X20.qword;
|
||||
auto &X21 = state.gpr.X21.qword;
|
||||
auto &X22 = state.gpr.X22.qword;
|
||||
auto &X23 = state.gpr.X23.qword;
|
||||
auto &X20 = state.gpr.x20.qword;
|
||||
auto &X21 = state.gpr.x21.qword;
|
||||
auto &X22 = state.gpr.x22.qword;
|
||||
auto &X23 = state.gpr.x23.qword;
|
||||
|
||||
auto &X24 = state.gpr.X24.qword;
|
||||
auto &X25 = state.gpr.X25.qword;
|
||||
auto &X26 = state.gpr.X26.qword;
|
||||
auto &X27 = state.gpr.X27.qword;
|
||||
auto &X24 = state.gpr.x24.qword;
|
||||
auto &X25 = state.gpr.x25.qword;
|
||||
auto &X26 = state.gpr.x26.qword;
|
||||
auto &X27 = state.gpr.x27.qword;
|
||||
|
||||
auto &X28 = state.gpr.X28.qword;
|
||||
auto &X29 = state.gpr.X29.qword;
|
||||
auto &X30 = state.gpr.X30.qword;
|
||||
auto &X28 = state.gpr.x28.qword;
|
||||
auto &X29 = state.gpr.x29.qword;
|
||||
auto &X30 = state.gpr.x30.qword;
|
||||
|
||||
auto &FP = state.gpr.X29.qword;
|
||||
auto &WFP = state.gpr.X29.qword;
|
||||
auto &FP = state.gpr.x29.qword;
|
||||
auto &WFP = state.gpr.x29.qword;
|
||||
|
||||
auto &LP = state.gpr.X30.qword;
|
||||
auto &WLP = state.gpr.X30.qword;
|
||||
auto &LP = state.gpr.x30.qword;
|
||||
auto &WLP = state.gpr.x30.qword;
|
||||
|
||||
auto &SP = state.gpr.X31.qword;
|
||||
auto &WSP = state.gpr.X31.dword;
|
||||
auto &SP = state.gpr.sp.qword;
|
||||
auto &WSP = state.gpr.sp.dword;
|
||||
|
||||
addr_t zero = 0;
|
||||
auto &WZR = reinterpret_cast<uint32_t &>(zero);
|
||||
@@ -267,6 +267,9 @@ extern "C" {
|
||||
auto &V30 = state.simd.v[30];
|
||||
auto &V31 = state.simd.v[31];
|
||||
|
||||
auto &TPIDR_EL0 = state.sr.tpidr_el0;
|
||||
auto &TPIDRRO_EL0 = state.sr.tpidrro_el0;
|
||||
|
||||
// Lifted code will be placed here in clones versions of this function.
|
||||
return memory;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ set(ARMRUNTIME_INCLUDEDIRECTORIES ${CMAKE_SOURCE_DIR})
|
||||
function (add_runtime_helper target_name address_bit_size little_endian)
|
||||
message(" > Generating runtime target: ${target_name}")
|
||||
|
||||
add_runtime(${target_name} ARCH aarch64 SOURCES ${ARMRUNTIME_SOURCEFILES} ADDRESS_SIZE ${address_bit_size})
|
||||
add_runtime(${target_name} SOURCES ${ARMRUNTIME_SOURCEFILES} ADDRESS_SIZE ${address_bit_size})
|
||||
target_include_directories(${target_name} PRIVATE ${ARMRUNTIME_INCLUDEDIRECTORIES})
|
||||
|
||||
if (little_endian)
|
||||
|
||||
@@ -26,56 +26,55 @@
|
||||
#include "remill/Arch/AArch64/Runtime/Operators.h"
|
||||
#include "remill/Arch/AArch64/Runtime/Types.h"
|
||||
|
||||
#define REG_PC state.gpr.PC.qword
|
||||
#define REG_SP state.gpr.SP.qword
|
||||
#define REG_LP state.gpr.X30.qword
|
||||
#define REG_FP state.gpr.X29.qword
|
||||
#define REG_XZR state.gpr.X31.qword
|
||||
#define REG_PC state.gpr.pc.qword
|
||||
#define REG_SP state.gpr.sp.qword
|
||||
#define REG_LP state.gpr.x30.qword
|
||||
#define REG_FP state.gpr.x29.qword
|
||||
#define REG_XZR static_cast<uint64_t>(0)
|
||||
|
||||
#define REG_X0 state.gpr.X0.qword
|
||||
#define REG_X1 state.gpr.X1.qword
|
||||
#define REG_X2 state.gpr.X2.qword
|
||||
#define REG_X3 state.gpr.X3.qword
|
||||
#define REG_X0 state.gpr.x0.qword
|
||||
#define REG_X1 state.gpr.x1.qword
|
||||
#define REG_X2 state.gpr.x2.qword
|
||||
#define REG_X3 state.gpr.x3.qword
|
||||
|
||||
#define REG_X4 state.gpr.X4.qword
|
||||
#define REG_X5 state.gpr.X5.qword
|
||||
#define REG_X6 state.gpr.X6.qword
|
||||
#define REG_X7 state.gpr.X7.qword
|
||||
#define REG_X4 state.gpr.x4.qword
|
||||
#define REG_X5 state.gpr.x5.qword
|
||||
#define REG_X6 state.gpr.x6.qword
|
||||
#define REG_X7 state.gpr.x7.qword
|
||||
|
||||
#define REG_X8 state.gpr.X8.qword
|
||||
#define REG_X9 state.gpr.X9.qword
|
||||
#define REG_X10 state.gpr.X10.qword
|
||||
#define REG_X11 state.gpr.X11.qword
|
||||
#define REG_X8 state.gpr.x8.qword
|
||||
#define REG_X9 state.gpr.x9.qword
|
||||
#define REG_X10 state.gpr.x10.qword
|
||||
#define REG_X11 state.gpr.x11.qword
|
||||
|
||||
#define REG_X12 state.gpr.X12.qword
|
||||
#define REG_X13 state.gpr.X13.qword
|
||||
#define REG_X14 state.gpr.X14.qword
|
||||
#define REG_X15 state.gpr.X15.qword
|
||||
#define REG_X12 state.gpr.x12.qword
|
||||
#define REG_X13 state.gpr.x13.qword
|
||||
#define REG_X14 state.gpr.x14.qword
|
||||
#define REG_X15 state.gpr.x15.qword
|
||||
|
||||
#define REG_X16 state.gpr.X16.qword
|
||||
#define REG_X17 state.gpr.X17.qword
|
||||
#define REG_X18 state.gpr.X18.qword
|
||||
#define REG_X19 state.gpr.X19.qword
|
||||
#define REG_X16 state.gpr.x16.qword
|
||||
#define REG_X17 state.gpr.x17.qword
|
||||
#define REG_X18 state.gpr.x18.qword
|
||||
#define REG_X19 state.gpr.x19.qword
|
||||
|
||||
#define REG_X20 state.gpr.X20.qword
|
||||
#define REG_X21 state.gpr.X21.qword
|
||||
#define REG_X22 state.gpr.X22.qword
|
||||
#define REG_X23 state.gpr.X23.qword
|
||||
#define REG_X20 state.gpr.x20.qword
|
||||
#define REG_X21 state.gpr.x21.qword
|
||||
#define REG_X22 state.gpr.x22.qword
|
||||
#define REG_X23 state.gpr.x23.qword
|
||||
|
||||
#define REG_X24 state.gpr.X24.qword
|
||||
#define REG_X25 state.gpr.X25.qword
|
||||
#define REG_X26 state.gpr.X26.qword
|
||||
#define REG_X27 state.gpr.X27.qword
|
||||
#define REG_X24 state.gpr.x24.qword
|
||||
#define REG_X25 state.gpr.x25.qword
|
||||
#define REG_X26 state.gpr.x26.qword
|
||||
#define REG_X27 state.gpr.x27.qword
|
||||
|
||||
#define REG_X28 state.gpr.X28.qword
|
||||
#define REG_X29 state.gpr.X29.qword
|
||||
#define REG_X30 state.gpr.X30.qword
|
||||
#define REG_X28 state.gpr.x28.qword
|
||||
#define REG_X29 state.gpr.x29.qword
|
||||
#define REG_X30 state.gpr.x30.qword
|
||||
|
||||
#define FLAG_Z state.pstate.Z // Zero flag.
|
||||
#define FLAG_S state.pstate.S // Sign flag.
|
||||
#define FLAG_C state.pstate.C // Carry flag.
|
||||
#define FLAG_V state.pstate.V // Overflow.
|
||||
#define FLAG_N state.pstate.N // Negative.
|
||||
#define FLAG_Z state.sr.z // Zero flag.
|
||||
#define FLAG_C state.sr.c // Carry flag.
|
||||
#define FLAG_V state.sr.v // Overflow.
|
||||
#define FLAG_N state.sr.n // Negative.
|
||||
|
||||
#define HYPER_CALL state.hyper_call
|
||||
#define INTERRUPT_VECTOR state.interrupt_vector
|
||||
|
||||
+185
-121
@@ -20,189 +20,242 @@
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic fatal "-Wpadded"
|
||||
|
||||
#define aword IF_64BIT_ELSE(qword, dword)
|
||||
|
||||
#include "remill/Arch/Runtime/State.h"
|
||||
#include "remill/Arch/Runtime/Types.h"
|
||||
|
||||
struct Reg final {
|
||||
union {
|
||||
alignas(4) uint32_t dword;
|
||||
IF_64BIT(alignas(8) uint64_t qword;)
|
||||
alignas(8) uint64_t qword;
|
||||
} __attribute__((packed));
|
||||
|
||||
IF_32BIT(uint32_t _padding0;)
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(sizeof(uint64_t) == sizeof(Reg), "Invalid packing of `Reg`.");
|
||||
static_assert(0 == __builtin_offsetof(Reg, dword),
|
||||
"Invalid packing of `Reg::dword`.");
|
||||
|
||||
IF_64BIT(static_assert(0 == __builtin_offsetof(Reg, qword),
|
||||
"Invalid packing of `Reg::qword`.");)
|
||||
static_assert(0 == __builtin_offsetof(Reg, qword),
|
||||
"Invalid packing of `Reg::qword`.");
|
||||
|
||||
struct alignas(16) GPR final {
|
||||
// Prevents LLVM from casting a `GPR` into an `i64` to access `rax`.
|
||||
struct alignas(8) GPR final {
|
||||
// Prevents LLVM from casting a `GPR` into an `i64` to access `X0`.
|
||||
volatile uint64_t _0;
|
||||
Reg X0;
|
||||
Reg x0;
|
||||
volatile uint64_t _1;
|
||||
Reg X1;
|
||||
Reg x1;
|
||||
volatile uint64_t _2;
|
||||
Reg X2;
|
||||
Reg x2;
|
||||
volatile uint64_t _3;
|
||||
Reg X3;
|
||||
Reg x3;
|
||||
volatile uint64_t _4;
|
||||
Reg X4;
|
||||
Reg x4;
|
||||
volatile uint64_t _5;
|
||||
Reg X5;
|
||||
Reg x5;
|
||||
volatile uint64_t _6;
|
||||
Reg X6;
|
||||
Reg x6;
|
||||
volatile uint64_t _7;
|
||||
Reg X7;
|
||||
Reg x7;
|
||||
volatile uint64_t _8;
|
||||
Reg X8;
|
||||
Reg x8;
|
||||
volatile uint64_t _9;
|
||||
Reg X9;
|
||||
Reg x9;
|
||||
volatile uint64_t _10;
|
||||
Reg X10;
|
||||
Reg x10;
|
||||
volatile uint64_t _11;
|
||||
Reg X11;
|
||||
Reg x11;
|
||||
volatile uint64_t _12;
|
||||
Reg X12;
|
||||
Reg x12;
|
||||
volatile uint64_t _13;
|
||||
Reg X13;
|
||||
Reg x13;
|
||||
volatile uint64_t _14;
|
||||
Reg X14;
|
||||
Reg x14;
|
||||
volatile uint64_t _15;
|
||||
Reg X15;
|
||||
Reg x15;
|
||||
volatile uint64_t _16;
|
||||
Reg X16;
|
||||
Reg x16;
|
||||
volatile uint64_t _17;
|
||||
Reg X17;
|
||||
Reg x17;
|
||||
volatile uint64_t _18;
|
||||
Reg X18;
|
||||
Reg x18;
|
||||
volatile uint64_t _19;
|
||||
Reg X19;
|
||||
Reg x19;
|
||||
volatile uint64_t _20;
|
||||
Reg X20;
|
||||
Reg x20;
|
||||
volatile uint64_t _21;
|
||||
Reg X21;
|
||||
Reg x21;
|
||||
volatile uint64_t _22;
|
||||
Reg X22;
|
||||
Reg x22;
|
||||
volatile uint64_t _23;
|
||||
Reg X23;
|
||||
Reg x23;
|
||||
volatile uint64_t _24;
|
||||
Reg X24;
|
||||
Reg x24;
|
||||
volatile uint64_t _25;
|
||||
Reg X25;
|
||||
Reg x25;
|
||||
volatile uint64_t _26;
|
||||
Reg X26;
|
||||
Reg x26;
|
||||
volatile uint64_t _27;
|
||||
Reg X27;
|
||||
Reg x27;
|
||||
volatile uint64_t _28;
|
||||
Reg X28;
|
||||
Reg x28;
|
||||
volatile uint64_t _29;
|
||||
Reg X29;
|
||||
Reg x29;
|
||||
volatile uint64_t _30;
|
||||
Reg X30;
|
||||
Reg x30;
|
||||
|
||||
// Reg 31 is called zero registers;
|
||||
volatile uint64_t _31;
|
||||
Reg X31; // Stack pointer.
|
||||
Reg sp; // Stack pointer.
|
||||
|
||||
// Program counter of the CURRENT instruction!
|
||||
volatile uint64_t _32;
|
||||
Reg PC; // Program counter.
|
||||
Reg pc; // Program counter of the CURRENT instruction!
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(528 == sizeof(GPR), "Invalid structure packing of `GPR`.");
|
||||
|
||||
union alignas(8) PSTATE_BITS final {
|
||||
union PSTATE final {
|
||||
uint64_t flat;
|
||||
struct {
|
||||
// bit 0
|
||||
uint32_t N : 1; // Negative condition flag
|
||||
uint32_t Z : 1; // Zero condition flag
|
||||
uint32_t C : 1; // Carry condition flag
|
||||
uint32_t V : 1; // Overflow condition flag
|
||||
// Bit 0.
|
||||
uint32_t N:1; // Negative condition flag.
|
||||
uint32_t Z:1; // Zero condition flag.
|
||||
uint32_t C:1; // Carry condition flag.
|
||||
uint32_t V:1; // Overflow condition flag.
|
||||
|
||||
// bit 4
|
||||
uint32_t D : 1; // Debug mask bit [AArch64 only]
|
||||
uint32_t A : 1; // Asynchronous abort mask bit
|
||||
uint32_t I : 1; // IRQ mask bit
|
||||
uint32_t F : 1; // FIQ mask bit
|
||||
// Bit 4.
|
||||
uint32_t D:1; // Debug mask bit [AArch64 only].
|
||||
uint32_t A:1; // Asynchronous abort mask bit.
|
||||
uint32_t I:1; // IRQ mask bit.
|
||||
uint32_t F:1; // FIQ mask bit.
|
||||
|
||||
// bit 8
|
||||
uint32_t SS : 1; // Single-step bit
|
||||
uint32_t IL : 1; // Illegal state bit
|
||||
uint32_t EL : 2; // Exception Level (see above)
|
||||
// Bit 8.
|
||||
uint32_t SS:1; // Single-step bit.
|
||||
uint32_t IL:1; // Illegal state bit.
|
||||
uint32_t EL:2; // Exception Level (see above).
|
||||
|
||||
// bit 12
|
||||
uint32_t nRW : 1; // not Register Width: 0=64, 1=32
|
||||
uint32_t SP : 1; // Stack pointer select: 0=SP0, 1=SPx [AArch64 only]
|
||||
uint32_t Q : 1; // Cumulative saturation flag [AArch32 only]
|
||||
uint32_t GE : 4; // Greater than or Equal flags [AArch32 only]
|
||||
// Bit 12.
|
||||
uint32_t nRW:1; // not Register Width: 0=64, 1=32
|
||||
uint32_t SP:1; // Stack pointer select: 0=SP0, 1=SPx [AArch64 only]
|
||||
uint32_t Q:1; // Cumulative saturation flag [AArch32 only]
|
||||
uint32_t GE:4; // Greater than or Equal flags [AArch32 only]
|
||||
|
||||
// bit 19
|
||||
uint32_t IT : 8; // If-then state [AArch32 only]
|
||||
uint32_t J : 1; // Jazelle state [AArch32 only]
|
||||
uint32_t T : 1; // Thumb state [AArch32 only]
|
||||
uint32_t E : 1; // Endian state [AArch32 only]
|
||||
uint32_t M : 5; // Mode field (see above) [AArch32 only]
|
||||
uint32_t reserved_flags : 29; // bits 34-63.
|
||||
// Bit 19.
|
||||
uint32_t IT:8; // If-then state [AArch32 only]
|
||||
uint32_t J:1; // Jazelle state [AArch32 only]
|
||||
uint32_t T:1; // Thumb state [AArch32 only]
|
||||
uint32_t E:1; // Endian state [AArch32 only]
|
||||
uint32_t M:5; // Mode field (see above) [AArch32 only]
|
||||
uint32_t _res0:29; // bits 34-63.
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(8 == sizeof(PSTATE_BITS),
|
||||
"Invalid structure packing of `NativeProcState`.");
|
||||
static_assert(8 == sizeof(PSTATE), "Invalid structure packing of `PSTATE`.");
|
||||
|
||||
struct alignas(8) PSTATE final {
|
||||
uint8_t _0;
|
||||
bool N; // Negative condition flag.
|
||||
uint8_t _1;
|
||||
bool Z; // Zero condition flag
|
||||
uint8_t _2;
|
||||
bool C; // Carry condition flag
|
||||
uint8_t _3;
|
||||
bool V; // Overflow condition flag
|
||||
uint8_t _4;
|
||||
uint8_t D; // Debug mask bit [AArch64 only]
|
||||
uint8_t _5;
|
||||
uint8_t A; // Asynchronous abort mask bit
|
||||
uint8_t _6;
|
||||
uint8_t I; // IRQ mask bit
|
||||
uint8_t _7;
|
||||
uint8_t F; // FIQ mask bit
|
||||
uint8_t _8;
|
||||
uint8_t SS; // Single-step bit
|
||||
uint8_t _9;
|
||||
uint8_t IL; // Illegal state bit
|
||||
uint8_t _10;
|
||||
uint8_t EL; // Exception Level (see above)
|
||||
uint8_t _11;
|
||||
uint8_t nRW; // not Register Width: 0=64, 1=32
|
||||
uint8_t _12;
|
||||
uint8_t SP; // Stack pointer select: 0=SP0, 1=SPx [AArch64 only]
|
||||
uint8_t _13;
|
||||
uint8_t Q; // Cumulative saturation flag [AArch32 only]
|
||||
uint8_t _14;
|
||||
uint8_t GE; // Greater than or Equal flags [AArch32 only]
|
||||
uint8_t _15;
|
||||
uint8_t IT; // If-then state [AArch32 only]
|
||||
uint8_t _16;
|
||||
uint8_t J; // Jazelle state [AArch32 only]
|
||||
uint8_t _17;
|
||||
uint8_t T; // Thumb state [AArch32 only]
|
||||
uint8_t _18;
|
||||
uint8_t E; // Endian state [AArch32 only]
|
||||
uint8_t _19;
|
||||
uint8_t M; // Mode field (see above) [AArch32 only]
|
||||
// Condition code register. Really, this is a 32-bit register, but
|
||||
// it is accessed 64-bit register instructions: `mrs <Xt>, nzcv`.
|
||||
union NZCV {
|
||||
uint64_t flat;
|
||||
struct {
|
||||
uint64_t _0:28;
|
||||
uint64_t v:1; // Result overflowed, bit 28.
|
||||
uint64_t c:1; // Result produced a carry.
|
||||
uint64_t z:1; // Result is zero.
|
||||
uint64_t n:1; // Result is negative, bit 31.
|
||||
uint64_t _1:32;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(8 == sizeof(NZCV), "Invalid packing of `union NZCV`.");
|
||||
|
||||
static_assert(40 == sizeof(PSTATE),
|
||||
"Invalid packing of `struct PSTATE`.");
|
||||
#if COMPILING_WITH_GCC
|
||||
using FPURoundingMode = uint64_t;
|
||||
using FPUFlushToZeroMode = uint64_t;
|
||||
using FPUDefaultNaNMode = uint64_t;
|
||||
using FPUHalfPrecisionMode = uint64_t;
|
||||
#else
|
||||
|
||||
enum FPURoundingMode : uint64_t {
|
||||
kFPURoundToNearestEven, // RN (round nearest).
|
||||
kFPURoundUpInf, // RP (round toward plus infinity).
|
||||
kFPURoundDownNegInf, // RM (round toward minus infinity).
|
||||
kFPURoundToZero // RZ (round toward zero).
|
||||
};
|
||||
|
||||
enum FPUFlushToZeroMode : uint64_t {
|
||||
kFlushToZeroDisabled,
|
||||
kFlushToZeroEnabled
|
||||
};
|
||||
|
||||
enum FPUDefaultNaNMode : uint64_t {
|
||||
kPropagateOriginalNaN,
|
||||
kPropagateDefaultNaN
|
||||
};
|
||||
|
||||
enum FPUHalfPrecisionMode : uint64_t {
|
||||
kIEEEHalfPrecisionMode,
|
||||
kAlternativeHalfPrecisionMode
|
||||
};
|
||||
#endif
|
||||
|
||||
// Floating point control register. Really, this is a 32-bit register, but
|
||||
// it is accessed 64-bit register instructions: `mrs <Xt>, fpcr`.
|
||||
union FPCR {
|
||||
uint64_t flat;
|
||||
struct {
|
||||
uint64_t _res0:22; // [21:0]
|
||||
FPURoundingMode rmode:2; // [23:22]
|
||||
FPUFlushToZeroMode fz:1; // [24]
|
||||
FPUDefaultNaNMode dn:1; // [25]
|
||||
FPUHalfPrecisionMode ahp:1; // [26]
|
||||
uint64_t _res1:5; // [31:27]
|
||||
uint64_t _1:32;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(sizeof(FPCR) == 8, "Invalid packing of `union FPCR`.");
|
||||
|
||||
// Floating point status register. Really, this is a 32-bit register, but
|
||||
// it is accessed 64-bit register instructions: `mrs <Xt>, fpsr`.
|
||||
union FPSR {
|
||||
uint64_t flat;
|
||||
struct {
|
||||
uint64_t ioc:1; // Invalid operation cumulative exception bit.
|
||||
uint64_t dzc:1; // Division by zero cumulative exception bit.
|
||||
uint64_t ofc:1; // Overflow cumulative exception bit.
|
||||
uint64_t ufc:1; // Underflow cumulative exception bit.
|
||||
uint64_t ixc:1; // Inexact cumulative exception bit.
|
||||
uint64_t _res0:2; // Bits 5 and 6.
|
||||
uint64_t idc:1; // Input denormal cumulative exception bit.
|
||||
uint64_t _res1:19; // Bits 8 through 26.
|
||||
uint64_t qc:1; // Cumulative saturation bit, bit 27.
|
||||
uint64_t v:1; // Result overflowed, bit 28. [AArch32 only]
|
||||
uint64_t c:1; // Result produced a carry. [AArch32 only]
|
||||
uint64_t z:1; // Result is zero. [AArch32 only]
|
||||
uint64_t n:1; // Result is negative, bit 31. [AArch32 only]
|
||||
uint64_t _1:32;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(sizeof(FPSR) == 8, "Invalid packing of `union FPSR`.");
|
||||
|
||||
// System registers affecting control and status of the machine.
|
||||
struct alignas(8) SR final {
|
||||
uint64_t _0;
|
||||
Reg tpidr_el0; // Thread pointer for EL0.
|
||||
|
||||
uint64_t _1;
|
||||
Reg tpidrro_el0; // Read-only thread pointer for EL0.
|
||||
|
||||
uint8_t _2;
|
||||
bool n; // Negative condition flag.
|
||||
uint8_t _3;
|
||||
bool z; // Zero condition flag
|
||||
uint8_t _4;
|
||||
bool c; // Carry condition flag
|
||||
uint8_t _5;
|
||||
bool v; // Overflow condition flag
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert((5 * 8) == sizeof(SR), "Invalid packing of `struct SR`.");
|
||||
|
||||
enum : size_t {
|
||||
kNumVecRegisters = 32
|
||||
@@ -212,19 +265,30 @@ struct alignas(16) SIMD {
|
||||
vec128_t v[kNumVecRegisters];
|
||||
};
|
||||
|
||||
static_assert(512 == sizeof(SIMD),
|
||||
"Invalid packing of `struct SIMD`.");
|
||||
static_assert(512 == sizeof(SIMD), "Invalid packing of `struct SIMD`.");
|
||||
|
||||
struct alignas(16) State final : public ArchState {
|
||||
SIMD simd; // 512 bytes.
|
||||
PSTATE_BITS native_state; // 8 bytes.
|
||||
PSTATE pstate; // 40 bytes.
|
||||
|
||||
uint64_t _0;
|
||||
|
||||
GPR gpr; // 528 bytes.
|
||||
|
||||
uint64_t _1;
|
||||
|
||||
NZCV nzcv; // 8 bytes (high 4 are unused).
|
||||
FPCR fpcr; // 8 bytes (high 4 are unused).
|
||||
FPSR fpsr; // 8 bytes (high 4 are unused).
|
||||
|
||||
SR sr; // 40 bytes.
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert((1088 + 16) == sizeof(State),
|
||||
static_assert((1120 + 16) == sizeof(State),
|
||||
"Invalid packing of `struct State`");
|
||||
|
||||
using AArch64State = State;
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#endif // REMILL_ARCH_AARCH64_RUNTIME_STATE_H_
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
/*
|
||||
* Types.h
|
||||
* Copyright (c) 2017 Trail of Bits, Inc.
|
||||
*
|
||||
* Created on: May 9, 2017
|
||||
* Author: akshayk
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef REMILL_ARCH_AARCH64_RUNTIME_TYPES_H_
|
||||
@@ -14,7 +23,7 @@ typedef RnW<uint8_t> R8W;
|
||||
typedef RnW<uint8_t> R8W;
|
||||
typedef RnW<uint16_t> R16W;
|
||||
|
||||
// Note: ARM zero-extends like x86, but the smallest register size that
|
||||
// Note: AArch64 zero-extends like x86, but the smallest register size that
|
||||
// can be accessed is 32 bits.
|
||||
typedef RnW<uint64_t> R32W;
|
||||
typedef RnW<uint64_t> R64W;
|
||||
@@ -57,4 +66,4 @@ typedef In<uint64_t> I64;
|
||||
typedef In<addr_t> PC;
|
||||
typedef In<addr_t> ADDR;
|
||||
|
||||
#endif /* REMILL_ARCH_AARCH64_RUNTIME_TYPES_H_ */
|
||||
#endif // REMILL_ARCH_AARCH64_RUNTIME_TYPES_H_
|
||||
|
||||
@@ -42,23 +42,16 @@ DEF_SEM(EOR_REG, D dst, S1 src1, S2 src2) {
|
||||
|
||||
} // namespace
|
||||
|
||||
// DEF_ISEL() = SUB<M32W, M32, R32>;
|
||||
|
||||
DEF_ISEL(SUB_R64W_R64_R64) = SUB<R64W, R64, R64>;
|
||||
DEF_ISEL(SUB_R64W_R64_U) = SUB<R64W, R64, I64>;
|
||||
|
||||
DEF_ISEL(ADD_R64W_R64_R64) = ADD<R64W, R64, R64>;
|
||||
DEF_ISEL(ADD_R64W_R64_U) = ADD<R64W, R64, I64>;
|
||||
|
||||
DEF_ISEL(ADD_32_ADDSUB_IMM) = ADD<R32W, R32, I32>;
|
||||
DEF_ISEL(ADD_64_ADDSUB_IMM) = ADD<R64W, R64, I64>;
|
||||
|
||||
DEF_ISEL(ADD_32_ADDSUB_SHIFT) = ADD<R32W, R32, R32>;
|
||||
DEF_ISEL(ADD_64_ADDSUB_SHIFT) = ADD<R64W, R64, R64>;
|
||||
|
||||
DEF_ISEL(ASR_R64W_R64_U) = ASR<R64W, R64, I64>;
|
||||
|
||||
DEF_ISEL(EOR_R64W_R64_R64) = EOR_REG<R64W, R64, R64>;
|
||||
|
||||
DEF_ISEL(SUB_32_ADDSUB_SHIFT) = SUB<R32W, R32, R32>;
|
||||
DEF_ISEL(SUB_64_ADDSUB_SHIFT) = SUB<R64W, R64, R64>;
|
||||
|
||||
DEF_ISEL(EOR_32_LOG_SHIFT) = EOR_REG<R32W, R32, R32>;
|
||||
DEF_ISEL(EOR_64_LOG_SHIFT) = EOR_REG<R64W, R64, R64>;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -18,72 +18,72 @@ namespace {
|
||||
|
||||
// when '101' result = (PSTATE.N == PSTATE.V); // GE or LT
|
||||
static inline bool CondGE(const State &state) {
|
||||
return state.pstate.N == state.pstate.V;
|
||||
return FLAG_N == FLAG_V;
|
||||
}
|
||||
|
||||
// when '101' result = (PSTATE.N == PSTATE.V); // GE or LT
|
||||
static inline bool CondLT(const State &state) {
|
||||
return state.pstate.N != state.pstate.V;
|
||||
return FLAG_N != FLAG_V;
|
||||
}
|
||||
|
||||
// when '000' result = (PSTATE.Z == '1'); // EQ or NE
|
||||
static inline bool CondEQ(const State &state) {
|
||||
return state.pstate.Z;
|
||||
return FLAG_Z;
|
||||
}
|
||||
|
||||
// when '000' result = (PSTATE.Z == '1'); // EQ or NE
|
||||
static inline bool CondNE(const State &state) {
|
||||
return !state.pstate.Z;
|
||||
return !FLAG_Z;
|
||||
}
|
||||
|
||||
// when '110' result = (PSTATE.N == PSTATE.V && PSTATE.Z == '0'); // GT or LE
|
||||
static inline bool CondGT(const State &state) {
|
||||
return (state.pstate.N == state.pstate.V) && !state.pstate.Z;
|
||||
return (FLAG_N == FLAG_V) && !FLAG_Z;
|
||||
}
|
||||
|
||||
// when '110' result = (PSTATE.N == PSTATE.V && PSTATE.Z == '0'); // GT or LE
|
||||
static inline bool CondLE(const State &state) {
|
||||
return (state.pstate.N != state.pstate.V) || state.pstate.Z;
|
||||
return (FLAG_N != FLAG_V) || FLAG_Z;
|
||||
}
|
||||
|
||||
// when '001' result = (PSTATE.C == '1'); // CS or CC
|
||||
static inline bool CondCS(const State &state) {
|
||||
return state.pstate.C;
|
||||
return FLAG_C;
|
||||
}
|
||||
|
||||
// when '001' result = (PSTATE.C == '1'); // CS or CC
|
||||
static inline bool CondCC(const State &state) {
|
||||
return !state.pstate.C;
|
||||
return !FLAG_C;
|
||||
}
|
||||
|
||||
// when '010' result = (PSTATE.N == '1'); // MI or PL
|
||||
static inline bool CondMI(const State &state) {
|
||||
return state.pstate.N;
|
||||
return FLAG_N;
|
||||
}
|
||||
|
||||
// when '010' result = (PSTATE.N == '1'); // MI or PL
|
||||
static inline bool CondPL(const State &state) {
|
||||
return !state.pstate.N;
|
||||
return !FLAG_N;
|
||||
}
|
||||
|
||||
// when '011' result = (PSTATE.V == '1'); // VS or VC
|
||||
static inline bool CondVS(const State &state) {
|
||||
return state.pstate.V;
|
||||
return FLAG_V;
|
||||
}
|
||||
|
||||
// when '011' result = (PSTATE.V == '1'); // VS or VC
|
||||
static inline bool CondVC(const State &state) {
|
||||
return !state.pstate.V;
|
||||
return !FLAG_V;
|
||||
}
|
||||
|
||||
// when '100' result = (PSTATE.C == '1' && PSTATE.Z == '0'); // HI or LS
|
||||
static inline bool CondHI(const State &state) {
|
||||
return state.pstate.C && !state.pstate.Z;
|
||||
return FLAG_C && !FLAG_Z;
|
||||
}
|
||||
|
||||
// when '100' result = (PSTATE.C == '1' && PSTATE.Z == '0'); // HI or LS
|
||||
static inline bool CondLS(const State &state) {
|
||||
return !state.pstate.C || state.pstate.Z;
|
||||
return !FLAG_C || FLAG_Z;
|
||||
}
|
||||
|
||||
static inline bool CondAL(const State &state) {
|
||||
|
||||
@@ -75,6 +75,7 @@ DEF_ISEL(STR_64_LDST_IMMPOST) = StoreUpdateIndex<R64, M64W>;
|
||||
|
||||
DEF_ISEL(STR_32_LDST_POS) = Store<R32, M32W>;
|
||||
DEF_ISEL(STR_64_LDST_POS) = Store<R64, M64W>;
|
||||
|
||||
DEF_ISEL(STRB_32_LDST_POS) = Store<R8, M8W>;
|
||||
DEF_ISEL(STRH_32_LDST_POS) = Store<R16, M16W>;
|
||||
|
||||
@@ -184,13 +185,14 @@ DEF_ISEL(MOV_ADD_64_ADDSUB_IMM) = Load<R64W, R64>;
|
||||
DEF_ISEL(MOV_ORR_32_LOG_SHIFT) = Load<R32W, R32>;
|
||||
DEF_ISEL(MOV_ORR_64_LOG_SHIFT) = Load<R64W, R64>;
|
||||
|
||||
DEF_ISEL(MOV_ORR_32_LOG_IMM) = Load<R32W, I32>;
|
||||
DEF_ISEL(MOV_ORR_64_LOG_IMM) = Load<R64W, I64>;
|
||||
|
||||
DEF_ISEL(MOV_MOVZ_32_MOVEWIDE) = Load<R32W, I32>;
|
||||
DEF_ISEL(MOV_MOVZ_64_MOVEWIDE) = Load<R64W, I64>;
|
||||
|
||||
DEF_ISEL(MOV_MOVN_32_MOVEWIDE) = Load<R32W, I32>;
|
||||
|
||||
DEF_ISEL(MOV_ORR_32_LOG_IMM) = Load<R32W, I32>;
|
||||
DEF_ISEL(MOV_ORR_64_LOG_IMM) = Load<R64W, I64>;
|
||||
DEF_ISEL(MOV_MOVN_64_MOVEWIDE) = Load<R64W, I64>;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -215,19 +217,22 @@ DEF_ISEL(MOVN_64_MOVEWIDE) = Load<R64W, I64>;
|
||||
|
||||
namespace {
|
||||
|
||||
DEF_SEM(LoadAddress64, R64W dst, PC label) {
|
||||
addr_t label_addr = Read(label);
|
||||
DEF_SEM(ADRP, R64W dst, PC label) {
|
||||
addr_t label_addr = Read(label);
|
||||
|
||||
// clear the bottom 12 bits of label_addr
|
||||
// to make this page aligned
|
||||
// the Post decoding already made the label page aligned
|
||||
// and added the label to PC
|
||||
// the semantics just needs to fix up for PC not being page aligned
|
||||
auto label_page = UAnd(UNot(static_cast<uint64_t>(4095)), label_addr);
|
||||
Write(dst, label_page);
|
||||
return memory;
|
||||
// clear the bottom 12 bits of label_addr
|
||||
// to make this page aligned
|
||||
// the Post decoding already made the label page aligned
|
||||
// and added the label to PC
|
||||
// the semantics just needs to fix up for PC not being page aligned
|
||||
auto label_page = UAnd(UNot(static_cast<uint64_t>(4095)), label_addr);
|
||||
Write(dst, label_page);
|
||||
return memory;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(ADRP_ONLY_PCRELADDR) = ADRP;
|
||||
|
||||
DEF_ISEL(ADR_ONLY_PCRELADDR) = Load<R64W, I64>;
|
||||
|
||||
DEF_ISEL(ADRP_ONLY_PCRELADDR) = LoadAddress64;
|
||||
|
||||
+14
-2
@@ -173,8 +173,20 @@ const Arch *Arch::GetMips(OSName, ArchName) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Arch *GetGlobalArch(void) {
|
||||
return Arch::Get(GetOSName(FLAGS_os), GetArchName(FLAGS_arch));
|
||||
const Arch *GetHostArch(void) {
|
||||
static const Arch *gHostArch = nullptr;
|
||||
if (!gHostArch) {
|
||||
gHostArch = Arch::Get(GetOSName(REMILL_OS), GetArchName(REMILL_ARCH));
|
||||
}
|
||||
return gHostArch;
|
||||
}
|
||||
|
||||
const Arch *GetTargetArch(void) {
|
||||
static const Arch *gTargetArch = nullptr;
|
||||
if (!gTargetArch) {
|
||||
gTargetArch = Arch::Get(GetOSName(FLAGS_os), GetArchName(FLAGS_arch));
|
||||
}
|
||||
return gTargetArch;
|
||||
}
|
||||
|
||||
bool Arch::IsX86(void) const {
|
||||
|
||||
+7
-1
@@ -75,7 +75,13 @@ class Arch {
|
||||
Arch(void) = delete;
|
||||
};
|
||||
|
||||
const Arch *GetGlobalArch(void);
|
||||
// Get the (approximate) architecture of the running system. This may not
|
||||
// include all feature sets.
|
||||
const Arch *GetHostArch(void);
|
||||
|
||||
// Get the architecture of the modelled code. This is based on command-line
|
||||
// flags.
|
||||
const Arch *GetTargetArch(void);
|
||||
|
||||
} // namespace remill
|
||||
|
||||
|
||||
@@ -17,6 +17,18 @@
|
||||
#ifndef REMILL_ARCH_NAME_H_
|
||||
#define REMILL_ARCH_NAME_H_
|
||||
|
||||
#ifndef REMILL_ARCH
|
||||
# if defined(__x86_64__)
|
||||
# define REMILL_ARCH "amd64"
|
||||
# elif defined(__i386__) || defined(_M_X86)
|
||||
# define REMILL_ARCH "x86"
|
||||
# elif defined(__aarch64__)
|
||||
# define REMILL_ARCH "aarch64"
|
||||
# else
|
||||
# error "Cannot infer current architecture."
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace remill {
|
||||
|
||||
@@ -31,12 +31,14 @@
|
||||
# define _IF_32BIT(...)
|
||||
# define _IF_64BIT(...) , __VA_ARGS__
|
||||
# define IF_64BIT_ELSE(a, b) a
|
||||
# define aword qword
|
||||
#else
|
||||
# define IF_32BIT(...) __VA_ARGS__
|
||||
# define IF_64BIT(...)
|
||||
# define _IF_32BIT(...) , __VA_ARGS__
|
||||
# define _IF_64BIT(...)
|
||||
# define IF_64BIT_ELSE(a, b) b
|
||||
# define aword dword
|
||||
#endif
|
||||
|
||||
// Attributes that will force inlining of specific code.
|
||||
|
||||
@@ -31,7 +31,7 @@ class SyncHyperCall {
|
||||
kAMD64EmulateInstruction,
|
||||
kMipsEmulateInstruction,
|
||||
|
||||
// TODO(pag): How to distinguish litte- and big-endian?
|
||||
// TODO(pag): How to distinguish little- and big-endian?
|
||||
kAArch64EmulateInstruction,
|
||||
|
||||
kAssertPrivileged,
|
||||
|
||||
+12
-11
@@ -43,21 +43,19 @@ typedef uint64_t addr64_t;
|
||||
typedef IF_64BIT_ELSE(addr64_t, addr32_t) addr_t;
|
||||
typedef IF_64BIT_ELSE(int64_t, int32_t) addr_diff_t;
|
||||
|
||||
#if COMPILING_WITH_GCC && !defined(__x86_64__)
|
||||
struct uint128_t {
|
||||
uint8_t elems[16];
|
||||
};
|
||||
struct int128_t {
|
||||
int8_t elems[16];
|
||||
};
|
||||
#else
|
||||
#if defined(__x86_64__) || defined(__i386__) || defined(_M_X86)
|
||||
typedef unsigned uint128_t __attribute__((mode(TI)));
|
||||
static_assert(16 == sizeof(uint128_t), "Invalid `uint128_t` size.");
|
||||
|
||||
typedef int int128_t __attribute__((mode(TI)));
|
||||
static_assert(16 == sizeof(int128_t), "Invalid `int128_t` size.");
|
||||
#elif defined(__aarch64__)
|
||||
typedef __uint128_t uint128_t;
|
||||
typedef __int128_t int128_t;
|
||||
#else
|
||||
# error "Cannot determine (u)int128_t type of unuspported architecture."
|
||||
#endif
|
||||
|
||||
static_assert(16 == sizeof(uint128_t), "Invalid `uint128_t` size.");
|
||||
static_assert(16 == sizeof(int128_t), "Invalid `int128_t` size.");
|
||||
|
||||
typedef float float32_t;
|
||||
static_assert(4 == sizeof(float32_t), "Invalid `float32_t` size.");
|
||||
|
||||
@@ -454,6 +452,9 @@ struct RVnW<vec64_t> final {
|
||||
uint64_t * const val_ref;
|
||||
};
|
||||
|
||||
// A `void` pointer is used so that we can treat different vector types
|
||||
// uniformly (from the LLVM bitcode side). That is, the type of value passed
|
||||
// in may be a pointer to a wider vector than was is specified by `T`.
|
||||
template <typename T>
|
||||
struct Vn final {
|
||||
const void * const val;
|
||||
|
||||
@@ -23,21 +23,14 @@ set(X86RUNTIME_SOURCEFILES
|
||||
set_source_files_properties(Instructions.cpp PROPERTIES COMPILE_FLAGS "-O3 -g0")
|
||||
set_source_files_properties(BasicBlock.cpp PROPERTIES COMPILE_FLAGS "-O0 -g3")
|
||||
|
||||
set(X86RUNTIME_COMPILEOPTIONS
|
||||
-mno-sse
|
||||
-mno-avx
|
||||
-mno-3dnow
|
||||
)
|
||||
|
||||
set(X86RUNTIME_INCLUDEDIRECTORIES ${CMAKE_SOURCE_DIR})
|
||||
|
||||
function (add_runtime_helper target_name address_bit_size enable_avx enable_avx512)
|
||||
message(" > Generating runtime target: ${target_name}")
|
||||
|
||||
add_runtime(${target_name} ARCH x86_64 SOURCES ${X86RUNTIME_SOURCEFILES} ADDRESS_SIZE ${address_bit_size})
|
||||
add_runtime(${target_name} SOURCES ${X86RUNTIME_SOURCEFILES} ADDRESS_SIZE ${address_bit_size})
|
||||
|
||||
target_include_directories(${target_name} PRIVATE ${X86RUNTIME_INCLUDEDIRECTORIES})
|
||||
target_compile_options(${target_name} PRIVATE ${X86RUNTIME_COMPILEOPTIONS})
|
||||
target_compile_definitions(${target_name} PRIVATE "HAS_FEATURE_AVX=${enable_avx}")
|
||||
target_compile_definitions(${target_name} PRIVATE "HAS_FEATURE_AVX512=${enable_avx512}")
|
||||
|
||||
|
||||
@@ -47,23 +47,21 @@
|
||||
#endif
|
||||
|
||||
#if HAS_FEATURE_AVX
|
||||
#define IF_AVX(...) __VA_ARGS__
|
||||
#define IF_AVX_ELSE(a, b) a
|
||||
# define IF_AVX(...) __VA_ARGS__
|
||||
# define IF_AVX_ELSE(a, b) a
|
||||
#else
|
||||
#define IF_AVX(...)
|
||||
#define IF_AVX_ELSE(a, b) b
|
||||
# define IF_AVX(...)
|
||||
# define IF_AVX_ELSE(a, b) b
|
||||
#endif
|
||||
|
||||
#if HAS_FEATURE_AVX && HAS_FEATURE_AVX512
|
||||
#define IF_AVX512(...) __VA_ARGS__
|
||||
#define IF_AVX512_ELSE(a, b) a
|
||||
# define IF_AVX512(...) __VA_ARGS__
|
||||
# define IF_AVX512_ELSE(a, b) a
|
||||
#else
|
||||
#define IF_AVX512(...)
|
||||
#define IF_AVX512_ELSE(a, b) b
|
||||
# define IF_AVX512(...)
|
||||
# define IF_AVX512_ELSE(a, b) b
|
||||
#endif
|
||||
|
||||
#define aword IF_64BIT_ELSE(qword, dword)
|
||||
|
||||
union FPUStatusWord final {
|
||||
uint16_t flat;
|
||||
struct {
|
||||
@@ -515,6 +513,8 @@ struct alignas(16) State final : public ArchState {
|
||||
static_assert((2672 + 16) == sizeof(State),
|
||||
"Invalid packing of `struct State`");
|
||||
|
||||
using X86State = State;
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#endif // REMILL_ARCH_X86_RUNTIME_STATE_H_
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace {
|
||||
|
||||
template <typename D>
|
||||
DEF_SEM(SETNLE, D dst) {
|
||||
Write(dst, BAnd(BNot(FLAG_ZF), BXnor(FLAG_CF, FLAG_PF)));
|
||||
Write(dst, BAnd(BNot(FLAG_ZF), BXnor(FLAG_SF, FLAG_OF)));
|
||||
return memory;
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ DEF_SEM(SETNL, D dst) {
|
||||
|
||||
template <typename D>
|
||||
DEF_SEM(SETNBE, D dst) {
|
||||
Write(dst, BNot(BOr(FLAG_DF, FLAG_ZF)));
|
||||
Write(dst, BAnd(BNot(FLAG_CF), BNot(FLAG_ZF)));
|
||||
return memory;
|
||||
}
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ ALWAYS_INLINE static bool CompareFloats(FloatCompareOperator op, T v1, T v2) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef issignaling
|
||||
#if !defined(issignaling)
|
||||
|
||||
union nan32_t {
|
||||
float32_t f;
|
||||
@@ -173,7 +173,7 @@ ALWAYS_INLINE bool issignaling(float64_t x) {
|
||||
return !x_nan.is_quiet_nan;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif // !defined(issignaling)
|
||||
|
||||
template <typename S1, typename S2>
|
||||
DEF_SEM(COMISS, S1 src1, S2 src2) {
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace remill {
|
||||
|
||||
class IntrinsicTable {
|
||||
public:
|
||||
IntrinsicTable(llvm::Module *M);
|
||||
explicit IntrinsicTable(llvm::Module *module);
|
||||
|
||||
llvm::Function * const error;
|
||||
|
||||
|
||||
@@ -364,6 +364,22 @@ llvm::Value *InstructionLifter::LiftRegisterOperand(
|
||||
}
|
||||
return val;
|
||||
|
||||
// LLVM on AArch64 converts things like `RnW<uint64_t>`, which is a struct
|
||||
// containing a `uint64_t *`, into a `uintptr_t` when they are being passed
|
||||
// as arguments.
|
||||
} else if (Operand::kActionWrite == op.action) {
|
||||
CHECK(GetHostArch()->IsAArch64() || GetTargetArch()->IsAArch64())
|
||||
<< "Operand " << op.Debug() << " is a write operand, but argument "
|
||||
<< " type " << LLVMThingToString(arg_type) << " is not a pointer type "
|
||||
<< std::hex << inst.pc;
|
||||
|
||||
CHECK(arg_type == word_type)
|
||||
<< LLVMThingToString(arg_type) << " must be a pointer-sized integer "
|
||||
<< " in order to store the address of the register.";
|
||||
|
||||
auto val = LoadRegAddress(block, arch_reg.name);
|
||||
return new llvm::PtrToIntInst(val, arg_type, "", block);
|
||||
|
||||
} else {
|
||||
CHECK(arg_type->isIntegerTy() || arg_type->isFloatingPointTy())
|
||||
<< "Expected " << arch_reg.name << " to be an integral or float type "
|
||||
|
||||
+26
-23
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
#include <glog/logging.h>
|
||||
|
||||
#include <sstream>
|
||||
@@ -51,6 +52,8 @@
|
||||
#include "remill/BC/Version.h"
|
||||
#include "remill/OS/FileSystem.h"
|
||||
|
||||
DECLARE_string(arch);
|
||||
|
||||
namespace remill {
|
||||
|
||||
// Initialize the attributes for a lifted function.
|
||||
@@ -279,38 +282,37 @@ void StoreModuleToFile(llvm::Module *module, std::string file_name) {
|
||||
|
||||
namespace {
|
||||
|
||||
#ifndef BUILD_SEMANTICS_DIR_X86
|
||||
#error "Macro `BUILD_SEMANTICS_DIR_X86` must be defined."
|
||||
#define BUILD_SEMANTICS_DIR_X86
|
||||
#endif // BUILD_SEMANTICS_DIR_X86
|
||||
#ifndef REMILL_BUILD_SEMANTICS_DIR_X86
|
||||
#error "Macro `REMILL_BUILD_SEMANTICS_DIR_X86` must be defined."
|
||||
#define REMILL_BUILD_SEMANTICS_DIR_X86
|
||||
#endif // REMILL_BUILD_SEMANTICS_DIR_X86
|
||||
|
||||
#ifndef BUILD_SEMANTICS_DIR_AARCH64
|
||||
#error \
|
||||
"Macro `BUILD_SEMANTICS_DIR_AARCH64` must be defined to support AArch64 architecture."
|
||||
#define BUILD_SEMANTICS_DIR_AARCH64
|
||||
#endif // BUILD_SEMANTICS_DIR_AARCH64
|
||||
#ifndef REMILL_BUILD_SEMANTICS_DIR_AARCH64
|
||||
#error "Macro `REMILL_BUILD_SEMANTICS_DIR_AARCH64` must be defined to support AArch64 architecture."
|
||||
#define REMILL_BUILD_SEMANTICS_DIR_AARCH64
|
||||
#endif // REMILL_BUILD_SEMANTICS_DIR_AARCH64
|
||||
|
||||
#ifndef INSTALL_SEMANTICS_DIR
|
||||
#error "Macro `INSTALL_SEMANTICS_DIR` must be defined."
|
||||
#define INSTALL_SEMANTICS_DIR
|
||||
#endif // INSTALL_SEMANTICS_DIR
|
||||
#ifndef REMILL_INSTALL_SEMANTICS_DIR
|
||||
#error "Macro `REMILL_INSTALL_SEMANTICS_DIR` must be defined."
|
||||
#define REMILL_INSTALL_SEMANTICS_DIR
|
||||
#endif // REMILL_INSTALL_SEMANTICS_DIR
|
||||
|
||||
static const char *gSemanticsSearchPaths[] = {
|
||||
// Derived from the build.
|
||||
BUILD_SEMANTICS_DIR_X86 "\0",
|
||||
BUILD_SEMANTICS_DIR_AARCH64 "\0",
|
||||
INSTALL_SEMANTICS_DIR "\0",
|
||||
REMILL_BUILD_SEMANTICS_DIR_X86 "\0",
|
||||
REMILL_BUILD_SEMANTICS_DIR_AARCH64 "\0",
|
||||
REMILL_INSTALL_SEMANTICS_DIR "\0",
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// Find the path to the semantics bitcode file.
|
||||
std::string FindSemanticsBitcodeFile(const std::string &path,
|
||||
const std::string &arch) {
|
||||
if (!path.empty()) {
|
||||
return path;
|
||||
}
|
||||
// Find the path to the semantics bitcode file associated with `FLAGS_arch`.
|
||||
std::string FindSemanticsBitcodeFile(void) {
|
||||
return FindSemanticsBitcodeFile(FLAGS_arch);
|
||||
}
|
||||
|
||||
// Find the path to the semantics bitcode file.
|
||||
std::string FindSemanticsBitcodeFile(const std::string &arch) {
|
||||
for (auto sem_dir : gSemanticsSearchPaths) {
|
||||
std::stringstream ss;
|
||||
ss << sem_dir << "/" << arch << ".bc";
|
||||
@@ -320,7 +322,8 @@ std::string FindSemanticsBitcodeFile(const std::string &path,
|
||||
}
|
||||
}
|
||||
|
||||
LOG(FATAL) << "Cannot find path to " << arch << " semantics bitcode file.";
|
||||
LOG(FATAL)
|
||||
<< "Cannot find path to " << arch << " semantics bitcode file.";
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -98,8 +98,8 @@ llvm::Module *LoadModuleFromFile(llvm::LLVMContext *context,
|
||||
void StoreModuleToFile(llvm::Module *module, std::string file_name);
|
||||
|
||||
// Find the path to the semantics bitcode file.
|
||||
std::string FindSemanticsBitcodeFile(const std::string &path,
|
||||
const std::string &arch);
|
||||
std::string FindSemanticsBitcodeFile(void);
|
||||
std::string FindSemanticsBitcodeFile(const std::string &arch);
|
||||
|
||||
// Return a pointer to the Nth argument (N=0 is the first argument).
|
||||
llvm::Argument *NthArgument(llvm::Function *func, size_t index);
|
||||
|
||||
+1
-14
@@ -19,20 +19,7 @@
|
||||
|
||||
#include "remill/OS/OS.h"
|
||||
|
||||
#ifndef REMILL_OS
|
||||
# if defined(__APPLE__)
|
||||
# define REMILL_OS "mac"
|
||||
# elif defined(__linux__)
|
||||
# define REMILL_OS "linux"
|
||||
# elif defined(_WIN32)
|
||||
# define REMILL_OS "windows"
|
||||
# else
|
||||
# define REMILL_OS ""
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
DEFINE_string(os, REMILL_OS, "Source OS. Valid OSes: linux, mac, windows.");
|
||||
DEFINE_string(os, REMILL_OS, "Source OS. Valid OSes: linux, macos, windows.");
|
||||
|
||||
namespace remill {
|
||||
|
||||
|
||||
@@ -19,6 +19,18 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifndef REMILL_OS
|
||||
# if defined(__APPLE__)
|
||||
# define REMILL_OS "macos"
|
||||
# elif defined(__linux__)
|
||||
# define REMILL_OS "linux"
|
||||
# elif defined(WIN32)
|
||||
# define REMILL_OS "windows"
|
||||
# else
|
||||
# error "Cannot infer current OS."
|
||||
# endif
|
||||
#endif
|
||||
|
||||
namespace remill {
|
||||
|
||||
enum OSName : uint32_t {
|
||||
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) 2017 Trail of Bits, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This script is a convenience script for generating some assembly code that
|
||||
# is a template for saving the machine state to a `State` structure.
|
||||
|
||||
DIR=$(dirname $(dirname $( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )))
|
||||
|
||||
CXX=$(which c++)
|
||||
|
||||
mkdir -p $DIR/generated/Arch/AArch64
|
||||
|
||||
pushd /tmp
|
||||
|
||||
${CXX} \
|
||||
-std=gnu++11 \
|
||||
-Wno-nested-anon-types -Wno-variadic-macros -Wno-extended-offsetof \
|
||||
-Wno-invalid-offsetof \
|
||||
-Wno-return-type-c-linkage \
|
||||
-I${DIR} \
|
||||
-DADDRESS_SIZE_BITS=64 \
|
||||
$DIR/tests/AArch64/PrintSaveState.cpp
|
||||
|
||||
./a.out > $DIR/generated/Arch/AArch64/SaveState.S
|
||||
|
||||
|
||||
${CXX} \
|
||||
-std=gnu++11 \
|
||||
-Wno-nested-anon-types -Wno-variadic-macros -Wno-extended-offsetof \
|
||||
-Wno-invalid-offsetof \
|
||||
-Wno-return-type-c-linkage \
|
||||
-I${DIR} \
|
||||
-DADDRESS_SIZE_BITS=64 \
|
||||
$DIR/tests/AArch64/PrintRestoreState.cpp
|
||||
|
||||
./a.out > $DIR/generated/Arch/AArch64/RestoreState.S
|
||||
|
||||
rm ./a.out
|
||||
popd
|
||||
+3
-11
@@ -44,7 +44,7 @@ function GetUbuntuOSVersion
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
printf "[x] Ubuntu ${DISTRIB_CODENAME} is not supported.\n"
|
||||
printf "[x] Ubuntu ${DISTRIB_CODENAME} is not supported. Only xenial (16.04) and trusty (14.04) are supported.\n"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
@@ -65,7 +65,7 @@ function GetArchVersion
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
printf "[x] ${version} architecture is not supported.\n"
|
||||
printf "[x] ${version} architecture is not supported. Only aarch64 and x86_64 (i.e. amd64) are supported.\n"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
@@ -182,14 +182,6 @@ function Build
|
||||
function GetLLVMVersion
|
||||
{
|
||||
case ${1} in
|
||||
3.6)
|
||||
LLVM_VERSION=llvm36
|
||||
return 0
|
||||
;;
|
||||
3.7)
|
||||
LLVM_VERSION=llvm37
|
||||
return 0
|
||||
;;
|
||||
3.8)
|
||||
LLVM_VERSION=llvm38
|
||||
return 0
|
||||
@@ -203,7 +195,7 @@ function GetLLVMVersion
|
||||
;;
|
||||
*)
|
||||
# unknown option
|
||||
printf "[x] Unknown LLVM version ${1}\n"
|
||||
printf "[x] Unknown LLVM version ${1}. Valid versions are 3.8, 3.9, and 4.0.\n"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
DIR=$(dirname $(dirname $( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )))
|
||||
|
||||
CXX=$(which clang++)
|
||||
CXX=$(which c++)
|
||||
|
||||
pushd /tmp
|
||||
${CXX} \
|
||||
@@ -29,7 +29,9 @@ ${CXX} \
|
||||
-m64 -I${DIR} \
|
||||
-DADDRESS_SIZE_BITS=64 -DHAS_FEATURE_AVX=1 -DHAS_FEATURE_AVX512=1 \
|
||||
$DIR/tests/X86/PrintSaveState.cpp
|
||||
|
||||
|
||||
mkdir -p $DIR/generated/Arch/X86/
|
||||
|
||||
./a.out > $DIR/generated/Arch/X86/SaveState.S
|
||||
rm ./a.out
|
||||
popd
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Trail of Bits, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* ADD <Wd>, <Wn>, <Wm>{, <shift> #<amount>} */
|
||||
TEST_BEGIN(ADD_32_ADDSUB_SHIFT, add_w0_w0_w1, 2)
|
||||
TEST_INPUTS(
|
||||
0, 0,
|
||||
0, 1,
|
||||
1, 0,
|
||||
1, 1,
|
||||
0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0, 0xFFFFFFFF,
|
||||
0xfafbfbfd, 0xf1f2f3f4,
|
||||
0x0a0b0c0d, 0x01020304)
|
||||
|
||||
add w0, w0, w1
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(ADD_32_ADDSUB_SHIFT, add_w0_w0_w1_lsl5, 2)
|
||||
TEST_INPUTS(
|
||||
0, 0,
|
||||
0, 1,
|
||||
1, 0,
|
||||
1, 1,
|
||||
0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0, 0xFFFFFFFF,
|
||||
0xfafbfbfd, 0xf1f2f3f4,
|
||||
0x0a0b0c0d, 0x01020304)
|
||||
|
||||
add w0, w0, w1, lsl 5
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(ADD_32_ADDSUB_SHIFT, add_w0_w0_w1_lsr5, 2)
|
||||
TEST_INPUTS(
|
||||
0, 0,
|
||||
0, 1,
|
||||
1, 0,
|
||||
1, 1,
|
||||
0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0, 0xFFFFFFFF,
|
||||
0xfafbfbfd, 0xf1f2f3f4,
|
||||
0x0a0b0c0d, 0x01020304)
|
||||
|
||||
add w0, w0, w1, lsr 5
|
||||
TEST_END
|
||||
|
||||
/* Note: ROR is not allowed here */
|
||||
|
||||
TEST_BEGIN(ADD_32_ADDSUB_SHIFT, add_w0_w0_w1_asr5, 2)
|
||||
TEST_INPUTS(
|
||||
0, 0,
|
||||
0, 1,
|
||||
1, 0,
|
||||
1, 1,
|
||||
0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0, 0xFFFFFFFF,
|
||||
0xfafbfbfd, 0xf1f2f3f4,
|
||||
0x0a0b0c0d, 0x01020304)
|
||||
|
||||
add w0, w0, w1, asr 5
|
||||
TEST_END
|
||||
|
||||
/* ADD <Xd>, <Xn>, <Xm>{, <shift> #<amount>} */
|
||||
TEST_BEGIN(ADD_64_ADDSUB_SHIFT, add_x0_x0_x1, 2)
|
||||
TEST_INPUTS(
|
||||
0, 0,
|
||||
0, 1,
|
||||
1, 0,
|
||||
1, 1,
|
||||
0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0, 0xFFFFFFFF,
|
||||
0xfafbfbfd, 0xf1f2f3f4,
|
||||
0x0a0b0c0d, 0x01020304,
|
||||
0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFF00000000, 0xFFFFFFFF,
|
||||
0xFFFFFFFF, 0xFFFFFFFF00000000,
|
||||
0xFFFFFFFFFFFFFFFF, 0,
|
||||
0, 0xFFFFFFFFFFFFFFFF,
|
||||
0xfafbfbfdf1f2f3f4, 0xf1f2f3f4fafbfbfd,
|
||||
0x0a0b0c0d01020304, 0x010203040a0b0c0d)
|
||||
|
||||
add x0, x0, x1
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(ADD_64_ADDSUB_SHIFT, add_x0_x0_x1_lsl5, 2)
|
||||
TEST_INPUTS(
|
||||
0, 0,
|
||||
0, 1,
|
||||
1, 0,
|
||||
1, 1,
|
||||
0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0, 0xFFFFFFFF,
|
||||
0xfafbfbfd, 0xf1f2f3f4,
|
||||
0x0a0b0c0d, 0x01020304,
|
||||
0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFF00000000, 0xFFFFFFFF,
|
||||
0xFFFFFFFF, 0xFFFFFFFF00000000,
|
||||
0xFFFFFFFFFFFFFFFF, 0,
|
||||
0, 0xFFFFFFFFFFFFFFFF,
|
||||
0xfafbfbfdf1f2f3f4, 0xf1f2f3f4fafbfbfd,
|
||||
0x0a0b0c0d01020304, 0x010203040a0b0c0d)
|
||||
|
||||
add x0, x0, x1, lsl 5
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(ADD_64_ADDSUB_SHIFT, add_x0_x0_x1_lsr5, 2)
|
||||
TEST_INPUTS(
|
||||
0, 0,
|
||||
0, 1,
|
||||
1, 0,
|
||||
1, 1,
|
||||
0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0, 0xFFFFFFFF,
|
||||
0xfafbfbfd, 0xf1f2f3f4,
|
||||
0x0a0b0c0d, 0x01020304)
|
||||
|
||||
add x0, x0, x1, lsr 5
|
||||
TEST_END
|
||||
|
||||
/* Note: ROR is not allowed here */
|
||||
|
||||
TEST_BEGIN(ADD_64_ADDSUB_SHIFT, add_x0_x0_x1_asr5, 2)
|
||||
TEST_INPUTS(
|
||||
0, 0,
|
||||
0, 1,
|
||||
1, 0,
|
||||
1, 1,
|
||||
0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0, 0xFFFFFFFF,
|
||||
0xfafbfbfd, 0xf1f2f3f4,
|
||||
0x0a0b0c0d, 0x01020304,
|
||||
0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFF00000000, 0xFFFFFFFF,
|
||||
0xFFFFFFFF, 0xFFFFFFFF00000000,
|
||||
0xFFFFFFFFFFFFFFFF, 0,
|
||||
0, 0xFFFFFFFFFFFFFFFF,
|
||||
0xfafbfbfdf1f2f3f4, 0xf1f2f3f4fafbfbfd,
|
||||
0x0a0b0c0d01020304, 0x010203040a0b0c0d)
|
||||
|
||||
add x0, x0, x1, asr 5
|
||||
TEST_END
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Trail of Bits, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* SUB <Wd>, <Wn>, <Wm>{, <shift> #<amount>} */
|
||||
TEST_BEGIN(SUB_32_ADDSUB_SHIFT, sub_w0_w0_w1, 2)
|
||||
TEST_INPUTS(
|
||||
0, 0,
|
||||
0, 1,
|
||||
1, 0,
|
||||
1, 1,
|
||||
0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0, 0xFFFFFFFF,
|
||||
0xfafbfbfd, 0xf1f2f3f4,
|
||||
0x0a0b0c0d, 0x01020304)
|
||||
|
||||
sub w0, w0, w1
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(SUB_32_ADDSUB_SHIFT, sub_w0_w0_w1_lsl5, 2)
|
||||
TEST_INPUTS(
|
||||
0, 0,
|
||||
0, 1,
|
||||
1, 0,
|
||||
1, 1,
|
||||
0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0, 0xFFFFFFFF,
|
||||
0xfafbfbfd, 0xf1f2f3f4,
|
||||
0x0a0b0c0d, 0x01020304)
|
||||
|
||||
sub w0, w0, w1, lsl 5
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(SUB_32_ADDSUB_SHIFT, sub_w0_w0_w1_lsr5, 2)
|
||||
TEST_INPUTS(
|
||||
0, 0,
|
||||
0, 1,
|
||||
1, 0,
|
||||
1, 1,
|
||||
0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0, 0xFFFFFFFF,
|
||||
0xfafbfbfd, 0xf1f2f3f4,
|
||||
0x0a0b0c0d, 0x01020304)
|
||||
|
||||
sub w0, w0, w1, lsr 5
|
||||
TEST_END
|
||||
|
||||
/* Note: ROR is not allowed here */
|
||||
|
||||
TEST_BEGIN(SUB_32_ADDSUB_SHIFT, sub_w0_w0_w1_asr5, 2)
|
||||
TEST_INPUTS(
|
||||
0, 0,
|
||||
0, 1,
|
||||
1, 0,
|
||||
1, 1,
|
||||
0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0, 0xFFFFFFFF,
|
||||
0xfafbfbfd, 0xf1f2f3f4,
|
||||
0x0a0b0c0d, 0x01020304)
|
||||
|
||||
sub w0, w0, w1, asr 5
|
||||
TEST_END
|
||||
|
||||
/* SUB <Xd>, <Xn>, <Xm>{, <shift> #<amount>} */
|
||||
TEST_BEGIN(SUB_64_ADDSUB_SHIFT, sub_x0_x0_x1, 2)
|
||||
TEST_INPUTS(
|
||||
0, 0,
|
||||
0, 1,
|
||||
1, 0,
|
||||
1, 1,
|
||||
0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0, 0xFFFFFFFF,
|
||||
0xfafbfbfd, 0xf1f2f3f4,
|
||||
0x0a0b0c0d, 0x01020304,
|
||||
0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFF00000000, 0xFFFFFFFF,
|
||||
0xFFFFFFFF, 0xFFFFFFFF00000000,
|
||||
0xFFFFFFFFFFFFFFFF, 0,
|
||||
0, 0xFFFFFFFFFFFFFFFF,
|
||||
0xfafbfbfdf1f2f3f4, 0xf1f2f3f4fafbfbfd,
|
||||
0x0a0b0c0d01020304, 0x010203040a0b0c0d)
|
||||
|
||||
sub x0, x0, x1
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(SUB_64_ADDSUB_SHIFT, sub_x0_x0_x1_lsl5, 2)
|
||||
TEST_INPUTS(
|
||||
0, 0,
|
||||
0, 1,
|
||||
1, 0,
|
||||
1, 1,
|
||||
0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0, 0xFFFFFFFF,
|
||||
0xfafbfbfd, 0xf1f2f3f4,
|
||||
0x0a0b0c0d, 0x01020304,
|
||||
0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFF00000000, 0xFFFFFFFF,
|
||||
0xFFFFFFFF, 0xFFFFFFFF00000000,
|
||||
0xFFFFFFFFFFFFFFFF, 0,
|
||||
0, 0xFFFFFFFFFFFFFFFF,
|
||||
0xfafbfbfdf1f2f3f4, 0xf1f2f3f4fafbfbfd,
|
||||
0x0a0b0c0d01020304, 0x010203040a0b0c0d)
|
||||
|
||||
sub x0, x0, x1, lsl 5
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(SUB_64_ADDSUB_SHIFT, sub_x0_x0_x1_lsr5, 2)
|
||||
TEST_INPUTS(
|
||||
0, 0,
|
||||
0, 1,
|
||||
1, 0,
|
||||
1, 1,
|
||||
0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0, 0xFFFFFFFF,
|
||||
0xfafbfbfd, 0xf1f2f3f4,
|
||||
0x0a0b0c0d, 0x01020304)
|
||||
|
||||
sub x0, x0, x1, lsr 5
|
||||
TEST_END
|
||||
|
||||
/* Note: ROR is not allowed here */
|
||||
|
||||
TEST_BEGIN(SUB_64_ADDSUB_SHIFT, sub_x0_x0_x1_asr5, 2)
|
||||
TEST_INPUTS(
|
||||
0, 0,
|
||||
0, 1,
|
||||
1, 0,
|
||||
1, 1,
|
||||
0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0, 0xFFFFFFFF,
|
||||
0xfafbfbfd, 0xf1f2f3f4,
|
||||
0x0a0b0c0d, 0x01020304,
|
||||
0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFF00000000, 0xFFFFFFFF,
|
||||
0xFFFFFFFF, 0xFFFFFFFF00000000,
|
||||
0xFFFFFFFFFFFFFFFF, 0,
|
||||
0, 0xFFFFFFFFFFFFFFFF,
|
||||
0xfafbfbfdf1f2f3f4, 0xf1f2f3f4fafbfbfd,
|
||||
0x0a0b0c0d01020304, 0x010203040a0b0c0d)
|
||||
|
||||
sub x0, x0, x1, asr 5
|
||||
TEST_END
|
||||
@@ -0,0 +1,84 @@
|
||||
# Copyright (c) 2017 Trail of Bits, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
find_package(gtest REQUIRED)
|
||||
|
||||
# google gtest
|
||||
find_package(gtest REQUIRED)
|
||||
list(APPEND PROJECT_LIBRARIES ${gtest_LIBRARIES})
|
||||
list(APPEND PROJECT_INCLUDEDIRECTORIES ${gtest_INCLUDE_DIRS})
|
||||
|
||||
enable_testing()
|
||||
enable_language(BC)
|
||||
enable_language(ASM)
|
||||
|
||||
add_custom_target(build_aarch64_tests)
|
||||
|
||||
add_executable(lift-aarch64-tests
|
||||
EXCLUDE_FROM_ALL
|
||||
Lift.cpp
|
||||
Tests.S
|
||||
)
|
||||
|
||||
target_compile_options(lift-aarch64-tests
|
||||
PRIVATE ${X86_TEST_FLAGS}
|
||||
-DIN_TEST_GENERATOR
|
||||
)
|
||||
|
||||
target_link_libraries(lift-aarch64-tests PUBLIC remill ${PROJECT_LIBRARIES})
|
||||
target_include_directories(lift-aarch64-tests PUBLIC ${PROJECT_INCLUDEDIRECTORIES})
|
||||
target_compile_definitions(lift-aarch64-tests PUBLIC ${PROJECT_DEFINITIONS})
|
||||
|
||||
add_executable(run-aarch64-tests
|
||||
EXCLUDE_FROM_ALL
|
||||
Run.cpp
|
||||
Tests.S
|
||||
tests_aarch64.S
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT tests_aarch64.bc
|
||||
COMMAND lift-aarch64-tests
|
||||
--arch aarch64
|
||||
--bc_out tests_aarch64.bc
|
||||
DEPENDS lift-aarch64-tests semantics
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT tests_aarch64.S
|
||||
COMMAND ${CMAKE_BC_COMPILER}
|
||||
-Wno-override-module
|
||||
-S -O3 -g0
|
||||
-c tests_aarch64.bc
|
||||
-o tests_aarch64.S
|
||||
DEPENDS tests_aarch64.bc
|
||||
)
|
||||
|
||||
target_link_libraries(run-aarch64-tests PUBLIC remill ${PROJECT_LIBRARIES})
|
||||
target_include_directories(run-aarch64-tests PUBLIC ${PROJECT_INCLUDEDIRECTORIES})
|
||||
target_compile_definitions(run-aarch64-tests PUBLIC ${PROJECT_DEFINITIONS})
|
||||
|
||||
target_compile_options(run-aarch64-tests
|
||||
PRIVATE -I${CMAKE_SOURCE_DIR}
|
||||
-DADDRESS_SIZE_BITS=64
|
||||
-DGTEST_HAS_RTTI=0
|
||||
-DGTEST_HAS_TR1_TUPLE=0
|
||||
)
|
||||
|
||||
add_dependencies(build_aarch64_tests
|
||||
lift-aarch64-tests
|
||||
run-aarch64-tests
|
||||
)
|
||||
|
||||
add_test(aarch64 run-aarch64-tests)
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Trail of Bits, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
#include <glog/logging.h>
|
||||
|
||||
#include <llvm/IR/Function.h>
|
||||
#include <llvm/IR/GlobalValue.h>
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
#include <llvm/IR/Type.h>
|
||||
|
||||
#include "remill/Arch/Arch.h"
|
||||
#include "remill/Arch/Instruction.h"
|
||||
#include "remill/Arch/Name.h"
|
||||
#include "remill/BC/IntrinsicTable.h"
|
||||
#include "remill/BC/Lifter.h"
|
||||
#include "remill/BC/Util.h"
|
||||
#include "remill/OS/OS.h"
|
||||
|
||||
#include "tests/AArch64/Test.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
# define SYMBOL_PREFIX "_"
|
||||
#else
|
||||
# define SYMBOL_PREFIX ""
|
||||
#endif
|
||||
|
||||
DEFINE_string(bc_out, "",
|
||||
"Name of the file in which to place the generated bitcode.");
|
||||
|
||||
DECLARE_string(arch);
|
||||
DECLARE_string(os);
|
||||
|
||||
extern "C" {
|
||||
int gNativeState [[gnu::used]] = 0;
|
||||
int gLiftedState [[gnu::used]] = 0;
|
||||
} // extern
|
||||
|
||||
namespace {
|
||||
|
||||
// Decode a test and add it as a basic block to the module.
|
||||
//
|
||||
// TODO(pag): Eventually handle control-flow.
|
||||
static void AddFunctionToModule(llvm::Module *module,
|
||||
const remill::Arch *arch,
|
||||
const test::TestInfo &test) {
|
||||
DLOG(INFO)
|
||||
<< "Adding block for: " << test.test_name;
|
||||
|
||||
std::stringstream ss;
|
||||
ss << SYMBOL_PREFIX << test.test_name << "_lifted";
|
||||
|
||||
auto word_type = llvm::Type::getIntNTy(module->getContext(),
|
||||
arch->address_size);
|
||||
auto func = remill::DeclareLiftedFunction(module, ss.str());
|
||||
remill::CloneBlockFunctionInto(func);
|
||||
|
||||
func->setLinkage(llvm::GlobalValue::ExternalLinkage);
|
||||
func->setVisibility(llvm::GlobalValue::DefaultVisibility);
|
||||
|
||||
remill::IntrinsicTable intrinsics(module);
|
||||
remill::InstructionLifter lifter(word_type, &intrinsics);
|
||||
|
||||
auto saw_isel = false;
|
||||
|
||||
auto block = &(func->front());
|
||||
auto addr = test.test_begin;
|
||||
while (addr < test.test_end) {
|
||||
std::string inst_bytes;
|
||||
auto bytes = reinterpret_cast<const char *>(addr);
|
||||
inst_bytes.insert(inst_bytes.end(), bytes, bytes + 4);
|
||||
|
||||
remill::Instruction inst;
|
||||
CHECK(arch->DecodeInstruction(addr, inst_bytes, inst))
|
||||
<< "Can't decode test instruction in " << test.test_name;
|
||||
|
||||
LOG(INFO)
|
||||
<< "Lifting " << inst.Serialize();
|
||||
|
||||
CHECK(lifter.LiftIntoBlock(inst, block))
|
||||
<< "Can't lift test instruction in " << test.test_name;
|
||||
|
||||
saw_isel = saw_isel || inst.function == test.isel_name;
|
||||
|
||||
addr += inst.NumBytes();
|
||||
}
|
||||
|
||||
CHECK(saw_isel)
|
||||
<< "Test " << test.test_name << " does not have an instruction that "
|
||||
<< "uses the semantics function " << test.isel_name;
|
||||
|
||||
remill::AddTerminatingTailCall(block, intrinsics.missing_block);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" int main(int argc, char *argv[]) {
|
||||
|
||||
google::ParseCommandLineFlags(&argc, &argv, true);
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
|
||||
auto os = remill::GetOSName(REMILL_OS);
|
||||
auto arch = remill::Arch::Get(os, remill::kArchAArch64LittleEndian);
|
||||
|
||||
DLOG(INFO) << "Generating tests.";
|
||||
|
||||
auto context = new llvm::LLVMContext;
|
||||
auto bc_file = remill::FindSemanticsBitcodeFile(FLAGS_arch);
|
||||
auto module = remill::LoadModuleFromFile(context, bc_file);
|
||||
remill::GetHostArch()->PrepareModule(module);
|
||||
|
||||
for (auto i = 0U; ; ++i) {
|
||||
const auto &test = test::__aarch64_test_table_begin[i];
|
||||
if (&test >= &(test::__aarch64_test_table_end[0])) break;
|
||||
AddFunctionToModule(module, arch, test);
|
||||
}
|
||||
|
||||
DLOG(INFO) << "Serializing bitcode to " << FLAGS_bc_out;
|
||||
remill::StoreModuleToFile(module, FLAGS_bc_out);
|
||||
|
||||
DLOG(INFO) << "Done.";
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Trail of Bits, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* dildributed under the License is dildributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
|
||||
#define ADDRESS_SIZE_BITS 64
|
||||
|
||||
#include "remill/Arch/AArch64/Runtime/State.h"
|
||||
|
||||
int main(void) {
|
||||
|
||||
printf("/* Auto-generated file! Don't modify! */\n\n");
|
||||
|
||||
// Build up the `nzcv` bits from the `State` structure.
|
||||
printf("mov x2, #0\n");
|
||||
|
||||
printf("ldrb w1, [x28, #%lu]\n", offsetof(State, sr.n));
|
||||
printf("orr x2, x2, x1, LSL 31\n");
|
||||
|
||||
printf("ldrb w1, [x28, #%lu]\n", offsetof(State, sr.z));
|
||||
printf("orr x2, x2, x1, LSL 30\n");
|
||||
|
||||
printf("ldrb w1, [x28, #%lu]\n", offsetof(State, sr.c));
|
||||
printf("orr x2, x2, x1, LSL 29\n");
|
||||
|
||||
printf("ldrb w1, [x28, #%lu]\n", offsetof(State, sr.v));
|
||||
printf("orr x2, x2, x1, LSL 28\n");
|
||||
|
||||
// Sync `nzcv` between native and lifted.
|
||||
printf("str x2, [x28, #%lu]\n", offsetof(State, nzcv));
|
||||
printf("msr nzcv, x2\n");
|
||||
|
||||
// Floating point condition register.
|
||||
printf("ldr x1, [x28, #%lu]\n", offsetof(State, fpcr));
|
||||
printf("msr fpcr, x1\n");
|
||||
|
||||
// Floating point status register.
|
||||
printf("ldr x1, [x28, #%lu]\n", offsetof(State, fpsr));
|
||||
printf("msr fpsr, x1\n");
|
||||
|
||||
// User-space thread pointer register.
|
||||
printf("ldr x1, [x28, #%lu]\n", offsetof(State, sr.tpidr_el0));
|
||||
printf("msr tpidr_el0, x1\n");
|
||||
|
||||
// Secondary user space thread pointer register is read-only.
|
||||
|
||||
// General purpose regs (except x28, which contains State *).
|
||||
printf("ldr x0, [x28, #%lu]\n", offsetof(State, gpr.x0));
|
||||
printf("ldr x1, [x28, #%lu]\n", offsetof(State, gpr.x1));
|
||||
printf("ldr x2, [x28, #%lu]\n", offsetof(State, gpr.x2));
|
||||
printf("ldr x3, [x28, #%lu]\n", offsetof(State, gpr.x3));
|
||||
printf("ldr x4, [x28, #%lu]\n", offsetof(State, gpr.x4));
|
||||
printf("ldr x5, [x28, #%lu]\n", offsetof(State, gpr.x5));
|
||||
printf("ldr x6, [x28, #%lu]\n", offsetof(State, gpr.x6));
|
||||
printf("ldr x7, [x28, #%lu]\n", offsetof(State, gpr.x7));
|
||||
printf("ldr x8, [x28, #%lu]\n", offsetof(State, gpr.x8));
|
||||
printf("ldr x9, [x28, #%lu]\n", offsetof(State, gpr.x9));
|
||||
printf("ldr x10, [x28, #%lu]\n", offsetof(State, gpr.x10));
|
||||
printf("ldr x11, [x28, #%lu]\n", offsetof(State, gpr.x11));
|
||||
printf("ldr x12, [x28, #%lu]\n", offsetof(State, gpr.x12));
|
||||
printf("ldr x13, [x28, #%lu]\n", offsetof(State, gpr.x13));
|
||||
printf("ldr x14, [x28, #%lu]\n", offsetof(State, gpr.x14));
|
||||
printf("ldr x15, [x28, #%lu]\n", offsetof(State, gpr.x15));
|
||||
printf("ldr x16, [x28, #%lu]\n", offsetof(State, gpr.x16));
|
||||
printf("ldr x17, [x28, #%lu]\n", offsetof(State, gpr.x17));
|
||||
printf("ldr x18, [x28, #%lu]\n", offsetof(State, gpr.x18));
|
||||
printf("ldr x19, [x28, #%lu]\n", offsetof(State, gpr.x19));
|
||||
printf("ldr x20, [x28, #%lu]\n", offsetof(State, gpr.x20));
|
||||
printf("ldr x21, [x28, #%lu]\n", offsetof(State, gpr.x21));
|
||||
printf("ldr x22, [x28, #%lu]\n", offsetof(State, gpr.x22));
|
||||
printf("ldr x23, [x28, #%lu]\n", offsetof(State, gpr.x23));
|
||||
printf("ldr x24, [x28, #%lu]\n", offsetof(State, gpr.x24));
|
||||
printf("ldr x25, [x28, #%lu]\n", offsetof(State, gpr.x25));
|
||||
printf("ldr x26, [x28, #%lu]\n", offsetof(State, gpr.x26));
|
||||
printf("ldr x27, [x28, #%lu]\n", offsetof(State, gpr.x27));
|
||||
printf("ldr x30, [x28, #%lu]\n", offsetof(State, gpr.x30));
|
||||
|
||||
// Restore the stack pointer.
|
||||
printf("ldr x29, [x28, #%lu]\n", offsetof(State, gpr.sp));
|
||||
printf("mov sp, x29\n");
|
||||
|
||||
printf("ldr x29, [x28, #%lu]\n", offsetof(State, gpr.x29));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Trail of Bits, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
|
||||
#define ADDRESS_SIZE_BITS 64
|
||||
|
||||
#include "remill/Arch/AArch64/Runtime/State.h"
|
||||
|
||||
int main(void) {
|
||||
|
||||
printf("/* Auto-generated file! Don't modify! */\n\n");
|
||||
|
||||
// X30 - State *
|
||||
|
||||
// General purpose regs (except x28, which contains State *).
|
||||
printf("str x0, [x28, #%lu]\n", offsetof(State, gpr.x0));
|
||||
printf("str x1, [x28, #%lu]\n", offsetof(State, gpr.x1));
|
||||
printf("str x2, [x28, #%lu]\n", offsetof(State, gpr.x2));
|
||||
printf("str x3, [x28, #%lu]\n", offsetof(State, gpr.x3));
|
||||
printf("str x4, [x28, #%lu]\n", offsetof(State, gpr.x4));
|
||||
printf("str x5, [x28, #%lu]\n", offsetof(State, gpr.x5));
|
||||
printf("str x6, [x28, #%lu]\n", offsetof(State, gpr.x6));
|
||||
printf("str x7, [x28, #%lu]\n", offsetof(State, gpr.x7));
|
||||
printf("str x8, [x28, #%lu]\n", offsetof(State, gpr.x8));
|
||||
printf("str x9, [x28, #%lu]\n", offsetof(State, gpr.x9));
|
||||
printf("str x10, [x28, #%lu]\n", offsetof(State, gpr.x10));
|
||||
printf("str x11, [x28, #%lu]\n", offsetof(State, gpr.x11));
|
||||
printf("str x12, [x28, #%lu]\n", offsetof(State, gpr.x12));
|
||||
printf("str x13, [x28, #%lu]\n", offsetof(State, gpr.x13));
|
||||
printf("str x14, [x28, #%lu]\n", offsetof(State, gpr.x14));
|
||||
printf("str x15, [x28, #%lu]\n", offsetof(State, gpr.x15));
|
||||
printf("str x16, [x28, #%lu]\n", offsetof(State, gpr.x16));
|
||||
printf("str x17, [x28, #%lu]\n", offsetof(State, gpr.x17));
|
||||
printf("str x18, [x28, #%lu]\n", offsetof(State, gpr.x18));
|
||||
printf("str x19, [x28, #%lu]\n", offsetof(State, gpr.x19));
|
||||
printf("str x20, [x28, #%lu]\n", offsetof(State, gpr.x20));
|
||||
printf("str x21, [x28, #%lu]\n", offsetof(State, gpr.x21));
|
||||
printf("str x22, [x28, #%lu]\n", offsetof(State, gpr.x22));
|
||||
printf("str x23, [x28, #%lu]\n", offsetof(State, gpr.x23));
|
||||
printf("str x24, [x28, #%lu]\n", offsetof(State, gpr.x24));
|
||||
printf("str x25, [x28, #%lu]\n", offsetof(State, gpr.x25));
|
||||
printf("str x26, [x28, #%lu]\n", offsetof(State, gpr.x26));
|
||||
printf("str x27, [x28, #%lu]\n", offsetof(State, gpr.x27));
|
||||
printf("str x29, [x28, #%lu]\n", offsetof(State, gpr.x29));
|
||||
printf("str x30, [x28, #%lu]\n", offsetof(State, gpr.x30));
|
||||
|
||||
// Save the stack pointer.
|
||||
printf("mov x29, sp\n");
|
||||
printf("str x29, [x28, #%lu]\n", offsetof(State, gpr.sp));
|
||||
|
||||
printf("mov x29, #1\n");
|
||||
|
||||
// Save the N flag.
|
||||
printf("strb w29, [x28, #%lu]\n", offsetof(State, sr.n));
|
||||
printf("b.mi 1f\n");
|
||||
printf("strb wzr, [x28, #%lu]\n", offsetof(State, sr.n));
|
||||
printf("1:\n");
|
||||
|
||||
// Save the Z flag.
|
||||
printf("strb w29, [x28, #%lu]\n", offsetof(State, sr.z));
|
||||
printf("b.eq 1f\n");
|
||||
printf("strb wzr, [x28, #%lu]\n", offsetof(State, sr.z));
|
||||
printf("1:\n");
|
||||
|
||||
// Save the C flag.
|
||||
printf("strb w29, [x28, #%lu]\n", offsetof(State, sr.c));
|
||||
printf("b.cs 1f\n");
|
||||
printf("strb wzr, [x28, #%lu]\n", offsetof(State, sr.c));
|
||||
printf("1:\n");
|
||||
|
||||
// Save the V flag.
|
||||
printf("strb w29, [x28, #%lu]\n", offsetof(State, sr.v));
|
||||
printf("b.vs 1f\n");
|
||||
printf("strb wzr, [x28, #%lu]\n", offsetof(State, sr.v));
|
||||
printf("1:\n");
|
||||
|
||||
// Restore x29.
|
||||
printf("ldr x29, [x28, #%lu]\n", offsetof(State, gpr.x29));
|
||||
|
||||
// Save the real version of the nzvc reg.
|
||||
printf("mrs x1, nzcv\n");
|
||||
printf("str x1, [x28, #%lu]\n", offsetof(State, nzcv));
|
||||
|
||||
// Floating point condition register.
|
||||
printf("mrs x1, fpcr\n");
|
||||
printf("str x1, [x28, #%lu]\n", offsetof(State, fpcr));
|
||||
|
||||
// Floating point status register.
|
||||
printf("mrs x1, fpsr\n");
|
||||
printf("str x1, [x28, #%lu]\n", offsetof(State, fpsr));
|
||||
|
||||
// User-space thread pointer register.
|
||||
printf("mrs x1, tpidr_el0\n");
|
||||
printf("str x1, [x28, #%lu]\n", offsetof(State, sr.tpidr_el0));
|
||||
|
||||
// Secondary user space thread pointer register that is read-only from
|
||||
// user space.
|
||||
printf("mrs x1, tpidrro_el0\n");
|
||||
printf("str x1, [x28, #%lu]\n", offsetof(State, sr.tpidrro_el0));
|
||||
|
||||
// Restore stolen `x1`.
|
||||
printf("ldr x1, [x28, #%lu]\n", offsetof(State, gpr.x1));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,542 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Trail of Bits, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define _XOPEN_SOURCE
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <dlfcn.h>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
#include <glog/logging.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <signal.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
#include "tests/AArch64/Test.h"
|
||||
|
||||
#include "remill/Arch/Runtime/Runtime.h"
|
||||
#include "remill/Arch/AArch64/Runtime/State.h"
|
||||
|
||||
DECLARE_string(arch);
|
||||
DECLARE_string(os);
|
||||
|
||||
namespace {
|
||||
|
||||
struct alignas(128) Stack {
|
||||
uint8_t _redzone1[128];
|
||||
uint8_t bytes[(SIGSTKSZ / 128) * 128];
|
||||
uint8_t _redzone2[128];
|
||||
};
|
||||
|
||||
// Native test case code executes off of `gStack`. The state of the stack
|
||||
// after executing this code is saved in `gBackupStack`. Lifted test case
|
||||
// code executes off of the normal runtime stack, but emulates operations
|
||||
// that act on `gStack`.
|
||||
static Stack gRandomStack;
|
||||
static Stack gLiftedStack;
|
||||
static Stack gNativeStack;
|
||||
static Stack gSigStack;
|
||||
|
||||
static const auto gStackBase = reinterpret_cast<uintptr_t>(
|
||||
&(gLiftedStack.bytes[0]));
|
||||
|
||||
static const auto gStackLimit = reinterpret_cast<uintptr_t>(
|
||||
&(gLiftedStack._redzone2[0]));
|
||||
|
||||
template <typename T>
|
||||
NEVER_INLINE static T &AccessMemory(addr_t addr) {
|
||||
if (!(addr >= gStackBase && (addr + sizeof(T)) <= gStackLimit)) {
|
||||
EXPECT_TRUE(!"Memory access falls outside the valid range of the stack.");
|
||||
}
|
||||
return *reinterpret_cast<T *>(static_cast<uintptr_t>(addr));
|
||||
}
|
||||
|
||||
// Used to handle exceptions in instructions.
|
||||
static sigjmp_buf gJmpBuf;
|
||||
static sigjmp_buf gUnsupportedInstrBuf;
|
||||
|
||||
// Are we running in a native test case or a lifted one?
|
||||
static bool gInNativeTest = false;
|
||||
|
||||
extern "C" {
|
||||
|
||||
|
||||
// Native state before we run the native test case. We then use this as the
|
||||
// initial state for the lifted testcase. The lifted test case code mutates
|
||||
// this, and we require that after running the lifted testcase, `gAArch64StateBefore`
|
||||
// matches `gAArch64StateAfter`,
|
||||
std::aligned_storage<sizeof(AArch64State), alignof(AArch64State)>::type gLiftedState;
|
||||
|
||||
// Native state after running the native test case.
|
||||
std::aligned_storage<sizeof(AArch64State), alignof(AArch64State)>::type gNativeState;
|
||||
|
||||
// Address of the native test to run. The `InvokeTestCase` function saves
|
||||
// the native program state but then needs a way to figure out where to go
|
||||
// without storing that information in any register. So what we do is we
|
||||
// store it here and indirectly `JMP` into the native test case code after
|
||||
// saving the machine state to `gAArch64StateBefore`.
|
||||
uintptr_t gTestToRun = 0;
|
||||
|
||||
// Used for swapping the stack pointer between `gStack` and the normal
|
||||
// call stack. This lets us run both native and lifted testcase code on
|
||||
// the same stack.
|
||||
uint8_t *gStackSwitcher = nullptr;
|
||||
|
||||
uint64_t gStackSaveSlots[2] = {0, 0};
|
||||
|
||||
// Invoke a native test case addressed by `gTestToRun` and store the machine
|
||||
// state before and after executing the test in `gAArch64StateBefore` and
|
||||
// `gAArch64StateAfter`, respectively.
|
||||
extern void InvokeTestCase(uint64_t, uint64_t, uint64_t);
|
||||
|
||||
#define MAKE_RW_MEMORY(size) \
|
||||
NEVER_INLINE uint ## size ## _t __remill_read_memory_ ## size( \
|
||||
Memory *, addr_t addr) {\
|
||||
return AccessMemory<uint ## size ## _t>(addr); \
|
||||
} \
|
||||
NEVER_INLINE Memory *__remill_write_memory_ ## size( \
|
||||
Memory *, addr_t addr, const uint ## size ## _t in) { \
|
||||
AccessMemory<uint ## size ## _t>(addr) = in; \
|
||||
return nullptr; \
|
||||
}
|
||||
|
||||
#define MAKE_RW_FP_MEMORY(size) \
|
||||
NEVER_INLINE float ## size ## _t __remill_read_memory_f ## size( \
|
||||
Memory *, addr_t addr) { \
|
||||
return AccessMemory<float ## size ## _t>(addr); \
|
||||
} \
|
||||
NEVER_INLINE Memory *__remill_write_memory_f ## size(\
|
||||
Memory *, addr_t addr, float ## size ## _t in) { \
|
||||
AccessMemory<float ## size ## _t>(addr) = in; \
|
||||
return nullptr; \
|
||||
}
|
||||
|
||||
MAKE_RW_MEMORY(8)
|
||||
MAKE_RW_MEMORY(16)
|
||||
MAKE_RW_MEMORY(32)
|
||||
MAKE_RW_MEMORY(64)
|
||||
|
||||
MAKE_RW_FP_MEMORY(32)
|
||||
MAKE_RW_FP_MEMORY(64)
|
||||
|
||||
NEVER_INLINE float64_t __remill_read_memory_f80(Memory *, addr_t) {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
NEVER_INLINE Memory *__remill_write_memory_f80(Memory *, addr_t, float64_t) {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
Memory *__remill_barrier_load_load(Memory *) { return nullptr; }
|
||||
Memory *__remill_barrier_load_store(Memory *) { return nullptr; }
|
||||
Memory *__remill_barrier_store_load(Memory *) { return nullptr; }
|
||||
Memory *__remill_barrier_store_store(Memory *) { return nullptr; }
|
||||
Memory *__remill_atomic_begin(Memory *) { return nullptr; }
|
||||
Memory *__remill_atomic_end(Memory *) { return nullptr; }
|
||||
|
||||
void __remill_defer_inlining(void) {}
|
||||
|
||||
Memory *__remill_error(addr_t, AArch64State &, Memory *) {
|
||||
siglongjmp(gJmpBuf, 0);
|
||||
}
|
||||
|
||||
Memory *__remill_missing_block(addr_t, AArch64State &, Memory *memory) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
Memory *__remill_sync_hyper_call(Memory *, AArch64State &, SyncHyperCall::Name) {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
Memory *__remill_function_call(addr_t, AArch64State &, Memory *) {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
Memory *__remill_function_return(addr_t, AArch64State &, Memory *) {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
Memory *__remill_jump(addr_t, AArch64State &, Memory *) {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
Memory *__remill_async_hyper_call(addr_t, AArch64State &, Memory *) {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
uint8_t __remill_undefined_8(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t __remill_undefined_16(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t __remill_undefined_32(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t __remill_undefined_64(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
float32_t __remill_undefined_f32(void) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
float64_t __remill_undefined_f64(void) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Marks `mem` as being used. This is used for making sure certain symbols are
|
||||
// kept around through optimization, and makes sure that optimization doesn't
|
||||
// perform dead-argument elimination on any of the intrinsics.
|
||||
void __remill_mark_as_used(void *mem) {
|
||||
asm("" :: "m"(mem));
|
||||
}
|
||||
|
||||
} // extern C
|
||||
|
||||
typedef Memory *(LiftedFunc)(addr_t, AArch64State &, Memory *);
|
||||
|
||||
// Mapping of test name to translated function.
|
||||
static std::map<uint64_t, LiftedFunc *> gTranslatedFuncs;
|
||||
|
||||
static std::vector<const test::TestInfo *> gTests;
|
||||
} // namespace
|
||||
|
||||
class InstrTest : public ::testing::TestWithParam<const test::TestInfo *> {};
|
||||
|
||||
template <typename T>
|
||||
inline static bool operator==(const T &a, const T &b) {
|
||||
return !memcmp(&a, &b, sizeof(a));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline static bool operator!=(const T &a, const T &b) {
|
||||
return !!memcmp(&a, &b, sizeof(a));
|
||||
}
|
||||
|
||||
static void RunWithFlags(const test::TestInfo *info,
|
||||
NZCV flags,
|
||||
std::string desc,
|
||||
uint64_t arg1,
|
||||
uint64_t arg2,
|
||||
uint64_t arg3) {
|
||||
|
||||
DLOG(INFO) << "Testing instruction: " << info->test_name << ": " << desc;
|
||||
if (sigsetjmp(gUnsupportedInstrBuf, true)) {
|
||||
DLOG(INFO) << "Unsupported instruction " << info->test_name;
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&gLiftedStack, &gRandomStack, sizeof(gLiftedStack));
|
||||
memset(&gLiftedState, 0, sizeof(gLiftedState));
|
||||
memset(&gNativeState, 0, sizeof(gNativeState));
|
||||
|
||||
auto lifted_state = reinterpret_cast<AArch64State *>(&gLiftedState);
|
||||
auto native_state = reinterpret_cast<AArch64State *>(&gNativeState);
|
||||
|
||||
// Set up the run's info.
|
||||
gTestToRun = info->test_begin;
|
||||
gStackSwitcher = &(gLiftedStack._redzone2[0]);
|
||||
|
||||
// This will execute on `gStack`. The mechanism behind this is that the
|
||||
// stack pointer is swapped with `gStackSwitcher`. The idea here is that
|
||||
// we want to run the native and lifted testcases on the same stack so that
|
||||
// we can compare that they both operate on the stack in the same ways.
|
||||
auto native_test_faulted = false;
|
||||
if (!sigsetjmp(gJmpBuf, true)) {
|
||||
gInNativeTest = true;
|
||||
asm("msr nzcv, %0" : : "r"(flags));
|
||||
InvokeTestCase(arg1, arg2, arg3);
|
||||
} else {
|
||||
native_test_faulted = true;
|
||||
}
|
||||
|
||||
// Copy out whatever was recorded on the stack so that we can compare it
|
||||
// with how the lifted program mutates the stack.
|
||||
memcpy(&gNativeStack, &gLiftedStack, sizeof(gLiftedStack));
|
||||
memcpy(&gLiftedStack, &gRandomStack, sizeof(gLiftedStack));
|
||||
|
||||
auto lifted_func = gTranslatedFuncs[info->test_begin];
|
||||
|
||||
// Includes the additional injected `adrp` and `add`.
|
||||
lifted_state->gpr.pc.aword = static_cast<addr_t>(info->test_begin + 4 + 4);
|
||||
|
||||
// This will execute on our stack but the lifted code will operate on
|
||||
// `gLiftedStack`. The mechanism behind this is that `gLiftedState` is the
|
||||
// native program state recorded before executing the native testcase,
|
||||
// but after swapping execution to operate on `gStack`.
|
||||
if (!sigsetjmp(gJmpBuf, true)) {
|
||||
gInNativeTest = false;
|
||||
(void) lifted_func(
|
||||
lifted_state->gpr.pc.aword,
|
||||
*lifted_state,
|
||||
nullptr);
|
||||
} else {
|
||||
EXPECT_TRUE(native_test_faulted);
|
||||
}
|
||||
|
||||
// The native test doesn't update
|
||||
native_state->gpr.pc.qword = info->test_end;
|
||||
|
||||
// Used in the test cases to hold the `State *`.
|
||||
lifted_state->gpr.x28.qword = 0;
|
||||
native_state->gpr.x28.qword = 0;
|
||||
|
||||
// Link pointer register (i.e. return address).
|
||||
lifted_state->gpr.x30.qword = 0;
|
||||
native_state->gpr.x30.qword = 0;
|
||||
|
||||
native_state->interrupt_vector = 0;
|
||||
lifted_state->interrupt_vector = 0;
|
||||
|
||||
native_state->hyper_call = AsyncHyperCall::kInvalid;
|
||||
lifted_state->hyper_call = AsyncHyperCall::kInvalid;
|
||||
|
||||
EXPECT_TRUE(lifted_state->sr.n == native_state->sr.n);
|
||||
EXPECT_TRUE(lifted_state->sr.z == native_state->sr.z);
|
||||
EXPECT_TRUE(lifted_state->sr.c == native_state->sr.c);
|
||||
EXPECT_TRUE(lifted_state->sr.v == native_state->sr.v);
|
||||
EXPECT_TRUE(lifted_state->gpr == native_state->gpr);
|
||||
|
||||
if (gLiftedState != gNativeState) {
|
||||
LOG(ERROR)
|
||||
<< "States did not match for " << desc;
|
||||
EXPECT_TRUE(!"Lifted and native states did not match.");
|
||||
}
|
||||
|
||||
if (gLiftedStack != gNativeStack) {
|
||||
LOG(ERROR)
|
||||
<< "Stacks did not match for " << desc;
|
||||
|
||||
for (size_t i = 0; i < sizeof(gLiftedStack.bytes); ++i) {
|
||||
if (gLiftedStack.bytes[i] != gNativeStack.bytes[i]) {
|
||||
LOG(ERROR)
|
||||
<< "Lifted stack at 0x" << std::hex
|
||||
<< reinterpret_cast<uintptr_t>(&(gLiftedStack.bytes[i]))
|
||||
<< " does not match native stack at 0x" << std::hex
|
||||
<< reinterpret_cast<uintptr_t>(&(gNativeStack.bytes[i]))
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_TRUE(!"Lifted and native stacks did not match.");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(InstrTest, SemanticsMatchNative) {
|
||||
auto info = GetParam();
|
||||
for (auto args = info->args_begin;
|
||||
args < info->args_end;
|
||||
args += info->num_args) {
|
||||
std::stringstream ss;
|
||||
if (1 <= info->num_args) {
|
||||
ss << "args: 0x" << std::hex << args[0];
|
||||
if (2 <= info->num_args) {
|
||||
ss << ", 0x" << std::hex << args[1];
|
||||
if (3 <= info->num_args) {
|
||||
ss << ", 0x" << std::hex << args[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
auto desc = ss.str();
|
||||
for (uint32_t i = 0; i <= 0xFU; ++i) {
|
||||
NZCV flags;
|
||||
flags.flat = i << 28;
|
||||
|
||||
std::stringstream ss2;
|
||||
ss2 << desc << " with N=" << flags.n << " Z=" << flags.z << " C="
|
||||
<< flags.c << " V=" << flags.v;
|
||||
|
||||
RunWithFlags(info, flags, ss2.str(), args[0], args[1], args[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
GeneralInstrTest,
|
||||
InstrTest,
|
||||
testing::ValuesIn(gTests));
|
||||
|
||||
// Recover from a signal.
|
||||
static void RecoverFromError(int sig_num, siginfo_t *, void *context_) {
|
||||
if (gInNativeTest) {
|
||||
memcpy(&gNativeState, &gLiftedState, sizeof(AArch64State));
|
||||
|
||||
auto context = reinterpret_cast<ucontext_t *>(context_);
|
||||
auto native_state = reinterpret_cast<AArch64State *>(&gNativeState);
|
||||
auto &gpr = native_state->gpr;
|
||||
#ifdef __APPLE__
|
||||
// const auto mcontext = context->uc_mcontext;
|
||||
// const auto &ss = mcontext->__ss;
|
||||
|
||||
(void) context;
|
||||
(void) native_state;
|
||||
(void) gpr;
|
||||
LOG(FATAL)
|
||||
<< "Implement apple signal handler.";
|
||||
#else
|
||||
|
||||
// `mcontext_t` is actually a `struct sigcontext`, defined as:
|
||||
// struct sigcontext {
|
||||
// __u64 fault_address;
|
||||
// /* AArch64 registers */
|
||||
// __u64 regs[31];
|
||||
// __u64 sp;
|
||||
// __u64 pc;
|
||||
// __u64 pstate;
|
||||
// /* 4K reserved for FP/SIMD state and future expansion */
|
||||
// __u8 __reserved[4096] __attribute__((__aligned__(16)));
|
||||
// };
|
||||
|
||||
const auto &mcontext = context->uc_mcontext;
|
||||
gpr.x0.qword = mcontext.regs[0];
|
||||
gpr.x1.qword = mcontext.regs[1];
|
||||
gpr.x2.qword = mcontext.regs[2];
|
||||
gpr.x3.qword = mcontext.regs[3];
|
||||
gpr.x4.qword = mcontext.regs[4];
|
||||
gpr.x5.qword = mcontext.regs[5];
|
||||
gpr.x6.qword = mcontext.regs[6];
|
||||
gpr.x7.qword = mcontext.regs[7];
|
||||
gpr.x8.qword = mcontext.regs[8];
|
||||
gpr.x9.qword = mcontext.regs[9];
|
||||
gpr.x10.qword = mcontext.regs[10];
|
||||
gpr.x11.qword = mcontext.regs[11];
|
||||
gpr.x12.qword = mcontext.regs[12];
|
||||
gpr.x13.qword = mcontext.regs[13];
|
||||
gpr.x14.qword = mcontext.regs[14];
|
||||
gpr.x15.qword = mcontext.regs[15];
|
||||
gpr.x16.qword = mcontext.regs[16];
|
||||
gpr.x17.qword = mcontext.regs[17];
|
||||
gpr.x18.qword = mcontext.regs[18];
|
||||
gpr.x19.qword = mcontext.regs[19];
|
||||
gpr.x20.qword = mcontext.regs[20];
|
||||
gpr.x21.qword = mcontext.regs[21];
|
||||
gpr.x22.qword = mcontext.regs[22];
|
||||
gpr.x23.qword = mcontext.regs[23];
|
||||
gpr.x24.qword = mcontext.regs[24];
|
||||
gpr.x25.qword = mcontext.regs[25];
|
||||
gpr.x26.qword = mcontext.regs[26];
|
||||
gpr.x27.qword = mcontext.regs[27];
|
||||
gpr.x28.qword = mcontext.regs[28];
|
||||
gpr.x29.qword = mcontext.regs[29];
|
||||
gpr.x30.qword = mcontext.regs[30];
|
||||
|
||||
gpr.pc.qword = mcontext.pc;
|
||||
gpr.sp.qword = mcontext.sp;
|
||||
|
||||
PSTATE pstate;
|
||||
pstate.flat = mcontext.pstate;
|
||||
native_state->sr.n = !!pstate.N;
|
||||
native_state->sr.z = !!pstate.Z;
|
||||
native_state->sr.c = !!pstate.C;
|
||||
native_state->sr.v = !!pstate.V;
|
||||
#endif // __APPLE__
|
||||
}
|
||||
siglongjmp(gJmpBuf, 0);
|
||||
}
|
||||
|
||||
static void ConsumeTrap(int, siginfo_t *, void *) {
|
||||
|
||||
}
|
||||
|
||||
static void HandleUnsupportedInstruction(int, siginfo_t *, void *) {
|
||||
siglongjmp(gUnsupportedInstrBuf, 0);
|
||||
}
|
||||
|
||||
typedef void (SignalHandler) (int, siginfo_t *, void *);
|
||||
static void HandleSignal(int sig_num, SignalHandler *handler) {
|
||||
struct sigaction sig;
|
||||
sig.sa_sigaction = handler;
|
||||
sig.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
#ifndef __APPLE__
|
||||
sig.sa_restorer = nullptr;
|
||||
#endif // __APPLE__
|
||||
sigfillset(&(sig.sa_mask));
|
||||
sigaction(sig_num, &sig, nullptr);
|
||||
}
|
||||
|
||||
// Set up various signal handlers.
|
||||
static void SetupSignals(void) {
|
||||
HandleSignal(SIGSEGV, RecoverFromError);
|
||||
HandleSignal(SIGBUS, RecoverFromError);
|
||||
HandleSignal(SIGFPE, RecoverFromError);
|
||||
HandleSignal(SIGTRAP, ConsumeTrap);
|
||||
HandleSignal(SIGILL, HandleUnsupportedInstruction);
|
||||
#ifdef SIGSTKFLT
|
||||
HandleSignal(SIGSTKFLT, RecoverFromError);
|
||||
#endif // SIGSTKFLT
|
||||
sigset_t set;
|
||||
sigemptyset(&set);
|
||||
sigprocmask(SIG_SETMASK, &set, nullptr);
|
||||
|
||||
stack_t sig_stack;
|
||||
sig_stack.ss_sp = &gSigStack;
|
||||
sig_stack.ss_size = SIGSTKSZ;
|
||||
sig_stack.ss_flags = 0;
|
||||
sigaltstack(&sig_stack, nullptr);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
google::ParseCommandLineFlags(&argc, &argv, true);
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
|
||||
auto this_exe = dlopen(nullptr, RTLD_NOW);
|
||||
|
||||
// Populate the tests vector.
|
||||
for (auto i = 0U; ; ++i) {
|
||||
const auto &test = test::__aarch64_test_table_begin[i];
|
||||
if (&test >= &(test::__aarch64_test_table_end[0])) break;
|
||||
gTests.push_back(&test);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << test.test_name << "_lifted";
|
||||
auto sym_func = dlsym(this_exe, ss.str().c_str());
|
||||
if (!sym_func) {
|
||||
sym_func = dlsym(this_exe, (std::string("_") + ss.str()).c_str());
|
||||
}
|
||||
|
||||
CHECK(nullptr != sym_func)
|
||||
<< "Could not find code for test case " << test.test_name;
|
||||
|
||||
auto lifted_func = reinterpret_cast<LiftedFunc *>(sym_func);
|
||||
gTranslatedFuncs[test.test_begin] = lifted_func;
|
||||
}
|
||||
|
||||
// Populate the random stack.
|
||||
memset(&gRandomStack, 0, sizeof(gRandomStack));
|
||||
for (auto &b : gRandomStack.bytes) {
|
||||
b = static_cast<uint8_t>(random());
|
||||
}
|
||||
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
SetupSignals();
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Trail of Bits, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TESTS_AARCH64_TEST_H_
|
||||
#define TESTS_AARCH64_TEST_H_
|
||||
|
||||
struct State;
|
||||
struct Memory;
|
||||
|
||||
namespace test {
|
||||
|
||||
enum : size_t {
|
||||
kPageSize = 4096,
|
||||
kMaxInstrLen = 4
|
||||
};
|
||||
|
||||
struct alignas(128) TestInfo {
|
||||
const uintptr_t test_begin;
|
||||
const uintptr_t test_end;
|
||||
const char *test_name;
|
||||
const uint64_t * const args_begin;
|
||||
const uint64_t * const args_end;
|
||||
const uint64_t num_args;
|
||||
const char *isel_name;
|
||||
} __attribute__((packed));
|
||||
|
||||
extern "C" {
|
||||
extern const TestInfo __aarch64_test_table_begin[];
|
||||
extern const TestInfo __aarch64_test_table_end[];
|
||||
} // extern C
|
||||
|
||||
} // namespace test
|
||||
|
||||
#endif // TESTS_AARCH64_TEST_H_
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Trail of Bits, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define CAT_3(a, b) a ## b
|
||||
#define CAT_2(a, b) CAT_3(a, b)
|
||||
#define CAT(a, b) CAT_2(a, b)
|
||||
|
||||
#define CAT3(a, b, c) CAT(a, CAT(b, c))
|
||||
|
||||
#define TO_STRING3(a) # a
|
||||
#define TO_STRING2(a) TO_STRING3(a)
|
||||
#define TO_STRING(a) TO_STRING2(a)
|
||||
|
||||
/* Note: Apple mangles C symbol names to have a leading underscore. */
|
||||
#ifdef __APPLE__
|
||||
# define SYMBOL(x) CAT(_, x)
|
||||
#else
|
||||
# define SYMBOL(x) x
|
||||
#endif
|
||||
|
||||
#define FUNC_NAME(instr_name, num_args) \
|
||||
CAT3(instr_name, _, num_args)
|
||||
|
||||
#ifdef IN_TEST_GENERATOR
|
||||
# define TEST_PROLOGUE
|
||||
#else
|
||||
# define TEST_PROLOGUE \
|
||||
adrp x28, SYMBOL(gNativeState) ; \
|
||||
add x28, x28, :lo12:SYMBOL(gNativeState) ;
|
||||
#endif /* IN_TEST_GENERATOR */
|
||||
|
||||
/* Defines the beginning of a test function. The key detail is that tests
|
||||
* register themselves into data structures located in a special section of
|
||||
* the binary.
|
||||
*
|
||||
* Each test function is associated with a `struct TestInfo` (see Test.h). These
|
||||
* structures are placed into the `__aarch64_test_table` section of the binary,
|
||||
* and bracketed by the `__aarch64_test_table_begin` and
|
||||
* `__aarch64_test_table_end` symbols, respectively.
|
||||
*/
|
||||
#define TEST_BEGIN(isel_name, instr_name, num_args) \
|
||||
.text ; \
|
||||
\
|
||||
.align 16 ; \
|
||||
.globl SYMBOL(FUNC_NAME(instr_name, num_args)) ; \
|
||||
\
|
||||
SYMBOL(FUNC_NAME(instr_name, num_args)): ; \
|
||||
.data ; \
|
||||
1: \
|
||||
.asciz TO_STRING(FUNC_NAME(instr_name, num_args)) ; \
|
||||
\
|
||||
.section "__aarch64_test_table", "a" ; \
|
||||
.balign 128 ; \
|
||||
2: \
|
||||
.quad 3f ; \
|
||||
.quad 7f ; \
|
||||
.quad 1b ; \
|
||||
.quad 4f ; \
|
||||
.quad 5f ; \
|
||||
.quad num_args ; \
|
||||
.quad 6f ; \
|
||||
\
|
||||
.rodata ; \
|
||||
6: \
|
||||
.asciz TO_STRING(isel_name) ; \
|
||||
\
|
||||
.text ; \
|
||||
3: \
|
||||
.cfi_startproc ; \
|
||||
TEST_PROLOGUE
|
||||
|
||||
/* Note: The test end address is placed *before* the `RET` so that we can
|
||||
* make sure that the end of a test marker is actually
|
||||
* `__remill_detach`. This is kind of a hack.
|
||||
*/
|
||||
#ifdef IN_TEST_GENERATOR
|
||||
# define TEST_END \
|
||||
7: \
|
||||
.cfi_endproc ; \
|
||||
.section "__aarch64_test_table", "a" ; \
|
||||
.quad 0 ; \
|
||||
hlt #0 ;
|
||||
|
||||
#else
|
||||
# define TEST_END \
|
||||
7: \
|
||||
b SYMBOL(__aarch64_save_state_after) ; \
|
||||
.cfi_endproc ; \
|
||||
.section "__aarch64_test_table", "a" ; \
|
||||
.quad 0 ; \
|
||||
.text ; \
|
||||
hlt #0 ;
|
||||
#endif /* IN_TEST_GENERATOR */
|
||||
|
||||
/* Defines the possible inputs to provide test. We add an extra 3 null inputs
|
||||
* at the end so that we can purposely 'overflow' when accessing the array so
|
||||
* that we can always specify 3 inputs, even if the program uses fewer. */
|
||||
#define TEST_INPUTS(...) \
|
||||
.data ; \
|
||||
.balign 8 ; \
|
||||
4: \
|
||||
.quad __VA_ARGS__ ; \
|
||||
5: \
|
||||
.quad 0, 0, 0; \
|
||||
.text ;
|
||||
|
||||
#ifndef IN_TEST_GENERATOR
|
||||
.data
|
||||
.extern SYMBOL(gTestToRun)
|
||||
.extern SYMBOL(gLiftedState)
|
||||
.extern SYMBOL(gNativeState)
|
||||
.extern SYMBOL(gStackSwitcher)
|
||||
.extern SYMBOL(gTestEnd)
|
||||
.extern SYMBOL(gStackSaveSlots)
|
||||
#endif /* IN_TEST_GENERATOR */
|
||||
|
||||
.text
|
||||
|
||||
#ifndef IN_TEST_GENERATOR
|
||||
.align 16
|
||||
.globl SYMBOL(InvokeTestCase)
|
||||
SYMBOL(InvokeTestCase):
|
||||
.cfi_startproc
|
||||
|
||||
/* Get the address of stack save slots into x28 */
|
||||
adrp x28, SYMBOL(gStackSaveSlots)
|
||||
add x28, x28, :lo12:SYMBOL(gStackSaveSlots)
|
||||
|
||||
str x29, [x28, #8] /* Save x29 into slot 1 */
|
||||
mov x29, sp
|
||||
str x29, [x28, #0] /* Save the stack pointer into slot 0 */
|
||||
ldr x29, [x28, #8] /* Restore x29 */
|
||||
|
||||
/* Swap off of the native stack */
|
||||
adrp x28, SYMBOL(gStackSwitcher)
|
||||
add x28, x28, :lo12:SYMBOL(gStackSwitcher)
|
||||
ldr x28, [x28]
|
||||
mov sp, x28
|
||||
|
||||
/* Start by saving the current native state into the `gLiftedState`
|
||||
* structure. This will be used as the initial state when running the
|
||||
* lifted tests. */
|
||||
adrp x28, SYMBOL(gLiftedState)
|
||||
add x28, x28, :lo12:SYMBOL(gLiftedState)
|
||||
#include "generated/Arch/AArch64/SaveState.S"
|
||||
|
||||
/* Branch to the test to run */
|
||||
adrp x28, SYMBOL(gTestToRun)
|
||||
add x28, x28, :lo12:SYMBOL(gTestToRun)
|
||||
ldr x28, [x28]
|
||||
br x28
|
||||
|
||||
.cfi_endproc
|
||||
|
||||
.align 16
|
||||
.globl SYMBOL(__aarch64_save_state_after)
|
||||
SYMBOL(__aarch64_save_state_after):
|
||||
.cfi_startproc
|
||||
/* Save the current native state into the `gNativeState`, which now
|
||||
* contains the post-test state for eventual comparison against lifted
|
||||
* execution. Finally, go and restore the originally saved state. */
|
||||
adrp x28, SYMBOL(gNativeState)
|
||||
add x28, x28, :lo12:SYMBOL(gNativeState)
|
||||
#include "generated/Arch/AArch64/SaveState.S"
|
||||
#include "generated/Arch/AArch64/RestoreState.S"
|
||||
|
||||
adrp x28, SYMBOL(gStackSaveSlots)
|
||||
add x28, x28, :lo12:SYMBOL(gStackSaveSlots)
|
||||
ldr x28, [x28] /* Load the saves SP from slot 0 */
|
||||
mov sp, x28
|
||||
|
||||
ret
|
||||
.cfi_endproc
|
||||
#endif /* IN_TEST_GENERATOR */
|
||||
|
||||
/* Create a symbol that represents the beginning of the test
|
||||
* information table. */
|
||||
.section "__aarch64_test_table", "a"
|
||||
.globl SYMBOL(__aarch64_test_table_begin)
|
||||
SYMBOL(__aarch64_test_table_begin):
|
||||
|
||||
#include "tests/AArch64/BINARY/ADD.S"
|
||||
#include "tests/AArch64/BINARY/SUB.S"
|
||||
|
||||
/* Create a symbol that represents the end of the test information table. */
|
||||
.section "__aarch64_test_table", "a"
|
||||
.globl SYMBOL(__aarch64_test_table_end)
|
||||
SYMBOL(__aarch64_test_table_end):
|
||||
|
||||
@@ -70,7 +70,7 @@ macro(COMPILE_X86_TESTS name address_size has_avx has_avx512)
|
||||
OUTPUT tests_${name}.S
|
||||
COMMAND ${CMAKE_BC_COMPILER}
|
||||
-Wno-override-module
|
||||
-S -O0 -g0
|
||||
-S -O3 -g0
|
||||
-c tests_${name}.bc
|
||||
-o tests_${name}.S
|
||||
DEPENDS tests_${name}.bc
|
||||
|
||||
+4
-11
@@ -46,14 +46,6 @@
|
||||
# define SYMBOL_PREFIX ""
|
||||
#endif
|
||||
|
||||
#ifndef REMILL_OS
|
||||
# if defined(__APPLE__)
|
||||
# define REMILL_OS "mac"
|
||||
# elif defined(__linux__)
|
||||
# define REMILL_OS "linux"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
DEFINE_string(bc_out, "",
|
||||
"Name of the file in which to place the generated bitcode.");
|
||||
|
||||
@@ -112,7 +104,7 @@ extern "C" int main(int argc, char *argv[]) {
|
||||
google::ParseCommandLineFlags(&argc, &argv, true);
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
|
||||
auto os = remill::kOSLinux;
|
||||
auto os = remill::GetOSName(REMILL_OS);
|
||||
auto arch_name = remill::GetArchName(FLAGS_arch);
|
||||
auto arch = remill::Arch::Get(os, arch_name);
|
||||
auto target_arch = remill::Arch::Get(os, remill::kArchAMD64_AVX512);
|
||||
@@ -120,9 +112,10 @@ extern "C" int main(int argc, char *argv[]) {
|
||||
DLOG(INFO) << "Generating tests.";
|
||||
|
||||
auto context = new llvm::LLVMContext;
|
||||
auto bc_file = remill::FindSemanticsBitcodeFile("", FLAGS_arch);
|
||||
auto bc_file = remill::FindSemanticsBitcodeFile(FLAGS_arch);
|
||||
auto module = remill::LoadModuleFromFile(context, bc_file);
|
||||
target_arch->PrepareModule(module);
|
||||
remill::GetHostArch()->PrepareModule(module);
|
||||
|
||||
for (auto i = 0U; ; ++i) {
|
||||
const auto &test = test::__x86_test_table_begin[i];
|
||||
if (&test >= &(test::__x86_test_table_end[0])) break;
|
||||
|
||||
+110
-67
@@ -61,8 +61,6 @@ static Stack gLiftedStack;
|
||||
static Stack gNativeStack;
|
||||
static Stack gSigStack;
|
||||
|
||||
static Flags gRflagsOff;
|
||||
static Flags gRflagsOn;
|
||||
static Flags gRflagsInitial;
|
||||
|
||||
static const addr_t g64BitMask = IF_64BIT_ELSE(~0UL, 0UL);
|
||||
@@ -101,23 +99,23 @@ static_assert(16 == sizeof(LongDoubleStorage),
|
||||
extern "C" {
|
||||
|
||||
// Used to record the FPU. We will use this to migrate native X87 or MMX
|
||||
// state into the `State` structure.
|
||||
// state into the `X86State` structure.
|
||||
FPU gFPU = {};
|
||||
|
||||
// Native state before we run the native test case. We then use this as the
|
||||
// initial state for the lifted testcase. The lifted test case code mutates
|
||||
// this, and we require that after running the lifted testcase, `gStateBefore`
|
||||
// matches `gStateAfter`,
|
||||
std::aligned_storage<sizeof(State), alignof(State)>::type gLiftedState;
|
||||
// this, and we require that after running the lifted testcase, `gX86StateBefore`
|
||||
// matches `gX86StateAfter`,
|
||||
std::aligned_storage<sizeof(X86State), alignof(X86State)>::type gLiftedState;
|
||||
|
||||
// Native state after running the native test case.
|
||||
std::aligned_storage<sizeof(State), alignof(State)>::type gNativeState;
|
||||
std::aligned_storage<sizeof(X86State), alignof(X86State)>::type gNativeState;
|
||||
|
||||
// Address of the native test to run. The `InvokeTestCase` function saves
|
||||
// the native program state but then needs a way to figure out where to go
|
||||
// without storing that information in any register. So what we do is we
|
||||
// store it here and indirectly `JMP` into the native test case code after
|
||||
// saving the machine state to `gStateBefore`.
|
||||
// saving the machine state to `gX86StateBefore`.
|
||||
uintptr_t gTestToRun = 0;
|
||||
|
||||
// Used for swapping the stack pointer between `gStack` and the normal
|
||||
@@ -134,8 +132,8 @@ uint8_t *gStackSwitcher = nullptr;
|
||||
uint64_t gStackSaveSlot = 0;
|
||||
|
||||
// Invoke a native test case addressed by `gTestToRun` and store the machine
|
||||
// state before and after executing the test in `gStateBefore` and
|
||||
// `gStateAfter`, respectively.
|
||||
// state before and after executing the test in `gX86StateBefore` and
|
||||
// `gX86StateAfter`, respectively.
|
||||
extern void InvokeTestCase(uint64_t, uint64_t, uint64_t);
|
||||
|
||||
#define MAKE_RW_MEMORY(size) \
|
||||
@@ -193,28 +191,60 @@ Memory *__remill_atomic_end(Memory *) { return nullptr; }
|
||||
|
||||
void __remill_defer_inlining(void) {}
|
||||
|
||||
Memory *__remill_error(addr_t, State &, Memory *) {
|
||||
Memory *__remill_error(addr_t, X86State &, Memory *) {
|
||||
siglongjmp(gJmpBuf, 0);
|
||||
}
|
||||
|
||||
Memory *__remill_missing_block(addr_t, State &, Memory *memory) {
|
||||
Memory *__remill_missing_block(addr_t, X86State &, Memory *memory) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
Memory *__remill_sync_hyper_call(
|
||||
Memory *mem, State &state, SyncHyperCall::Name call) {
|
||||
Memory *mem, X86State &state, SyncHyperCall::Name call) {
|
||||
auto eax = state.gpr.rax.dword;
|
||||
auto ebx = state.gpr.rbx.dword;
|
||||
auto ecx = state.gpr.rcx.dword;
|
||||
auto edx = state.gpr.rdx.dword;
|
||||
|
||||
switch (call) {
|
||||
case SyncHyperCall::kX86CPUID:
|
||||
state.gpr.rax.aword = 0;
|
||||
state.gpr.rbx.aword = 0;
|
||||
state.gpr.rcx.aword = 0;
|
||||
state.gpr.rdx.aword = 0;
|
||||
|
||||
asm volatile(
|
||||
"cpuid"
|
||||
: "=a"(state.gpr.rax.dword),
|
||||
"=b"(state.gpr.rbx.dword),
|
||||
"=c"(state.gpr.rcx.dword),
|
||||
"=d"(state.gpr.rdx.dword)
|
||||
: "a"(state.gpr.rax.dword),
|
||||
"b"(state.gpr.rbx.dword),
|
||||
"c"(state.gpr.rcx.dword),
|
||||
"d"(state.gpr.rdx.dword)
|
||||
: "a"(eax),
|
||||
"b"(ebx),
|
||||
"c"(ecx),
|
||||
"d"(edx)
|
||||
);
|
||||
break;
|
||||
|
||||
case SyncHyperCall::kX86ReadTSC:
|
||||
state.gpr.rax.aword = 0;
|
||||
state.gpr.rdx.aword = 0;
|
||||
asm volatile(
|
||||
"rdtsc"
|
||||
: "=a"(state.gpr.rax.dword),
|
||||
"=d"(state.gpr.rdx.dword)
|
||||
);
|
||||
break;
|
||||
|
||||
case SyncHyperCall::kX86ReadTSCP:
|
||||
state.gpr.rax.aword = 0;
|
||||
state.gpr.rcx.aword = 0;
|
||||
state.gpr.rdx.aword = 0;
|
||||
asm volatile(
|
||||
"rdtscp"
|
||||
: "=a"(state.gpr.rax.dword),
|
||||
"=c"(state.gpr.rcx.dword),
|
||||
"=d"(state.gpr.rdx.dword)
|
||||
);
|
||||
break;
|
||||
|
||||
@@ -225,19 +255,19 @@ Memory *__remill_sync_hyper_call(
|
||||
return mem;
|
||||
}
|
||||
|
||||
Memory *__remill_function_call(addr_t, State &, Memory *) {
|
||||
Memory *__remill_function_call(addr_t, X86State &, Memory *) {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
Memory *__remill_function_return(addr_t, State &, Memory *) {
|
||||
Memory *__remill_function_return(addr_t, X86State &, Memory *) {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
Memory *__remill_jump(addr_t, State &, Memory *) {
|
||||
Memory *__remill_jump(addr_t, X86State &, Memory *) {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
Memory *__remill_async_hyper_call(addr_t, State &, Memory *) {
|
||||
Memory *__remill_async_hyper_call(addr_t, X86State &, Memory *) {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
@@ -274,7 +304,7 @@ void __remill_mark_as_used(void *mem) {
|
||||
|
||||
} // extern C
|
||||
|
||||
typedef Memory *(LiftedFunc)(addr_t, State &, Memory *);
|
||||
typedef Memory *(LiftedFunc)(addr_t, X86State &, Memory *);
|
||||
|
||||
// Mapping of test name to translated function.
|
||||
static std::map<uint64_t, LiftedFunc *> gTranslatedFuncs;
|
||||
@@ -283,37 +313,15 @@ static std::vector<const test::TestInfo *> gTests;
|
||||
|
||||
static void InitFlags(void) {
|
||||
asm(
|
||||
"pushfq;"
|
||||
"pushfq;"
|
||||
"pushfq;"
|
||||
"pop %0;"
|
||||
"pop %1;"
|
||||
"pop %2;"
|
||||
:
|
||||
: "m"(gRflagsOn),
|
||||
"m"(gRflagsOff),
|
||||
"m"(gRflagsInitial));
|
||||
|
||||
gRflagsOn.cf = true;
|
||||
gRflagsOn.pf = true;
|
||||
gRflagsOn.af = true;
|
||||
gRflagsOn.zf = true;
|
||||
gRflagsOn.sf = true;
|
||||
gRflagsOn.df = true;
|
||||
gRflagsOn.of = true;
|
||||
|
||||
gRflagsOff.cf = false;
|
||||
gRflagsOff.pf = false;
|
||||
gRflagsOff.af = false;
|
||||
gRflagsOff.zf = false;
|
||||
gRflagsOff.sf = false;
|
||||
gRflagsOff.df = false;
|
||||
gRflagsOff.of = false;
|
||||
: "m"(gRflagsInitial));
|
||||
}
|
||||
|
||||
// Convert some native state, stored in various ways, into the `State` structure
|
||||
// Convert some native state, stored in various ways, into the `X86State` structure
|
||||
// type.
|
||||
static void ImportX87State(State *state) {
|
||||
static void ImportX87X86State(X86State *state) {
|
||||
|
||||
// Looks like MMX state.
|
||||
if (kFPUAbridgedTagValid == gFPU.ftw.fxsave.abridged.r0 &&
|
||||
@@ -386,8 +394,8 @@ static void RunWithFlags(const test::TestInfo *info,
|
||||
memset(&gLiftedState, 0, sizeof(gLiftedState));
|
||||
memset(&gNativeState, 0, sizeof(gNativeState));
|
||||
|
||||
auto lifted_state = reinterpret_cast<State *>(&gLiftedState);
|
||||
auto native_state = reinterpret_cast<State *>(&gNativeState);
|
||||
auto lifted_state = reinterpret_cast<X86State *>(&gLiftedState);
|
||||
auto native_state = reinterpret_cast<X86State *>(&gNativeState);
|
||||
|
||||
// This will be used to initialize the native flags state before executing
|
||||
// the native test.
|
||||
@@ -411,7 +419,7 @@ static void RunWithFlags(const test::TestInfo *info,
|
||||
native_test_faulted = true;
|
||||
}
|
||||
|
||||
ImportX87State(native_state);
|
||||
ImportX87X86State(native_state);
|
||||
ResetFlags();
|
||||
|
||||
// Copy out whatever was recorded on the stack so that we can compare it
|
||||
@@ -422,7 +430,7 @@ static void RunWithFlags(const test::TestInfo *info,
|
||||
auto lifted_func = gTranslatedFuncs[info->test_begin];
|
||||
|
||||
// This will execute on our stack but the lifted code will operate on
|
||||
// `gStack`. The mechanism behind this is that `gStateBefore` is the native
|
||||
// `gStack`. The mechanism behind this is that `gX86StateBefore` is the native
|
||||
// program state recorded before executing the native testcase, but after
|
||||
// swapping execution to operate on `gStack`.
|
||||
if (!sigsetjmp(gJmpBuf, true)) {
|
||||
@@ -494,16 +502,12 @@ static void RunWithFlags(const test::TestInfo *info,
|
||||
EXPECT_TRUE(lifted_state->gpr == native_state->gpr);
|
||||
if (gLiftedState != gNativeState) {
|
||||
LOG(ERROR)
|
||||
<< "States did not match for " << info->test_name
|
||||
<< " with arguments " << std::hex << arg1 << ", "
|
||||
<< std::hex << arg2 << ", " << std::hex << arg3;
|
||||
<< "States did not match for " << desc;
|
||||
EXPECT_TRUE(!"Lifted and native states did not match.");
|
||||
}
|
||||
if (gLiftedStack != gNativeStack) {
|
||||
LOG(ERROR)
|
||||
<< "Stacks did not match for " << info->test_name
|
||||
<< " with arguments " << std::hex << arg1 << ", "
|
||||
<< std::hex << arg2 << ", " << std::hex << arg3;
|
||||
<< "Stacks did not match for " << desc;
|
||||
|
||||
for (size_t i = 0; i < sizeof(gLiftedStack.bytes); ++i) {
|
||||
if (gLiftedStack.bytes[i] != gNativeStack.bytes[i]) {
|
||||
@@ -526,21 +530,60 @@ TEST_P(InstrTest, SemanticsMatchNative) {
|
||||
args < info->args_end;
|
||||
args += info->num_args) {
|
||||
std::stringstream ss;
|
||||
ss << info->test_name << " with";
|
||||
if (1 <= info->num_args) {
|
||||
ss << "args: 0x" << std::hex << args[0];
|
||||
ss << " ARG1=0x" << std::hex << args[0];
|
||||
if (2 <= info->num_args) {
|
||||
ss << ", 0x" << std::hex << args[1];
|
||||
ss << " ARG2=0x" << std::hex << args[1];
|
||||
if (3 <= info->num_args) {
|
||||
ss << ", 0x" << std::hex << args[3];
|
||||
ss << " ARG3=0x" << std::hex << args[3];
|
||||
}
|
||||
}
|
||||
ss << ";" << std::dec;
|
||||
}
|
||||
auto desc = ss.str();
|
||||
RunWithFlags(info, gRflagsOn, desc + " aflags on",
|
||||
args[0], args[1], args[2]);
|
||||
RunWithFlags(info, gRflagsOff, desc + " aflags off",
|
||||
args[0], args[1], args[2]);
|
||||
|
||||
union EFLAGS {
|
||||
uint32_t flat;
|
||||
struct {
|
||||
uint32_t cf:1;
|
||||
uint32_t pf:1;
|
||||
uint32_t af:1;
|
||||
uint32_t zf:1;
|
||||
uint32_t sf:1;
|
||||
uint32_t df:1;
|
||||
uint32_t of:1;
|
||||
uint32_t _0:25;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(sizeof(EFLAGS) == 4, "Invalid packing of `union EFLAGS`.");
|
||||
|
||||
// Go through all possible flag combinations.
|
||||
for (uint32_t i = 0U; i <= 0x7FU; ++i) {
|
||||
EFLAGS eflags;
|
||||
eflags.flat = i;
|
||||
|
||||
std::stringstream ss2;
|
||||
ss2 << desc << " and"
|
||||
<< " CF=" << eflags.cf
|
||||
<< " PF=" << eflags.pf
|
||||
<< " AF=" << eflags.af
|
||||
<< " ZF=" << eflags.zf
|
||||
<< " SF=" << eflags.sf
|
||||
<< " DF=" << eflags.df
|
||||
<< " OF=" << eflags.of;
|
||||
|
||||
Flags flags = gRflagsInitial;
|
||||
flags.cf = eflags.cf;
|
||||
flags.pf = eflags.pf;
|
||||
flags.af = eflags.af;
|
||||
flags.zf = eflags.zf;
|
||||
flags.sf = eflags.sf;
|
||||
flags.df = eflags.df;
|
||||
flags.of = eflags.of;
|
||||
|
||||
RunWithFlags(info, flags, ss2.str(), args[0], args[1], args[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,10 +595,10 @@ INSTANTIATE_TEST_CASE_P(
|
||||
// Recover from a signal.
|
||||
static void RecoverFromError(int sig_num, siginfo_t *, void *context_) {
|
||||
if (gInNativeTest) {
|
||||
memcpy(&gNativeState, &gLiftedState, sizeof(State));
|
||||
memcpy(&gNativeState, &gLiftedState, sizeof(X86State));
|
||||
|
||||
auto context = reinterpret_cast<ucontext_t *>(context_);
|
||||
auto native_state = reinterpret_cast<State *>(&gNativeState);
|
||||
auto native_state = reinterpret_cast<X86State *>(&gNativeState);
|
||||
auto &gpr = native_state->gpr;
|
||||
#ifdef __APPLE__
|
||||
const auto mcontext = context->uc_mcontext;
|
||||
|
||||
@@ -14,9 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TESTS_X86_TESTS_S_
|
||||
#define TESTS_X86_TESTS_S_
|
||||
|
||||
#ifndef ADDRESS_SIZE_BITS
|
||||
# define ADDRESS_SIZE_BITS 64
|
||||
#endif
|
||||
@@ -517,6 +514,3 @@ SYMBOL(__x86_test_table_begin):
|
||||
.section "__x86_test_table", "a"
|
||||
.globl SYMBOL(__x86_test_table_end)
|
||||
SYMBOL(__x86_test_table_end):
|
||||
|
||||
|
||||
#endif /* TESTS_X86_TESTS_S_ */
|
||||
|
||||
+100
-2
@@ -12,10 +12,108 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# test tools
|
||||
#add_subdirectory(testdec)
|
||||
#
|
||||
# compiler selection
|
||||
#
|
||||
|
||||
if (DEFINED ENV{LLVM_INSTALL_PREFIX})
|
||||
set(LLVM_INSTALL_PREFIX $ENV{LLVM_INSTALL_PREFIX})
|
||||
endif ()
|
||||
|
||||
# it is important to avoid re-defining these variables if they have been already
|
||||
# set or you risk ending up in a configure loop!
|
||||
if (NOT DEFINED CMAKE_C_COMPILER)
|
||||
if (DEFINED LLVM_INSTALL_PREFIX)
|
||||
set(CMAKE_C_COMPILER "${LLVM_INSTALL_PREFIX}/bin/clang")
|
||||
else ()
|
||||
set(CMAKE_C_COMPILER "clang")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED CMAKE_CXX_COMPILER)
|
||||
if (DEFINED LLVM_INSTALL_PREFIX)
|
||||
set(CMAKE_CXX_COMPILER "${LLVM_INSTALL_PREFIX}/bin/clang++")
|
||||
else ()
|
||||
set(CMAKE_CXX_COMPILER "clang++")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED CMAKE_ASM_COMPILER)
|
||||
if (DEFINED LLVM_INSTALL_PREFIX)
|
||||
set(CMAKE_ASM_COMPILER "${LLVM_INSTALL_PREFIX}/bin/clang")
|
||||
else ()
|
||||
set(CMAKE_ASM_COMPILER ${CMAKE_CXX_COMPILER})
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED CMAKE_LLVM_LINK)
|
||||
if (DEFINED LLVM_INSTALL_PREFIX)
|
||||
set(CMAKE_LLVM_LINK "${LLVM_INSTALL_PREFIX}/bin/llvm-link")
|
||||
else ()
|
||||
set(CMAKE_LLVM_LINK "llvm-link")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
#
|
||||
# compiler settings
|
||||
#
|
||||
|
||||
# enable the gnu extensions
|
||||
set(CMAKE_CXX_EXTENSIONS ON)
|
||||
|
||||
# visual studio already defaults to c++11
|
||||
if (NOT WIN32)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
endif ()
|
||||
|
||||
|
||||
# default build type
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE "RelWithDebInfo")
|
||||
endif ()
|
||||
|
||||
# debug symbols
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||
set(PROJECT_CXXFLAGS "${PROJECT_CXXFLAGS} -gdwarf-2 -g3")
|
||||
endif ()
|
||||
|
||||
# optimization flags and definitions
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(PROJECT_CXXFLAGS "${PROJECT_CXXFLAGS} -O0")
|
||||
else ()
|
||||
set(PROJECT_CXXFLAGS "${PROJECT_CXXFLAGS} -O3")
|
||||
list(APPEND PROJECT_DEFINITIONS "NDEBUG")
|
||||
endif ()
|
||||
|
||||
#
|
||||
# libraries
|
||||
#
|
||||
|
||||
if (DEFINED ENV{TRAILOFBITS_LIBRARIES})
|
||||
set(LIBRARY_REPOSITORY_ROOT $ENV{TRAILOFBITS_LIBRARIES})
|
||||
include("${LIBRARY_REPOSITORY_ROOT}/cmake/repository.cmake")
|
||||
|
||||
message(STATUS "Using the following library repository: ${LIBRARY_REPOSITORY_ROOT}")
|
||||
else ()
|
||||
message(STATUS "Using system libraries")
|
||||
endif ()
|
||||
|
||||
if (DEFINED ENV{TRAILOFBITS_LIBRARIES})
|
||||
set(LIBRARY_REPOSITORY_ROOT $ENV{TRAILOFBITS_LIBRARIES})
|
||||
include("${LIBRARY_REPOSITORY_ROOT}/cmake/repository.cmake")
|
||||
|
||||
message(STATUS "Using the following library repository: ${LIBRARY_REPOSITORY_ROOT}")
|
||||
else ()
|
||||
message(STATUS "Using system libraries")
|
||||
endif ()
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# mcsema needs to be manually cloned into this repo.
|
||||
if (EXISTS ${CMAKE_SOURCE_DIR}/tools/mcsema)
|
||||
add_subdirectory(mcsema)
|
||||
endif ()
|
||||
|
||||
if (EXISTS ${CMAKE_SOURCE_DIR}/tools/vmill)
|
||||
add_subdirectory(vmill)
|
||||
endif ()
|
||||
|
||||
Reference in New Issue
Block a user