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:
Peter Goodman
2017-08-04 15:24:46 -04:00
committed by GitHub
parent 6885ff58bd
commit bef332fd2a
48 changed files with 2562 additions and 784 deletions
+28 -11
View File
@@ -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)
+10 -12
View File
@@ -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 ()
+14 -17
View File
@@ -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.
+51
View File
@@ -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]
+63
View File
@@ -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
View File
@@ -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;
}
-228
View File
@@ -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
+73 -70
View File
@@ -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;
}
+1 -1
View File
@@ -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)
+40 -41
View File
@@ -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
View File
@@ -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_
+14 -5
View File
@@ -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_
+5 -12
View File
@@ -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 {
+14 -14
View File
@@ -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) {
+20 -15
View File
@@ -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
View File
@@ -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
View File
@@ -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
+12
View File
@@ -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 {
+2
View File
@@ -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.
+1 -1
View File
@@ -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
View File
@@ -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;
+1 -8
View File
@@ -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}")
+10 -10
View File
@@ -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_
+2 -2
View File
@@ -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;
}
+2 -2
View File
@@ -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) {
+1 -1
View File
@@ -27,7 +27,7 @@ namespace remill {
class IntrinsicTable {
public:
IntrinsicTable(llvm::Module *M);
explicit IntrinsicTable(llvm::Module *module);
llvm::Function * const error;
+16
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 {
+12
View File
@@ -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 {
+51
View File
@@ -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
View File
@@ -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
+4 -2
View File
@@ -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
+154
View File
@@ -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
+154
View File
@@ -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
+84
View File
@@ -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)
+144
View File
@@ -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;
}
+99
View File
@@ -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;
}
+120
View File
@@ -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;
}
+542
View File
@@ -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();
}
+47
View File
@@ -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_
+202
View File
@@ -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):
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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;
-6
View File
@@ -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
View File
@@ -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 ()