From bef332fd2ad5418a48bc9a9be90f282a9b22cab5 Mon Sep 17 00:00:00 2001 From: Peter Goodman Date: Fri, 4 Aug 2017 15:24:46 -0400 Subject: [PATCH] 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 --- CMakeLists.txt | 39 +- cmake/BCCompiler/CMakeBCInformation.cmake | 22 +- docs/LIFE_OF_AN_INSTRUCTION.md | 31 +- generated/Arch/AArch64/RestoreState.S | 51 ++ generated/Arch/AArch64/SaveState.S | 63 +++ remill/Arch/AArch64/Arch.cpp | 164 ++++-- remill/Arch/AArch64/Decode.cpp | 228 -------- remill/Arch/AArch64/Runtime/BasicBlock.cpp | 143 ++--- remill/Arch/AArch64/Runtime/CMakeLists.txt | 2 +- remill/Arch/AArch64/Runtime/Instructions.cpp | 81 ++- remill/Arch/AArch64/Runtime/State.h | 306 ++++++----- remill/Arch/AArch64/Runtime/Types.h | 19 +- remill/Arch/AArch64/Semantics/BINARY.cpp | 17 +- remill/Arch/AArch64/Semantics/BRANCH.cpp | 28 +- remill/Arch/AArch64/Semantics/DATAXFER.cpp | 35 +- remill/Arch/Arch.cpp | 16 +- remill/Arch/Arch.h | 8 +- remill/Arch/Name.h | 12 + remill/Arch/Runtime/Definitions.h | 2 + remill/Arch/Runtime/HyperCall.h | 2 +- remill/Arch/Runtime/Types.h | 23 +- remill/Arch/X86/Runtime/CMakeLists.txt | 9 +- remill/Arch/X86/Runtime/State.h | 20 +- remill/Arch/X86/Semantics/BITBYTE.cpp | 4 +- remill/Arch/X86/Semantics/SSE.cpp | 4 +- remill/BC/IntrinsicTable.h | 2 +- remill/BC/Lifter.cpp | 16 + remill/BC/Util.cpp | 49 +- remill/BC/Util.h | 4 +- remill/OS/OS.cpp | 15 +- remill/OS/OS.h | 12 + scripts/aarch64/print_save_state_asm.sh | 51 ++ scripts/build.sh | 14 +- scripts/x86/print_save_state_asm.sh | 6 +- tests/AArch64/BINARY/ADD.S | 154 ++++++ tests/AArch64/BINARY/SUB.S | 154 ++++++ tests/AArch64/CMakeLists.txt | 84 +++ tests/AArch64/Lift.cpp | 144 +++++ tests/AArch64/PrintRestoreState.cpp | 99 ++++ tests/AArch64/PrintSaveState.cpp | 120 ++++ tests/AArch64/Run.cpp | 542 +++++++++++++++++++ tests/AArch64/Test.h | 47 ++ tests/AArch64/Tests.S | 202 +++++++ tests/X86/CMakeLists.txt | 2 +- tests/X86/Lift.cpp | 15 +- tests/X86/Run.cpp | 177 +++--- tests/X86/Tests.S | 6 - tools/CMakeLists.txt | 102 +++- 48 files changed, 2562 insertions(+), 784 deletions(-) create mode 100644 generated/Arch/AArch64/RestoreState.S create mode 100644 generated/Arch/AArch64/SaveState.S create mode 100755 scripts/aarch64/print_save_state_asm.sh create mode 100644 tests/AArch64/BINARY/ADD.S create mode 100644 tests/AArch64/BINARY/SUB.S create mode 100644 tests/AArch64/CMakeLists.txt create mode 100644 tests/AArch64/Lift.cpp create mode 100644 tests/AArch64/PrintRestoreState.cpp create mode 100644 tests/AArch64/PrintSaveState.cpp create mode 100644 tests/AArch64/Run.cpp create mode 100644 tests/AArch64/Test.h create mode 100644 tests/AArch64/Tests.S diff --git a/CMakeLists.txt b/CMakeLists.txt index d28147d5..110094da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/cmake/BCCompiler/CMakeBCInformation.cmake b/cmake/BCCompiler/CMakeBCInformation.cmake index a0ec5b06..2afc4ff4 100644 --- a/cmake/BCCompiler/CMakeBCInformation.cmake +++ b/cmake/BCCompiler/CMakeBCInformation.cmake @@ -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 () diff --git a/docs/LIFE_OF_AN_INSTRUCTION.md b/docs/LIFE_OF_AN_INSTRUCTION.md index 21a7bd41..c7af03b4 100644 --- a/docs/LIFE_OF_AN_INSTRUCTION.md +++ b/docs/LIFE_OF_AN_INSTRUCTION.md @@ -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(state, &memory, &EAX, 1); + memory = MOV(memory, state, &EAX, 1); // push ebx EIP += 1; - PUSH(state, &memory, EBX); + memory = PUSH(memory, state, EBX); // mov ebx, dword [esp+0x8] EIP += 4; - MOV(state, &memory, &EBX, ESP + 0x8); + memory = MOV(memory, state, &EBX, ESP + 0x8); // int 0x80 EIP += 2; - INT_IMMb(state, &memory, 0x80); + memory = INT_IMMb(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. diff --git a/generated/Arch/AArch64/RestoreState.S b/generated/Arch/AArch64/RestoreState.S new file mode 100644 index 00000000..8b7fea3c --- /dev/null +++ b/generated/Arch/AArch64/RestoreState.S @@ -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] diff --git a/generated/Arch/AArch64/SaveState.S b/generated/Arch/AArch64/SaveState.S new file mode 100644 index 00000000..68e45cba --- /dev/null +++ b/generated/Arch/AArch64/SaveState.S @@ -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] diff --git a/remill/Arch/AArch64/Arch.cpp b/remill/Arch/AArch64/Arch.cpp index a50a5237..56063c44 100644 --- a/remill/Arch/AArch64/Arch.cpp +++ b/remill/Arch/AArch64/Arch.cpp @@ -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 ,