Aarch64 support and other stuff (#113)

* This includes most of Alessandro's wonderful cmake changes, and starts into AArch64 support, though not using Capstone anymore.

* Trying to make travis work

* Minor additions, still not completely working on my ubuntu 14.04 vm or on travis

* Making progress on getting travis to build

* Some documentation improvements

* I push the travis button
This commit is contained in:
Peter Goodman
2017-06-14 19:57:57 -04:00
committed by GitHub
parent c7874d56f4
commit 592b6cbe8d
49 changed files with 148394 additions and 758 deletions
+96
View File
@@ -0,0 +1,96 @@
---
Language: Cpp
# BasedOnStyle: Google
AccessModifierOffset: -1
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: true
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: true
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
IncludeCategories:
- Regex: '^<.*\.h>'
Priority: 1
- Regex: '^<.*'
Priority: 2
- Regex: '.*'
Priority: 3
IncludeIsMainRegex: '([-_](test|unittest))?$'
IndentCaseLabels: true
IndentWidth: 2
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: false
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
ReflowComments: true
SortIncludes: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Auto
TabWidth: 2
UseTab: Never
...
+6
View File
@@ -15,6 +15,12 @@
install_manifest.txt
Makefile
CMakeLists.txt.user
.vscode
.idea
cmake-build-debug
cmake-build-release
bin/*
lib/*
+4 -14
View File
@@ -1,27 +1,17 @@
sudo: required
dist: trusty
osx_image: xcode8
language: generic
os:
- linux
- osx
compiler:
- gcc
- clang
matrix:
exclude:
- compiler: gcc
os: osx
- compiler: clang
os: linux
allow_failures:
- os: osx
install:
- ./scripts/travis/install.sh
- ./scripts/build_ubuntu.sh
before_script:
- cd build
- cd ../remill-build
- make build_x86_tests
script:
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]] ; then ctest ; fi
- make test
notifications:
slack:
secure: eW0eTnlEz1Pa68ZJOps7L0UscoxoFa+JLuOIOy4PPrLs1DfIiGdhHMfWs2U8mF8YaBYmrXgav/1IxNVtdPxiwfyLdYbiNSoEfo1ofPpizTH02flXthnUVIzlQD1jT6OSk840vskT2WWufBEkoaEIxFDXVI8PFtAtIYyfveVEnRdkTPUvuLV0pTX92c+ZCH3FIWtDey+4aVUwsttJSVloDSExy27LuoBLDPSNX8goiZFFrIcBRpspumysEJXdj5FSNiED9TQA1NCGWhsUqUlTPMYp1B8M/lKciugqhAlGijSL3lOisJZWsf/YZcto/N7wNMU54YNbfmMUZu6K9UkveVOD8qtlAs/8MDPyH0O+s/Hf2y6KGb7PSO9sxYF/WWRLNqxp8SgmY3IKu8niglXsHBNd4cXibvM3a2KJoJo453PI48XAfaygPYcYcuNedsQT9Fng384vpUjV7YLLU1452qTjx5T5SarjeF4ExtzJVYAQWhFT7RkNpdtDnKNLczwicM8wRSCHfEmGLR1Dpw5zQlS3uFJydTWTgAeLSHKKkfgqLYiR7/Ongv1M8Q+JUgYtF2dV9PLEpb0QGzw6B/RnW2vGNWIE+JJCjpZTizQj0f8UIh+k//v9jL4simVHRDPArnvJaFP0T4BrUl4Z4XN/dYFOogp9rCHgQCvqVOCPrFI=
+12 -6
View File
@@ -98,7 +98,7 @@ if (NOT WIN32)
endif ()
# warnings and compiler settings
set(PROJECT_CXXWARNINGS "-Wall -Wextra -Werror -pedantic -Wno-unused-parameter -Wno-c++98-compat -Wno-unreachable-code-return -Wno-nested-anon-types -Wno-extended-offsetof -Wno-gnu-anonymous-struct -Wno-gnu-designator -Wno-variadic-macros -Wno-gnu-zero-variadic-macro-arguments -Wno-gnu-statement-expression -Wno-return-type-c-linkage -Wno-c99-extensions -Wno-ignored-attributes -Wno-unused-local-typedef")
set(PROJECT_CXXWARNINGS "-Wall -Wextra -Wno-unused-parameter -Wno-c++98-compat -Wno-unreachable-code-return -Wno-nested-anon-types -Wno-extended-offsetof -Wno-gnu-anonymous-struct -Wno-gnu-designator -Wno-variadic-macros -Wno-gnu-zero-variadic-macro-arguments -Wno-gnu-statement-expression -Wno-return-type-c-linkage -Wno-c99-extensions -Wno-ignored-attributes -Wno-unused-local-typedef")
set(PROJECT_CXXFLAGS "${PROJECT_CXXFLAGS} -Wno-unknown-warning-option ${PROJECT_CXXWARNINGS} -fPIC -fno-omit-frame-pointer -fvisibility-inlines-hidden -fno-exceptions -fno-asynchronous-unwind-tables -fno-rtti")
# default build type
@@ -150,9 +150,15 @@ list(APPEND PROJECT_LIBRARIES gflags)
list(APPEND PROJECT_DEFINITIONS "INSTALL_SEMANTICS_DIR=\"${CMAKE_INSTALL_PREFIX}share/remill/semantics/\"")
list(APPEND PROJECT_DEFINITIONS "BUILD_SEMANTICS_DIR=\"${CMAKE_CURRENT_BINARY_DIR}/remill/Arch/X86/Runtime/\"")
list(APPEND PROJECT_DEFINITIONS "BUILD_SEMANTICS_DIR_ARM=\"${CMAKE_CURRENT_BINARY_DIR}/remill/Arch/AArch64/Runtime/\"")
add_library(${PROJECT_NAME} STATIC
remill/Arch/AArch64/Arch.cpp
remill/Arch/AArch64/Decode.cpp
remill/Arch/AArch64/Extract.cpp
remill/Arch/X86/Arch.cpp
remill/Arch/Arch.cpp
remill/Arch/Instruction.cpp
remill/Arch/Name.cpp
@@ -186,13 +192,13 @@ install(DIRECTORY DESTINATION "share/remill")
#
add_custom_target(semantics)
# runtimes
add_subdirectory(remill/Arch/X86/Runtime)
add_subdirectory(remill/Arch/AArch64/Runtime)
# mcsema needs to be manually cloned into this repo.
if (EXISTS ${CMAKE_SOURCE_DIR}/tools/mcsema)
add_subdirectory(tools/mcsema)
endif ()
# tools
add_subdirectory(tools)
# only enable tests when compiling under x64
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
+26 -56
View File
@@ -1,6 +1,6 @@
# Remill
Remill is a static binary translator that translates machine code into [LLVM bitcode](http://llvm.org/docs/LangRef.html). It translates x86 and amd64 machine code (including AVX and AVX512) into LLVM bitcode.
Remill is a static binary translator that translates machine code instructions into [LLVM bitcode](http://llvm.org/docs/LangRef.html). It translates x86 and amd64 machine code (including AVX and AVX512) into LLVM bitcode. AArch64 support is underway.
Remill focuses on accurately lifting instructions. It is meant to be used as a library for other tools, e.g. [McSema](https://github.com/trailofbits/mcsema).
@@ -9,7 +9,6 @@ Remill focuses on accurately lifting instructions. It is meant to be used as a l
| | master |
| ----- | ------ |
| Linux | [![Build Status](https://travis-ci-job-status.herokuapp.com/badge/trailofbits/remill/master/linux/)](https://travis-ci.org/trailofbits/remill) |
| macOS | [![Build Status](https://travis-ci-job-status.herokuapp.com/badge/trailofbits/remill/master/osx/)](https://travis-ci.org/trailofbits/remill) |
## Additional Documentation
@@ -30,6 +29,8 @@ We are actively working on porting Remill to macOS.
## Dependencies
Most of Remill's dependencies can be provided by the [cxx-common](https://github.com/trailofbits/cxx-common) repository. Trail of Bits hosts downloadable, pre-built versions of cxx-common, which makes it substantially easier to get up and running with Remill. Nonetheless, the following table represents most of Remill's dependencies.
| Name | Version |
| ---- | ------- |
| [Git](https://git-scm.com/) | Latest |
@@ -45,11 +46,9 @@ We are actively working on porting Remill to macOS.
## Getting and Building the Code
### Step 1: Install dependencies
### On Linux
#### On Linux
##### Install Dependencies
First, update aptitude and get install the baseline dependencies.
```shell
sudo apt-get update
@@ -57,70 +56,41 @@ sudo apt-get upgrade
sudo apt-get install \
git \
cmake \
python2.7 python-pip \
python-pip \
build-essential \
unzip \
software-properties-common \
realpath
```
##### Upgrade CMake (Ubuntu 14.04)
Users wishing to run Remill on Ubuntu 14.04 should upgrade their version of CMake.
Next, clone the repository. This will clone the code into the `remill` directory.
```shell
sudo add-apt-repository -y ppa:george-edison55/cmake-3.x
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install cmake
git clone git@github.com:trailofbits/remill.git
```
#### On OS X
##### Install Dependencies
```
brew install glog
```
### Step 2: Clone and Enter the Repository
#### Clone the repository
```shell
This will also clone the cxx_common module (used to generate a library repository) and mcsema.
git clone --resursive git@github.com:trailofbits/remill.git
```
#### Enter the repository
```shell
cd remill
```
#### Build the code.
```shell
./build.sh
```
## Building and Running the Test Suite
### Build Google Test
#### On Linux
This script will build and install the Google Test framework. It will request administrator permissions.
Next, we build Remill. This script will create another directory, `remill-build`
at the same level as the `remill` directory. All remaining dependencies needed
by Remill will be built in the `remill-build` directory.
```shell
./scripts/unix/install_gtest.sh
./remill/scripts/build.sh
```
### Generate and Run the Test Cases
Next, we can install Remill. Remill itself is a library, and so there is no real way
to try it. However, you can head on over to the [McSema](https://github.com/trailofbits/mcsema) repository, which uses Remill for lifting instructions.
```shell
sudo make build_x86_tests
ctest
cd ./remill-build
sudo make install
```
## Try it Out
We can also build and run Remill's test suite.
```shell
cd ./remill-build
make build_x86_tests
./cmake-3.2.0-Linux-x86_64/bin/ctest
```
Remill is a library, and so there is no single way to try it. However, you can head on over to the [McSema](https://github.com/trailofbits/mcsema) repository and try that!
+14
View File
@@ -1,3 +1,17 @@
# 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.
set(CMAKE_BC_COMPILER "@CMAKE_BC_COMPILER@")
set(CMAKE_BC_LINKER "@CMAKE_BC_LINKER@")
+17 -2
View File
@@ -1,11 +1,26 @@
set(DEFAULT_BC_COMPILER_FLAGS "-std=gnu++11 -emit-llvm -Wno-unknown-warning-option -Werror -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")
# 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.
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")
if (NOT CMAKE_BC_COMPILE_OBJECT)
if (NOT DEFINED CMAKE_BC_COMPILER)
message(SEND_ERROR "The bitcode compiler was not found!")
endif ()
set(CMAKE_BC_COMPILE_OBJECT "${CMAKE_BC_COMPILER} <DEFINES> <INCLUDES> ${DEFAULT_BC_COMPILER_FLAGS} <FLAGS> -c <SOURCE> -o <OBJECT>")
# TODO(pag): Removed <INCLUDES> because it caused warnings.
set(CMAKE_BC_COMPILE_OBJECT "${CMAKE_BC_COMPILER} <DEFINES> ${DEFAULT_BC_COMPILER_FLAGS} <FLAGS> -c <SOURCE> -o <OBJECT>")
endif ()
if (NOT CMAKE_BC_LINK_EXECUTABLE)
@@ -1,3 +1,17 @@
# 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.
if (NOT DEFINED CMAKE_BC_COMPILER)
if (NOT "$ENV{LLVM_INSTALL_PREFIX}" STREQUAL "")
message(STATUS "Setting LLVM_INSTALL_PREFIX from the environment variable...")
@@ -1 +1,15 @@
# 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.
set(CMAKE_BC_COMPILER_WORKS 1 CACHE INTERNAL "")
+14
View File
@@ -1,3 +1,17 @@
# 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.
pkg
remill
src
+88 -173
View File
@@ -8,74 +8,63 @@ This document will use the instructions in the following basic block as a runnin
![Sample basic block](images/instruction_life_block.png)
## Step 1: CFG protocol buffer representation
## Decoding instructions
The first step to lifting involves getting the machine code instructions of some binary file into a format the Remill understands. We use a common and simple format so that Remill doesn't need to understand the many executable file formats ([ELF](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format), [Mach-O](https://en.wikipedia.org/wiki/Mach-O), [PE](https://en.wikipedia.org/wiki/Portable_Executable), etc.).
The first step to lifting is to decode the bytes of an instruction. This decoding step takes raw instruction bytes, and turns them into a higher-level [`Instruction`](/remill/Arch/Instruction.h) data structure. This data structure represents the logical operands to the machine code instructions. These operands have a one-to-one correspondence with arguments that will be passed to semantics functions.
The file format used by Remill is a [CFG protocol buffer](CFG_FORMAT.md). This file is produced using plug-in scripts for [Binary Ninja](https://binary.ninja) and [IDA Pro](https://www.hex-rays.com/products/ida). The CFG file for the above code block contains the following data.
Below is a string representation of the data structures representing our example assembly.
```protobuf
Module {
blocks = [
Block {
address = 0x804b7a3;
is_addressable = false;
instructions = [
```lisp
;; mov eax, 1
(X86_INSTR 8048098 5 MOV_GPRv_IMMv_32
(WRITE_OP (REG_32 EAX))
(READ_OP (IMM_32 1)))
// mov eax, 0x1
Instr {
bytes = "\xb8\x01\x00\x00\x00";
address = 0x804b7a3;
},
;; push ebx
(X86_INSTR 804809d 1 PUSH_GPRv_50_32
(READ_OP (REG_32 EBX)))
// push ebx
Instr {
bytes = "\x53";
address = 0x0804b7a8;
},
;; mov ebx, dword ptr [esp + 8]
(X86_INSTR 804809e 4 MOV_GPRv_MEMv_32
(WRITE_OP (REG_32 EBX))
(READ_OP (ADDR_32 DWORD (SEGMENT SS_BASE) ESP + 0x8))))
// mov ebx, dword [esp+0x8]
Instr {
bytes = "\x8b\x5c\x24\x08";
address = 0x0804b7a9;
},
// int 0x80
Instr {
bytes = "\xcd\x80";
address = 0x0804b7ad;
} ]
} ]
named_blocks = [ ];
referenced_blocks = [ ];
}
;; int 0x80
(X86_INSTR 80480a2 2 INT_IMMb
(READ_OP (IMM_8 80)))
```
## Step 2: Creating LLVM functions for each `Block`
## From architecture-specific to architecture-neutral
Remill's unit of translation is a [basic block](https://en.wikipedia.org/wiki/Basic_block). Whereas [McSema](https://github.com/trailofbits/mcsema) translates a function in a binary into an LLVM bitcode function, Remill translates each basic block of code into an LLVM bitcode function.
Decoded instructions must be lifted into a compatible function. Compatible functions are clones of the [`__remill_basic_block`](/remill/Arch/X86/Runtime/BasicBlock.cpp) function. The `__remill_basic_block` function is special because it defines local variables that "point into" the [`State`](/remill/Arch/X86/Runtime/State.h)) structure, which represents the machine's register state.
The translator creates an LLVM function for every `Block` in the CFG protocol buffer's `Module` message. In this example, Remill will create the function `__remill_sub_804b7a3`. This function will be a copy of the [`__remill_basic_block`](https://github.com/trailofbits/remill/blob/master/remill/Arch/X86/Runtime/BasicBlock.cpp) function. What this means is that all of the code and variable definitions within `__remill_basic_block` will also be in `__remill_sub_804b7a3`.
### The `__remill_basic_block` function
The `__remill_basic_block` function is very important. There are *a lot* of variables defined in this function, and the comments are mildy useful. For now, we will focus on the variable definitions that relate to machine code registers. Specifically, we will look at how the translator maps `ebx` from `push ebx` into `EBX`.
The following is an example of the `__remill_basic_block` function for X86.
```C++
// Method that will implement a basic block. We will clone this method for
// each basic block in the code being lifted.
[[gnu::used]]
void __remill_basic_block(State &state, Memory &memory, addr_t curr_pc) {
// Instructions will be lifted into clones of this function.
Memory *__remill_basic_block(Memory *memory, State &state, addr_t curr_pc) {
...
auto &EAX = state.gpr.rax.dword;
auto &EBX = state.gpr.rbx.dword;
auto &EBX = state.gpr.rbx.dword;\
auto &ESP = state.gpr.rsp.dword;
...
auto &SS_BASE = zero;
...
// Lifted code will be placed here in clones versions of this function.
return memory;
}
```
Remill is designed to be able to translate arbitrary machine code to LLVM bitcode. As of this writing, only the x86 and x64 architectures are supported. Nevertheless, there needs to be a kind of "common language" that the architecture-neutral LLVM side of things, and the architecture-specific semantics functions and machine instruction decoders can use to negotiate the translation process. This common language is variable names within the `__remill_basic_block` function.
In the case of the `push ebx` instruction from our example block, our decoder understands that `ebx` is a register. Surprisingly, the lifting side of Remill has no concept of what `ebx` is! Remill is designed to be able to translate arbitrary machine code to LLVM bitcode. To that end, there needs to be a kind of "common language" that the architecture-neutral LLVM side of things, and the architecture-specific semantics functions and machine instruction decoders can use to negotiate the translation process. This common language is variable names within the `__remill_basic_block` function. The instruction decoder ensures that decoded register names correspond to variables defined in `__remill_basic_block`. The programmer implementing `__remill_basic_block` ensures the same things. The conversion from `Instruction` data structures to LLVM bitcode _assumes_ that this correspondence exists.
Ideally, we don't want the LLVM translation side to understand machine registers. And yet, if you take a look at the [`Instruction`](/remill/Arch/Instruction.h) data structure, then you will see something like the following:
Let's hammer this home. If we scroll up, we see that the `Instruction` data structure corresponding to `push ebx` has `EBX` in one of its `(READ_OP (REG_32 EBX))` register operands. This operand corresponds to the following `Register` data structure.
```C++
class Register {
@@ -86,84 +75,23 @@ class Register {
};
```
If we look deep into the bowels of [`DecodeRegister`](/remill/Arch/X86/Arch.cpp#L406), the function that pulls out architecture-specific register operand information from [Intel's XED](https://software.intel.com/en-us/articles/xed-x86-encoder-decoder-software-library), we find that that a register operand can be built as follows:
The decoder initialized the `name` field with `"EBX"`, and the lifter can look up the variable name `EBX` in any cloned copies of `__remill_basic_block`.
## What does lifted code look like?
In spirit, the lifted code for the instructions in our running example looks like the following C++ code.
```C++
if (xed_operand_read(xedo)) {
...
op.reg = ReadOp(reg); // Returns `EBX`.
instr->operands.push_back(op);
}
```
In the case of the `push ebx` instruction from our example block, `ReadOp` will actually return the string `"EBX"`. Recall that each basic block of machine code is lifted into an LLVM function, and that function is a clone of `__remill_basic_block`. This means that the LLVM function implementing the basic block will have `EBX` defined. So, the translator doesn't actually know what `ebx` is, it just understands variable names, and the instruction decoder will produce variable names for registers, knowing that those variables are defined in `__remill_basic_block`, and therefore will be available within all of its clones.
## Step 3: Calling semantics functions for each `Instr`
The previous section described how the architecture-specific instruction decoder can communicate things like registers to the higher-level translation system. The decoder tells the translator to look for specific variables representing register values, knowing that those variables will be available to each block function.
The "[How to add a new instruction](ADD_AN_INSTRUCTION.md)" document describes the different between *SEM*s, functions implementing instruction semantics, and *ISEL*s, specialisations of those functions that are specific to an instruction form or encoding. The instruction decoder knows about the names of *ISEL*s, and it uses these names to [direct the translator](/remill/Arch/Instruction.h#L79-L80) to find and call the right function for each instruction being lifted.
The [operands](/remill/Arch/Instruction.h#L123) of the instruction data structure tell the translator how to find and compute values to pass as arguments to the semantics functions. In the last section, we showed how the `ebx` register in `push ebx` is represented as a register operand, whose variable name is `EBX`. The translator loads this value and passes it as an argument to the [`PUSH_GPRv_32`](/remill/Arch/X86/Semantics/PUSH.cpp#L28) *ISEL* function, as implemented by the [`PUSH`](/remill/Arch/X86/Semantics/PUSH.cpp#L17-L20) *SEM* function.
Here's what the unoptimised bitcode for the example basic block looks like:
```llvm
; Function Attrs: alwaysinline inlinehint nounwind
define private fastcc void @__remill_sub_804b7a3(%struct.State* dereferenceable(3200) %state, %struct.Memory* nonnull %memory, i32 %pc) #7 {
%MEMORY = alloca %struct.Memory*, align 4
store %struct.Memory* %memory, %struct.Memory** %MEMORY, align 4
%1 = getelementptr inbounds %struct.State, %struct.State* %state, i32 0, i32 5, i32 33
%2 = bitcast %union.Flags* %1 to i32*
%3 = getelementptr inbounds %struct.State, %struct.State* %state, i32 0, i32 5, i32 1
%4 = bitcast %union.Flags* %3 to i32*
%5 = getelementptr inbounds %struct.State, %struct.State* %state, i32 0, i32 5, i32 3
%6 = bitcast %union.Flags* %5 to i32*
%7 = getelementptr inbounds %struct.State, %struct.State* %state, i32 0, i32 5, i32 3
%8 = bitcast %union.Flags* %7 to i32*
%9 = getelementptr inbounds %struct.State, %struct.State* %state, i32 0, i32 5, i32 13
%10 = bitcast %union.Flags* %9 to i32*
%11 = getelementptr inbounds %struct.State, %struct.State* %state, i32 0, i32 4, i32 1
%12 = add i32 %pc, 5
store i32 %12, i32* %2, align 4
call void @void (anonymous namespace)::MOV<RnW<unsigned int>, In<unsigned int> >(State&, Memory*&, RnW<unsigned int>, In<unsigned int>)(%struct.State* nonnull %state, %struct.Memory** nonnull %MEMORY, i32* %4, i32 1)
%13 = load i32, i32* %2, align 4
%14 = add i32 %13, 1
store i32 %14, i32* %2, align 4
%15 = load i32, i32* %6, align 4
call void @void (anonymous namespace)::PUSH<unsigned int>(State&, Memory*&, unsigned int)(%struct.State* nonnull %state, %struct.Memory** nonnull %MEMORY, i32 %15)
%16 = load i32, i32* %2, align 4
%17 = add i32 %16, 4
store i32 %17, i32* %2, align 4
%18 = load i32, i32* %10, align 4
%19 = load i16, i16* %11, align 2
%20 = zext i16 %19 to i32
%21 = add i32 %18, 8
%22 = call i32 @__remill_compute_address(i32 %21, i32 %20)
call void @void (anonymous namespace)::MOV<RnW<unsigned int>, Mn<unsigned int> >(State&, Memory*&, RnW<unsigned int>, Mn<unsigned int>)(%struct.State* nonnull %state, %struct.Memory** nonnull %MEMORY, i32* %8, i32 %22)
%23 = load i32, i32* %2, align 4
%24 = add i32 %23, 2
store i32 %24, i32* %2, align 4
call void @INT_IMMb(%struct.State* nonnull %state, %struct.Memory** nonnull %MEMORY, i32 128)
%25 = load i32, i32* %2, align 4
%26 = load %struct.Memory*, %struct.Memory** %MEMORY, align 4
musttail call fastcc void @__remill_interrupt_call(%struct.State* nonnull %state, %struct.Memory* %26, i32 %25)
ret void
}
```
Yikes! That is a lot of code. Here's what it would look like if it was written directly in C++:
```C++
void __remill_sub_804b7a3(State *state, Memory *memory, addr_t pc) {
void __remill_sub_804b7a3(Memory *memory, State *state, addr_t pc) {
auto &EIP = state.gpr.rip.dword;
auto &EAX = state.gpr.rax.dword;
auto &EBX = state.gpr.rbx.dword;
auto &ESP = state.gpr.rsp.dword;
// mov eax, 0x1
pc += 5;
EIP = pc;
// mov eax, 0x1
EIP += 5;
MOV<R32W, I32>(state, &memory, &EAX, 1);
// push ebx
@@ -178,69 +106,59 @@ void __remill_sub_804b7a3(State *state, Memory *memory, addr_t pc) {
EIP += 2;
INT_IMMb<I8>(state, &memory, 0x80);
return __remill_interrupt_call(state, memory, EIP)
return __remill_interrupt_call(memory, state, EIP)
}
```
This is somewhat better. We can see that some of the mechanics of instructions happen outside of the instruction semantics function calls. Specifically, we see the program counter incremented before each semantics function call.
Earlier this documented mentioned that there is a one-to-one correspondence between operands in the `Instruction` data structure and arguments to semantics functions. We can see that with the `MOV` instruction.
There `State *state` parameter to `__remill_sub_804b7a3` holds all [machine register state](/remill/Arch/X86/Runtime/State.h). The `Memory *memory` parameter is curious. It is passed to each semantics function by pointer, implying that it is updated within. The `Memory` type is never actually defined, but it is crucial to Remill's [memory model](https://en.wikipedia.org/wiki/Memory_model_(programming)) and enforcing [memory ordering](https://en.wikipedia.org/wiki/Memory_ordering). We can see more of this when we apply and [inlining](https://en.wikipedia.org/wiki/Inline_expansion) optimisation.
The data structure of `mov ebx, dword [esp+0x8]` was:
## Step 4: Optimizing the bitcode
```lisp
;; mov ebx, dword ptr [esp + 8]
(X86_INSTR 804809e 4 MOV_GPRv_MEMv_32
(WRITE_OP (REG_32 EBX))
(READ_OP (ADDR_32 DWORD (SEGMENT SS_BASE) ESP + 0x8))))
```
```llvm
; Function Attrs: alwaysinline inlinehint nounwind
define private fastcc void @__remill_sub_804b7a3(%struct.State* dereferenceable(3200) %state, %struct.Memory* nonnull %memory, i32 %pc) #8 {
%1 = getelementptr inbounds %struct.State, %struct.State* %state, i64 0, i32 5, i32 33
%2 = bitcast %union.Flags* %1 to i32*
%3 = getelementptr inbounds %struct.State, %struct.State* %state, i64 0, i32 5, i32 1
%4 = bitcast %union.Flags* %3 to i32*
%5 = getelementptr inbounds %struct.State, %struct.State* %state, i64 0, i32 5, i32 3
%6 = bitcast %union.Flags* %5 to i32*
%7 = getelementptr inbounds %struct.State, %struct.State* %state, i64 0, i32 5, i32 13
%8 = bitcast %union.Flags* %7 to i32*
%9 = getelementptr inbounds %struct.State, %struct.State* %state, i64 0, i32 4, i32 1
store i32 1, i32* %4, align 4
%10 = load i32, i32* %6, align 4
%11 = load i32, i32* %8, align 8
%12 = add i32 %11, -4
%13 = load i16, i16* %9, align 2
%14 = zext i16 %13 to i32
%15 = tail call i32 @__remill_compute_address(i32 %12, i32 %14) #15
%16 = tail call %struct.Memory* @__remill_write_memory_32(%struct.Memory* nonnull %memory, i32 %15, i32 %10) #15
store i32 %12, i32* %8, align 4
%17 = add i32 %11, 4
%18 = tail call i32 @__remill_compute_address(i32 %17, i32 %14)
%19 = tail call i32 @__remill_read_memory_32(%struct.Memory* %16, i32 %18) #15
store i32 %19, i32* %6, align 4
%20 = add i32 %pc, 12
store i32 %20, i32* %2, align 4
%21 = getelementptr inbounds %struct.State, %struct.State* %state, i64 0, i32 9
store volatile i32 128, i32* %21, align 4
%22 = getelementptr inbounds %struct.State, %struct.State* %state, i64 0, i32 12
store volatile i8 1, i8* %22, align 2
%23 = load i32, i32* %2, align 4
musttail call fastcc void @__remill_interrupt_call(%struct.State* nonnull %state, %struct.Memory* %16, i32 %23)
ret void
The semantics function implementing the `mov` instruction is:
```c++
template <typename D, typename S>
DEF_SEM(MOV, D dst, const S src) {
WriteZExt(dst, Read(src));
return memory;
}
```
After inlining, the bitcode starts to show off its memory model. Again, lets see what things would look like if we translated this code into C++.
The `(REG_32 EBX)` corresponds with the `dst` parameter, and the `(ADDR_32 DWORD (SEGMENT SS_BASE) ESP + 0x8))` corresponds with the `src` paremter. What links the `Instruction` data structure and the `MOV` semantics function together is the weird `MOV_GPRv_MEMv_32` name that we see. This name is stored in the `Instruction::function` field. The lifting code "discovers" the `MOV` semantics function via this name, and that association is made using the following special syntax.
```c++
DEF_ISEL(MOV_GPRv_MEMv_32) = MOV<R32W, M32>;
```
You can head on over to the [how to add an instruction](ADD_AN_INSTRUCTION.md) document to better understand the meaning of `DEF_SEM` and `DEF_ISEL`.
## We must go deeper
The spiritual lifted code makes one function call per lifted instruction, where the actual implementation of each function can be arbitrarily complex. If we optimize the bitcode that Remill produces for our few example instructions, then what we get, if translated back to C++, looks like the following.
```C++
void __remill_sub_804b7a3(State *state, Memory *memory, addr_t pc) {
void __remill_sub_804b7a3(Memory *memory, State *state, addr_t pc) {
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 = state.seg.ss;
auto &SS_BASE = 0;
// mov eax, 0x1
EAX = 1;
// push ebx
ESP -= 4;
addr_t push_addr = __remill_compute_address(ESP, SS);
addr_t push_addr = ESP + SS_BASE;
memory = __remill_write_memory_32(memory, push_addr, EBX);
// mov ebx, dword [esp+0x8]
@@ -248,39 +166,36 @@ void __remill_sub_804b7a3(State *state, Memory *memory, addr_t pc) {
EBX = __remill_read_memory_32(memory, read_addr);
// int 0x80
state.interrupt_vector = 0x80; // This is a wart. See Issue #53.
state.hyper_call = AsyncHyperCall::kX86IntN;
state.interrupt_vector = 0x80;
EIP = pc + 12;
return __remill_interrupt_call(state, memory, EIP)
return __remill_async_hyper_call(state, memory, EIP)
}
```
### The Remill runtime exposed
## The Remill runtime exposed
In the case of Remill, we want to be explicit about accesses to the "modelled program's memory", and to the "runtime's memory". Take another look above at the optimized bitcode. We can see many `load` and `store` instructions. These instruction's are LLVM's way of representing memory reads and writes. Conceptually, the `State` structure is not part of a program's memory -- if you ran the program natively, then our `State` structure would not be present. In Remill, we consider the "runtime" to be made of the following:
1. Memory accesses to `alloca`'d space: local variables needed to support more elaborate computations.
2. Memory accesses to the `State` structure. The `State` structure is passed by pointer, so it must be accessed via `load` and `store` instructions.
3. All of the various intrinsics, e.g. `__remill_interrupt_call`, `__remill_read_memory_32`, etc.
3. All of the various intrinsics, e.g. `__remill_async_hyper_call`, `__remill_read_memory_32`, etc.
The naming of "runtime" does not mean that the bitcode itself needs to be executed, nor does it mean that the intrinsics must be implemented in any specific way. Remill's intrinsics provide semantic value (in terms of their naming). A user of Remill bitcode can do anything they want with these intrinsics. For example, they could convert all of the memory intrinsics into LLVM `load` and `store` instructions.
Anyway, back to the memory model and memory ordering. Notice that the `memory` pointer is passed into every memory access intrinsic. The `memory` pointer is also replaced in the case of memory write intrinsics. This is to enforce a total order across all memory writes, and a partial order between memory reads and writes.
The `__remill_interrupt_call` and `__remill_compute_address` intrinsics are examples of lower level abstractions that cannot be directly modelled by Remill.
The `__remill_compute_address` is used to model [memory segmentation](https://en.wikipedia.org/wiki/Memory_segmentation#x86_architecture). In x86 (32-bit) code, segmentation is widespread -- this intrinsic rarely shows up in x64 code. Memory segmentation is supported by the [local](https://en.wikipedia.org/wiki/Global_Descriptor_Table#Local_Descriptor_Table) and [global](https://en.wikipedia.org/wiki/Global_Descriptor_Table) descriptor tables.
The `__remill_interrupt_call` instruction instructs the "runtime" that an explicit [interrupt](https://en.wikipedia.org/wiki/Interrupt) (`int 0x80`) happens. Again, Remill has no way of knowing what this actually means or how it works -- that falls under the purview of the operating system kernel. What Remill *does* know is that an interrupt is a kind of ["indirect" control flow](https://en.wikipedia.org/wiki/Indirect_branch), and so it models is like all other indirect control flows.
The `__remill_async_hyper_call` instruction instructs the "runtime" that an explicit [interrupt](https://en.wikipedia.org/wiki/Interrupt) (`int 0x80`) happens. Again, Remill has no way of knowing what this actually means or how it works -- that falls under the purview of the operating system kernel. What Remill *does* know is that an interrupt is a kind of ["indirect" control flow](https://en.wikipedia.org/wiki/Indirect_branch), and so it models is like all other indirect control flows.
All Remill control-flow intrinsics and Remill lifted basic block functions share the same argument structure:
1. A pointer to the `State` structure.
2. A pointer to the opaque `Memory` structure.
1. A pointer to the opaque `Memory` structure.
2. A pointer to the `State` structure.
3. The program counter on entry to the lifted basic block.
In the case of the `__remill_interrupt_call`, the third argument, the program counter address, is computed to be the address following the `int 0x80` instruction.
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.
## Concluding remarks
+672
View File
@@ -0,0 +1,672 @@
/*
* 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 <gflags/gflags.h>
#include <glog/logging.h>
#include <algorithm>
#include <cctype>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <llvm/ADT/Triple.h>
#include <llvm/IR/Attributes.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Module.h>
#include "remill/Arch/AArch64/Decode.h"
#include "remill/Arch/Arch.h"
#include "remill/Arch/Instruction.h"
#include "remill/Arch/Name.h"
#include "remill/BC/Version.h"
#include "remill/OS/OS.h"
namespace remill {
namespace {
//template <uint32_t from_high, uint32_t to_low>
//static inline uint32_t Extract(uint32_t inst) {
// auto num_bits = (from_high - to_low) + 1U;
// return (inst >> to_low) & ((1U << num_bits) - 1U);
//}
template <uint32_t bit, typename T>
static inline T Select(T val) {
return (val >> bit) & T(1);
}
Instruction::Category InstCategory(const aarch64::InstData &inst) {
switch (inst.iclass) {
case aarch64::InstName::INVALID:
return Instruction::kCategoryInvalid;
// TODO(pag): B.cond.
case aarch64::InstName::B:
if (aarch64::InstForm::B_ONLY_CONDBRANCH == inst.iform) {
return Instruction::kCategoryConditionalBranch;
} else {
return Instruction::kCategoryDirectJump;
}
case aarch64::InstName::BR:
return Instruction::kCategoryIndirectJump;
case aarch64::InstName::CBZ:
case aarch64::InstName::CBNZ:
case aarch64::InstName::TBZ:
case aarch64::InstName::TBNZ:
return Instruction::kCategoryConditionalBranch;
case aarch64::InstName::BL:
return Instruction::kCategoryDirectFunctionCall;
case aarch64::InstName::BLR:
return Instruction::kCategoryIndirectFunctionCall;
case aarch64::InstName::RET:
return Instruction::kCategoryFunctionReturn;
case aarch64::InstName::HLT:
return Instruction::kCategoryError;
case aarch64::InstName::HVC:
case aarch64::InstName::SMC:
case aarch64::InstName::SVC:
case aarch64::InstName::SYS: // Has aliases `IC`, `DC`, `AT`, and `TLBI`.
case aarch64::InstName::SYSL:
return Instruction::kCategoryAsyncHyperCall;
case aarch64::InstName::NOP:
return Instruction::kCategoryNoOp;
// Note: These are implemented with synchronous hyper calls.
case aarch64::InstName::BRK:
return Instruction::kCategoryNormal;
default:
return Instruction::kCategoryNormal;
}
}
class AArch64Arch : public Arch {
public:
AArch64Arch(OSName os_name_, ArchName arch_name_);
virtual ~AArch64Arch(void);
void PrepareModule(llvm::Module *mod) const override;
// Decode an instruction.
bool DecodeInstruction(
uint64_t address, const std::string &instr_bytes,
Instruction &inst) const override;
private:
AArch64Arch(void) = delete;
};
AArch64Arch::AArch64Arch(OSName os_name_, ArchName arch_name_)
: Arch(os_name_, arch_name_) {}
AArch64Arch::~AArch64Arch(void) {}
void AArch64Arch::PrepareModule(llvm::Module *mod) const {
std::string dl;
llvm::Triple triple("aarch64-unknown-unknown-");
switch (os_name) {
case kOSLinux:
triple.setOS(llvm::Triple::Linux);
switch (arch_name) {
case kArchAArch64LittleEndian:
triple.setArch(llvm::Triple::aarch64);
dl = "e-m:e-i64:64-i128:128-n32:64-S128";
break;
default:
LOG(FATAL)
<< "Cannot prepare AArch64 module for architecture "
<< GetArchName(arch_name);
break;
}
break;
default:
LOG(FATAL)
<< "Cannot prepare module for AArch64 code on OS "
<< GetOSName(os_name);
break;
}
mod->setDataLayout(dl);
mod->setTargetTriple(triple.normalize());
// Go and remove compile-time attributes added into the semantics. These
// can screw up later compilation. We purposefully compile semantics with
// things like auto-vectorization disabled so that it keeps the bitcode
// to a simpler subset of the available LLVM instruction set. If/when we
// compile this bitcode back into machine code, we may want to use those
// features, and clang will complain if we try to do so if these metadata
// remain present.
auto &context = mod->getContext();
llvm::AttributeSet target_attribs;
target_attribs = target_attribs.addAttribute(
context, llvm::AttributeSet::FunctionIndex, "target-features");
target_attribs = target_attribs.addAttribute(
context, llvm::AttributeSet::FunctionIndex, "target-cpu");
for (llvm::Function &func : *mod) {
auto attribs = func.getAttributes();
attribs = attribs.removeAttributes(
context, llvm::AttributeSet::FunctionIndex, target_attribs);
func.setAttributes(attribs);
}
}
enum RegClass {
kRegX,
kRegW
};
static const char * const kPrefixX = "X\0X\0X\0X\0X\0X\0X\0X\0X\0X\0X\0X\0X\0X"
"\0X\0X\0X\0X\0X\0X\0X\0X\0X\0X\0X\0X\0X"
"\0X\0X\0X\0\0\0\0\0";
static const char * const kPrefixW = "WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW";
static const char *RegPrefix(RegClass size, uint32_t number) {
return kRegX == size ? &(kPrefixX[number * 2]) : &(kPrefixW[number]);
}
static const char * const kNumberName[] = {
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
"20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
"LP", "SP"
};
using RegNum = uint8_t;
static const char *RegNumberName(RegNum number) {
CHECK_LE(number, 31U);
return kNumberName[number];
}
static std::string RegName(RegClass rclass, RegNum number) {
std::stringstream ss;
ss << RegPrefix(rclass, number);
ss << RegNumberName(number);
return ss.str();
}
static size_t ReadRegSize(RegClass rclass) {
switch (rclass) {
case kRegX:
return 64;
case kRegW:
return 32;
}
}
static size_t WriteRegSize(RegClass rclass) {
switch (rclass) {
case kRegX:
case kRegW:
return 64;
}
}
enum Action {
kActionRead,
kActionWrite,
kActionReadWrite
};
static Operand::Register Reg(Action action, RegClass rclass, RegNum reg_num) {
Operand::Register reg;
if (kActionWrite == action) {
reg.name = RegName(rclass, reg_num);
reg.size = WriteRegSize(rclass);
} else if (kActionRead == action) {
reg.name = RegName(rclass, reg_num);
reg.size = ReadRegSize(rclass);
} else {
LOG(FATAL)
<< "Reg function only takes a simple read or write action.";
}
return reg;
}
static void AddRegOperand(Instruction &inst, Action action,
RegClass rclass, RegNum reg_num) {
Operand op;
op.type = Operand::kTypeRegister;
if (kActionWrite == action || kActionReadWrite == action) {
op.reg = Reg(kActionWrite, rclass, reg_num);
op.size = op.reg.size;
op.action = Operand::kActionWrite;
inst.operands.push_back(op);
}
if (kActionRead == action || kActionReadWrite == action) {
op.reg = Reg(kActionRead, rclass, reg_num);
op.size = op.reg.size;
op.action = Operand::kActionRead;
inst.operands.push_back(op);
}
}
static void AddNextPC(Instruction &inst) {
Operand op;
op.type = Operand::kTypeAddress;
op.size = 64;
op.addr.address_size = 64;
op.addr.base_reg.name = "PC";
op.addr.base_reg.size = 64;
op.addr.displacement = 4;
op.action = Operand::kActionRead;
op.addr.kind = Operand::Address::kAddressCalculation;
inst.operands.push_back(op);
}
// Base+offset memory operands are equivalent to indexing into an array.
//
// We have something like this:
// [<Xn|SP>, #<imm>]
//
// Which gets is:
// addr = Xn + imm
// ... deref addr and do stuff ...
static void AddBasePlusOffsetMemOp(Instruction &inst, Action action,
uint64_t access_size,
RegNum base_reg, uint64_t disp) {
Operand op;
op.type = Operand::kTypeAddress;
op.size = access_size;
op.addr.address_size = 64;
op.addr.base_reg = Reg(kActionRead, kRegX, base_reg);
op.addr.displacement = disp;
if (kActionWrite == action || kActionReadWrite == action) {
op.action = Operand::kActionWrite;
op.addr.kind = Operand::Address::kMemoryWrite;
inst.operands.push_back(op);
}
if (kActionRead == action || kActionReadWrite == action) {
op.action = Operand::kActionRead;
op.addr.kind = Operand::Address::kMemoryRead;
inst.operands.push_back(op);
}
}
// Pre-index memory operands write back the result of the displaced address
// to the base register.
//
// We have something like this:
// [<Xn|SP>, #<imm>]!
//
// Which gets us:
// addr = Xn + imm
// ... deref addr and do stuff ...
// Xn = addr + imm
//
// So we add in two operands: one that is a register write operand for Xn,
// the other that is the value of (Xn + imm + imm).
static void AddPreIndexMemOp(Instruction &inst, Action action,
uint64_t access_size,
RegNum base_reg, uint64_t disp) {
AddBasePlusOffsetMemOp(inst, action, access_size, base_reg, disp);
auto addr_op = inst.operands[inst.operands.size() - 1];
Operand reg_op;
reg_op.type = Operand::kTypeRegister;
reg_op.action = Operand::kActionWrite;
reg_op.reg = Reg(kActionWrite, kRegX, base_reg);
reg_op.size = reg_op.reg.size;
inst.operands.push_back(reg_op);
addr_op.addr.kind = Operand::Address::kAddressCalculation;
addr_op.addr.address_size = 64;
addr_op.addr.base_reg = Reg(kActionRead, kRegX, base_reg);
addr_op.addr.displacement *= 2;
inst.operands.push_back(addr_op);
}
// Post-index memory operands write back the result of the displaced address
// to the base register.
//
// We have something like this:
// [<Xn|SP>], #<imm>
//
// Which gets us:
// addr = Xn
// ... deref addr and do stuff ...
// Xn = addr + imm
//
// So we add in two operands: one that is a register write operand for Xn,
// the other that is the value of (Xn + imm).
static void AddPostIndexMemOp(Instruction &inst, Action action,
uint64_t access_size,
RegNum base_reg, uint64_t disp) {
AddBasePlusOffsetMemOp(inst, action, access_size, base_reg, 0);
auto addr_op = inst.operands[inst.operands.size() - 1];
Operand reg_op;
reg_op.type = Operand::kTypeRegister;
reg_op.action = Operand::kActionWrite;
reg_op.reg = Reg(kActionWrite, kRegX, base_reg);
reg_op.size = reg_op.reg.size;
inst.operands.push_back(reg_op);
addr_op.addr.kind = Operand::Address::kAddressCalculation;
addr_op.addr.address_size = 64;
addr_op.addr.base_reg = Reg(kActionRead, kRegX, base_reg);
addr_op.addr.displacement = disp;
inst.operands.push_back(addr_op);
}
enum {
kInstructionSize = 4 // Number of bytes in an instruction.
};
bool AArch64Arch::DecodeInstruction(
uint64_t address, const std::string &inst_bytes,
Instruction &inst) const {
aarch64::InstData dinst = {};
auto bytes = reinterpret_cast<const uint8_t *>(inst_bytes.data());
inst.arch_name = arch_name;
inst.pc = address;
inst.next_pc = address + kInstructionSize;
if (kInstructionSize != inst_bytes.size()) {
inst.function = "INVALID_SIZE";
return false;
} else if (0 != (address % kInstructionSize)) {
inst.function = "INVALID_UNALIGNED";
return false;
} else if (!aarch64::TryExtract(bytes, dinst)) {
inst.function = "INVALID_ENCODING";
return false;
}
inst.category = InstCategory(dinst);
inst.function = aarch64::InstFormToString(dinst.iform);
if (!aarch64::TryDecode(dinst, inst)) {
inst.category = Instruction::kCategoryInvalid;
return false;
}
return true;
}
} // namespace
namespace aarch64 {
// RET {<Xn>}
bool TryDecodeRET_64R_BRANCH_REG(const InstData &data, Instruction &inst) {
AddRegOperand(inst, kActionRead, kRegX, data.Rn);
return true;
}
// BLR <Xn>
bool TryDecodeBLR_64_BRANCH_REG(const InstData &data, Instruction &inst) {
AddRegOperand(inst, kActionRead, kRegX, data.Rn);
AddNextPC(inst);
return true;
}
// STP <Wt1>, <Wt2>, [<Xn|SP>, #<imm>]!
bool TryDecodeSTP_32_LDSTPAIR_PRE(const InstData &data, Instruction &inst) {
AddRegOperand(inst, kActionRead, kRegW, data.Rt);
AddRegOperand(inst, kActionRead, kRegW, data.Rt2);
uint64_t offset = static_cast<uint64_t>(data.imm7.simm7);
AddPreIndexMemOp(inst, kActionWrite, 64, data.Rn, offset << 2);
return true;
}
// STP <Xt1>, <Xt2>, [<Xn|SP>, #<imm>]!
bool TryDecodeSTP_64_LDSTPAIR_PRE(const InstData &data, Instruction &inst) {
AddRegOperand(inst, kActionRead, kRegX, data.Rt);
AddRegOperand(inst, kActionRead, kRegX, data.Rt2);
uint64_t offset = static_cast<uint64_t>(data.imm7.simm7);
AddPreIndexMemOp(inst, kActionWrite, 128, data.Rn, offset << 3);
return true;
}
// STP <Wt1>, <Wt2>, [<Xn|SP>], #<imm>
bool TryDecodeSTP_32_LDSTPAIR_POST(const InstData &data, Instruction &inst) {
AddRegOperand(inst, kActionRead, kRegW, data.Rt);
AddRegOperand(inst, kActionRead, kRegW, data.Rt2);
uint64_t offset = static_cast<uint64_t>(data.imm7.simm7);
AddPostIndexMemOp(inst, kActionWrite, 64, data.Rn, offset << 2);
return true;
}
// STP <Xt1>, <Xt2>, [<Xn|SP>], #<imm>
bool TryDecodeSTP_64_LDSTPAIR_POST(const InstData &data, Instruction &inst) {
AddRegOperand(inst, kActionRead, kRegX, data.Rt);
AddRegOperand(inst, kActionRead, kRegX, data.Rt2);
uint64_t offset = static_cast<uint64_t>(data.imm7.simm7);
AddPostIndexMemOp(inst, kActionWrite, 128, data.Rn, offset << 3);
return true;
}
// LDP <Wt1>, <Wt2>, [<Xn|SP>], #<imm>
bool TryDecodeLDP_32_LDSTPAIR_POST(const InstData &data, Instruction &inst) {
AddRegOperand(inst, kActionWrite, kRegW, data.Rt);
AddRegOperand(inst, kActionWrite, kRegW, data.Rt2);
AddPostIndexMemOp(inst, kActionRead, 64, data.Rn,
static_cast<uint64_t>(data.imm7.simm7) << 2);
return true;
}
// LDP <Xt1>, <Xt2>, [<Xn|SP>], #<imm>
bool TryDecodeLDP_64_LDSTPAIR_POST(const InstData &data, Instruction &inst) {
AddRegOperand(inst, kActionWrite, kRegX, data.Rt);
AddRegOperand(inst, kActionWrite, kRegX, data.Rt2);
AddPostIndexMemOp(inst, kActionRead, 128, data.Rn,
static_cast<uint64_t>(data.imm7.simm7) << 3);
return true;
}
// LDP <Wt1>, <Wt2>, [<Xn|SP>, #<imm>]!
bool TryDecodeLDP_32_LDSTPAIR_PRE(const InstData &data, Instruction &inst) {
AddRegOperand(inst, kActionWrite, kRegW, data.Rt);
AddRegOperand(inst, kActionWrite, kRegW, data.Rt2);
AddPreIndexMemOp(inst, kActionRead, 64, data.Rn,
static_cast<uint64_t>(data.imm7.simm7) << 2);
return true;
}
// LDP <Xt1>, <Xt2>, [<Xn|SP>, #<imm>]!
bool TryDecodeLDP_64_LDSTPAIR_PRE(const InstData &data, Instruction &inst) {
AddRegOperand(inst, kActionWrite, kRegX, data.Rt);
AddRegOperand(inst, kActionWrite, kRegX, data.Rt2);
AddPreIndexMemOp(inst, kActionRead, 128, data.Rn,
static_cast<uint64_t>(data.imm7.simm7) << 3);
return true;
}
// LDP <Wt1>, <Wt2>, [<Xn|SP>{, #<imm>}]
bool TryDecodeLDP_32_LDSTPAIR_OFF(const InstData &data, Instruction &inst) {
AddRegOperand(inst, kActionWrite, kRegW, data.Rt);
AddRegOperand(inst, kActionWrite, kRegW, data.Rt2);
AddBasePlusOffsetMemOp(inst, kActionRead, 64, data.Rn,
static_cast<uint64_t>(data.imm7.simm7) << 2);
return true;
}
// LDP <Xt1>, <Xt2>, [<Xn|SP>{, #<imm>}]
bool TryDecodeLDP_64_LDSTPAIR_OFF(const InstData &data, Instruction &inst) {
AddRegOperand(inst, kActionWrite, kRegX, data.Rt);
AddRegOperand(inst, kActionWrite, kRegX, data.Rt2);
AddBasePlusOffsetMemOp(inst, kActionRead, 128, data.Rn,
static_cast<uint64_t>(data.imm7.simm7) << 3);
return true;
}
// LDR <Wt>, [<Xn|SP>{, #<pimm>}]
bool TryDecodeLDR_32_LDST_POS(const InstData &data, Instruction &inst) {
AddRegOperand(inst, kActionWrite, kRegW, data.Rt);
AddBasePlusOffsetMemOp(inst, kActionRead, 32, data.Rn,
data.imm12.uimm << 2);
return true;
}
// LDR <Xt>, [<Xn|SP>{, #<pimm>}]
bool TryDecodeLDR_64_LDST_POS(const InstData &data, Instruction &inst) {
AddRegOperand(inst, kActionWrite, kRegX, data.Rt);
AddBasePlusOffsetMemOp(inst, kActionRead, 64, data.Rn,
data.imm12.uimm << 3);
return true;
}
static void AddPCRegMemOp(Instruction &inst, Action action, uint64_t disp) {
Operand op;
op.type = Operand::kTypeAddress;
op.size = 64;
op.addr.address_size = 64;
op.addr.base_reg.name = "PC";
op.addr.base_reg.size = 64;
op.addr.displacement = disp;
if (kActionRead == action) {
op.action = Operand::kActionRead;
op.addr.kind = Operand::Address::kMemoryRead;
} else if (kActionWrite == action) {
op.action = Operand::kActionWrite;
op.addr.kind = Operand::Address::kMemoryWrite;
} else {
LOG(FATAL)
<< "AddPCRegMemOp only accepts simple operand actions.";
}
inst.operands.push_back(op);
}
// LDR <Wt>, <label>
bool TryDecodeLDR_32_LOADLIT(const InstData &data, Instruction &inst) {
AddRegOperand(inst, kActionWrite, kRegW, data.Rt);
AddPCRegMemOp(inst, kActionRead,
static_cast<uint64_t>(data.imm19.simm19) << 2ULL);
return false;
}
// LDR <Xt>, <label>
bool TryDecodeLDR_64_LOADLIT(const InstData &data, Instruction &inst) {
AddRegOperand(inst, kActionWrite, kRegX, data.Rt);
AddPCRegMemOp(inst, kActionRead,
static_cast<uint64_t>(data.imm19.simm19) << 2ULL);
return true;
}
// LDR LDR_32_ldst_regoff:
// 0 x Rt 0
// 1 x Rt 1
// 2 x Rt 2
// 3 x Rt 3
// 4 x Rt 4
// 5 x Rn 0
// 6 x Rn 1
// 7 x Rn 2
// 8 x Rn 3
// 9 x Rn 4
// 10 0
// 11 1
// 12 x S 0
// 13 x option 0
// 14 x option 1
// 15 x option 2
// 16 x Rm 0
// 17 x Rm 1
// 18 x Rm 2
// 19 x Rm 3
// 20 x Rm 4
// 21 1
// 22 1 opc 0
// 23 0 opc 1
// 24 0
// 25 0
// 26 0 V 0
// 27 1
// 28 1
// 29 1
// 30 0 size 0
// 31 1 size 1
// LDR <Wt>, [<Xn|SP>, (<Wm>|<Xm>){, <extend> {<amount>}}]
bool TryDecodeLDR_32_LDST_REGOFF(const InstData &, Instruction &) {
return false;
}
// LDR LDR_64_ldst_regoff:
// 0 x Rt 0
// 1 x Rt 1
// 2 x Rt 2
// 3 x Rt 3
// 4 x Rt 4
// 5 x Rn 0
// 6 x Rn 1
// 7 x Rn 2
// 8 x Rn 3
// 9 x Rn 4
// 10 0
// 11 1
// 12 x S 0
// 13 x option 0
// 14 x option 1
// 15 x option 2
// 16 x Rm 0
// 17 x Rm 1
// 18 x Rm 2
// 19 x Rm 3
// 20 x Rm 4
// 21 1
// 22 1 opc 0
// 23 0 opc 1
// 24 0
// 25 0
// 26 0 V 0
// 27 1
// 28 1
// 29 1
// 30 1 size 0
// 31 1 size 1
// LDR <Xt>, [<Xn|SP>, (<Wm>|<Xm>){, <extend> {<amount>}}]
bool TryDecodeLDR_64_LDST_REGOFF(const InstData &, Instruction &) {
return false;
}
} // namespace aarch64
// TODO(pag): We pretend that these are singletons, but they aren't really!
const Arch *Arch::GetAArch64(
OSName os_name_, ArchName arch_name_) {
return new AArch64Arch(os_name_, arch_name_);
}
} // namespace remill
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+136
View File
@@ -0,0 +1,136 @@
/*
* 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 "remill/Arch/AArch64/Runtime/State.h"
extern "C" {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-variable"
// Instructions will be lifted into clones of this function.
[[gnu::used]] Memory *__remill_basic_block(Memory *memory, State &state,
addr_t curr_pc) {
bool branch_taken = false;
// Note: These variables MUST be defined for all architectures.
auto &STATE = state;
auto &MEMORY = *memory;
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 &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 &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 &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 &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 &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 &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 &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 &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 &FP = state.gpr.X29.qword;
auto &WFP = state.gpr.X29.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;
// Lifted code will be placed here in clones versions of this function.
return memory;
}
#pragma clang diagnostic pop
} // extern C
#include "remill/Arch/Runtime/Intrinsics.cpp"
@@ -0,0 +1,44 @@
# 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.
cmake_minimum_required (VERSION 3.2)
project(arm_runtime BC)
set(ARMRUNTIME_SOURCEFILES
Instructions.cpp
BasicBlock.cpp
)
set_source_files_properties(Instructions.cpp PROPERTIES COMPILE_FLAGS "-O3 -g0")
set_source_files_properties(BasicBlock.cpp PROPERTIES COMPILE_FLAGS "-O0 -g3")
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} SOURCES ${ARMRUNTIME_SOURCEFILES} ADDRESS_SIZE ${address_bit_size})
target_include_directories(${target_name} PRIVATE ${ARMRUNTIME_INCLUDEDIRECTORIES})
if (little_endian)
target_compile_definitions(${target_name} PRIVATE "LITTLE_ENDIAN=1")
else ()
target_compile_definitions(${target_name} PRIVATE "LITTLE_ENDIAN=0")
endif ()
endfunction ()
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
# add_runtime_helper(aarch64be 64 0)
add_runtime_helper(aarch64 64 1)
endif ()
@@ -0,0 +1,108 @@
/*
* 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 "remill/Arch/Runtime/Intrinsics.h"
#include "remill/Arch/Runtime/Operators.h"
#include <fenv.h>
#include <algorithm>
#include <bitset>
#include <cmath>
#include "remill/Arch/AArch64/Runtime/State.h"
#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_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_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_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_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 FLAG_Z state.state.Z // Zero flag.
#define FLAG_S state.state.S // Sign flag.
#define FLAG_C state.state.C // Carry flag.
#define FLAG_V state.state.V // Overflow.
#define FLAG_N state.state.N // Negative.
#define HYPER_CALL state.hyper_call
#define INTERRUPT_VECTOR state.interrupt_vector
namespace {
// Takes the place of an unsupported instruction.
DEF_SEM(HandleUnsupported) {
return __remill_sync_hyper_call(
memory, state, SyncHyperCall::kAArch64EmulateInstruction);
}
// Takes the place of an invalid instruction.
DEF_SEM(HandleInvalidInstruction) {
HYPER_CALL = AsyncHyperCall::kInvalidInstruction;
return memory;
}
} // namespace
// Takes the place of an unsupported instruction.
DEF_ISEL(UNSUPPORTED_INSTRUCTION) = HandleUnsupported;
DEF_ISEL(INVALID_INSTRUCTION) = HandleInvalidInstruction;
#include "remill/Arch/AArch64/Semantics/FLAGS.cpp"
#include "remill/Arch/AArch64/Semantics/BINARY.cpp"
#include "remill/Arch/AArch64/Semantics/BRANCH.cpp"
#include "remill/Arch/AArch64/Semantics/CALL_RET.cpp"
#include "remill/Arch/AArch64/Semantics/DATAXFER.cpp"
#include "remill/Arch/AArch64/Semantics/MISC.cpp"
+37
View File
@@ -0,0 +1,37 @@
/*
* 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 REMILL_ARCH_AARCH64_RUNTIME_OPERATORS_H_
#define REMILL_ARCH_AARCH64_RUNTIME_OPERATORS_H_
namespace {
// Read a register directly. Sometimes this is needed for suppressed operands.
ALWAYS_INLINE static IF_64BIT_ELSE(uint64_t, uint32_t)
_Read(Memory *, Reg reg) {
return reg.IF_64BIT_ELSE(qword, dword);
}
// Write directly to a register. This is sometimes needed for suppressed
// register operands.
ALWAYS_INLINE static void _Write(Memory *, Reg &reg,
IF_64BIT_ELSE(uint64_t, uint32_t) val) {
reg.aword = val;
}
} // namespace
#endif // REMILL_ARCH_AARCH64_RUNTIME_OPERATORS_H_
+218
View File
@@ -0,0 +1,218 @@
/*
* 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 REMILL_ARCH_AARCH64_RUNTIME_STATE_H_
#define REMILL_ARCH_AARCH64_RUNTIME_STATE_H_
#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;)
} __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`.");)
struct alignas(16) GPR final {
// Prevents LLVM from casting a `GPR` into an `i64` to access `rax`.
volatile uint64_t _0;
Reg X0;
volatile uint64_t _1;
Reg X1;
volatile uint64_t _2;
Reg X2;
volatile uint64_t _3;
Reg X3;
volatile uint64_t _4;
Reg X4;
volatile uint64_t _5;
Reg X5;
volatile uint64_t _6;
Reg X6;
volatile uint64_t _7;
Reg X7;
volatile uint64_t _8;
Reg X8;
volatile uint64_t _9;
Reg X9;
volatile uint64_t _10;
Reg X10;
volatile uint64_t _11;
Reg X11;
volatile uint64_t _12;
Reg X12;
volatile uint64_t _13;
Reg X13;
volatile uint64_t _14;
Reg X14;
volatile uint64_t _15;
Reg X15;
volatile uint64_t _16;
Reg X16;
volatile uint64_t _17;
Reg X17;
volatile uint64_t _18;
Reg X18;
volatile uint64_t _19;
Reg X19;
volatile uint64_t _20;
Reg X20;
volatile uint64_t _21;
Reg X21;
volatile uint64_t _22;
Reg X22;
volatile uint64_t _23;
Reg X23;
volatile uint64_t _24;
Reg X24;
volatile uint64_t _25;
Reg X25;
volatile uint64_t _26;
Reg X26;
volatile uint64_t _27;
Reg X27;
volatile uint64_t _28;
Reg X28;
volatile uint64_t _29;
Reg X29;
volatile uint64_t _30;
Reg X30;
// Reg 31 is called zero registers;
volatile uint64_t _31;
Reg X31; // Stack pointer.
// Program counter of the CURRENT instruction!
volatile uint64_t _32;
Reg PC; // Program counter.
} __attribute__((packed));
static_assert(528 == sizeof(GPR), "Invalid structure packing of `GPR`.");
union alignas(8) NativeProcState 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 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 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.
} __attribute__((packed));
} __attribute__((packed));
static_assert(8 == sizeof(NativeProcState),
"Invalid structure packing of `NativeProcState`.");
struct alignas(8) ProcState final {
uint8_t _0;
uint8_t N; // Negative condition flag.
uint8_t _1;
uint8_t Z; // Zero condition flag
uint8_t _2;
uint8_t C; // Carry condition flag
uint8_t _3;
uint8_t 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]
} __attribute__((packed));
static_assert(40 == sizeof(ProcState),
"Invalid packing of `struct ProcState`");
struct alignas(16) State final : public ArchState {
NativeProcState native_state; // 8 bytes.
ProcState state; // 40 bytes.
GPR gpr; // 528 bytes.
} __attribute__((packed));
static_assert((576 + 16) == sizeof(State),
"Invalid packing of `struct State`");
#pragma clang diagnostic pop
#endif // REMILL_ARCH_AARCH64_RUNTIME_STATE_H_
+58
View File
@@ -0,0 +1,58 @@
/*
* Types.h
*
* Created on: May 9, 2017
* Author: akshayk
*/
#ifndef REMILL_ARCH_AARCH64_RUNTIME_TYPES_H_
#define REMILL_ARCH_AARCH64_RUNTIME_TYPES_H_
// We need this for boolean conditions, used in branch instructions.
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
// can be accessed is 32 bits.
typedef RnW<uint64_t> R32W;
typedef RnW<uint64_t> R64W;
typedef Rn<uint8_t> R8;
typedef Rn<uint16_t> R16;
typedef Rn<uint32_t> R32;
typedef Rn<uint64_t> R64;
typedef RVn<vec32_t> V32;
typedef RVn<vec64_t> V64;
typedef RVnW<IF_64BIT_ELSE(vec64_t, vec32_t)> V32W;
typedef RVnW<vec64_t> V64W;
typedef MnW<uint8_t> M8W;
typedef MnW<uint16_t> M16W;
typedef MnW<uint32_t> M32W;
typedef MnW<uint64_t> M64W;
typedef MVnW<vec32_t> MV32W;
typedef MVnW<vec64_t> MV64W;
typedef MVnW<vec128_t> MV128W;
typedef Mn<uint8_t> M8;
typedef Mn<uint16_t> M16;
typedef Mn<uint32_t> M32;
typedef Mn<uint64_t> M64;
typedef MVn<vec32_t> MV32;
typedef MVn<vec64_t> MV64;
typedef MVn<vec128_t> MV128;
typedef In<uint32_t> I32;
typedef In<uint64_t> I64;
typedef In<addr_t> PC;
typedef In<addr_t> ADDR;
#endif /* REMILL_ARCH_AARCH64_RUNTIME_TYPES_H_ */
+74
View File
@@ -0,0 +1,74 @@
/*
* 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.
*/
namespace {
template <typename D, typename S1, typename S2>
DEF_SEM(SUB, D dst, S1 src1, S2 src2) {
WriteZExt(dst, USub(Read(src1), Read(src2)));
return memory;
}
template <typename D, typename S1, typename S2>
DEF_SEM(ADD, D dst, S1 src1, S2 src2) {
WriteZExt(dst, UAdd(Read(src1), Read(src2)));
return memory;
}
template <typename D, typename S1, typename S2>
DEF_SEM(ASR, D dst, S1 src1, S2 src2) {
WriteZExt(dst, Unsigned(SShr(Signed(Read(src1)), Signed(Read(src2)))));
return memory;
}
template <typename D, typename S1, typename S2>
DEF_SEM(EOR_REG, D dst, S1 src1, S2 src2) {
WriteZExt(dst, UXor(Read(src1), Read(src2)));
return memory;
}
} // 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(ASR_R64W_R64_U) = ASR<R64W, R64, I64>;
DEF_ISEL(EOR_R64W_R64_R64) = EOR_REG<R64W, R64, R64>;
namespace {
template <typename S1, typename S2>
DEF_SEM(CMP, S1 src1, S2 src2) {
auto lhs = Read(src1);
auto rhs = Read(src2);
auto res = USub(lhs, rhs);
FLAG_Z = ZeroFlag(res);
FLAG_N = SignFlag(res);
FLAG_V = Overflow<tag_sub>::Flag(lhs, rhs, res);
FLAG_C = Carry<tag_sub>::Flag(lhs, rhs, res);
return memory;
}
DEF_ISEL(CMP_XFL_R64_R64) = CMP<R64, R64>;
DEF_ISEL(CMP_XFL_R64_U) = CMP<R64, I64>;
} // namespace
+145
View File
@@ -0,0 +1,145 @@
/*
* 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.
*/
namespace {
static inline bool CondGE(const State &state) {
return UCmpNeq(state.state.GE, 0);
}
static inline bool CondLT(const State &state) {
return !CondGE(state);
}
static inline bool CondEQ(const State &state) {
return UCmpNeq(state.state.Z, 0);
}
static inline bool CondGT(const State &state) {
return CondGE(state) && !CondEQ(state);
}
static inline bool CondLE(const State &state) {
return CondLT(state) || CondEQ(state);
}
static inline bool CondCS(const State &state) {
return UCmpNeq(state.state.C, 0);
}
static inline bool CondMI(const State &state) {
return UCmpNeq(state.state.N, 0);
}
static inline bool CondVS(const State &state) {
return UCmpNeq(state.state.V, 0);
}
static inline bool CondHI(const State &state) {
return CondCS(state) && !CondEQ(state);
}
template <bool (*check_cond)(const State &)>
static bool NotCond(const State &state) {
return !check_cond(state);
}
} // namespace
DEF_COND(GE) = CondGE;
DEF_COND(GT) = CondGT;
DEF_COND(LE) = CondLE;
DEF_COND(LT) = CondLT;
DEF_COND(EQ) = CondEQ;
DEF_COND(NE) = NotCond<CondEQ>;
DEF_COND(CS) = CondCS;
DEF_COND(CC) = NotCond<CondCS>;
DEF_COND(MI) = CondMI;
DEF_COND(PL) = NotCond<CondMI>;
DEF_COND(VS) = CondVS;
DEF_COND(VC) = NotCond<CondVS>;
DEF_COND(HI) = CondHI;
DEF_COND(LS) = NotCond<CondHI>;
namespace {
DEF_SEM(DoDirectBranch, PC target_pc) {
Write(REG_PC, Read(target_pc));
return memory;
}
DEF_SEM(DoIndirectBranch, PC dst) {
Write(REG_PC, Read(dst));
return memory;
}
template <bool (*check_cond)(const State &)>
DEF_SEM(DirectCondBranch, R8W cond, PC taken, PC not_taken) {
addr_t taken_pc = Read(taken);
addr_t not_taken_pc = Read(not_taken);
uint8_t take_branch = check_cond(state);
Write(cond, take_branch);
Write(REG_PC, Select<addr_t>(take_branch, taken_pc, not_taken_pc));
return memory;
}
template <typename S>
DEF_SEM(CBZ, R8W cond, S src, PC taken, PC not_taken) {
addr_t taken_pc = Read(taken);
addr_t not_taken_pc = Read(not_taken);
uint8_t take_branch = UCmpEq(Read(src), 0);
Write(cond, take_branch);
Write(REG_PC, Select<addr_t>(take_branch, taken_pc, not_taken_pc));
return memory;
}
template <typename S>
DEF_SEM(CBNZ, R8W cond, S src, PC taken, PC not_taken) {
addr_t taken_pc = Read(taken);
addr_t not_taken_pc = Read(not_taken);
uint8_t take_branch = UCmpNeq(Read(src), 0);
Write(cond, take_branch);
Write(REG_PC, Select<addr_t>(take_branch, taken_pc, not_taken_pc));
return memory;
}
} // namespace
DEF_ISEL(B_U) = DoDirectBranch;
DEF_ISEL(B_LS_R8W_U_U) = DirectCondBranch<NotCond<CondHI>>;
DEF_ISEL(B_EQ_R8W_U_U) = DirectCondBranch<CondEQ>;
DEF_ISEL(B_NE_R8W_U_U) = DirectCondBranch<NotCond<CondEQ>>;
DEF_ISEL(B_GE_R8W_U_U) = DirectCondBranch<CondGE>;
DEF_ISEL(B_GT_R8W_U_U) = DirectCondBranch<CondGT>;
DEF_ISEL(B_LE_R8W_U_U) = DirectCondBranch<CondLE>;
DEF_ISEL(B_LT_R8W_U_U) = DirectCondBranch<CondLT>;
DEF_ISEL(BR_R64) = DoIndirectBranch;
DEF_ISEL(CBZ_R8W_R64_U_U) = CBZ<R64>;
DEF_ISEL(CBZ_R8W_R32_U_U) = CBZ<R32>;
DEF_ISEL(CBNZ_R8W_R64_U_U) = CBNZ<R64>;
DEF_ISEL(CBNZ_R8W_R32_U_U) = CBNZ<R32>;
@@ -0,0 +1,33 @@
/*
* 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.
*/
namespace {
DEF_SEM(CALL, R64 target_addr, PC ret_addr) {
Write(REG_LP, Read(ret_addr));
Write(REG_PC, Read(target_addr));
return memory;
}
DEF_SEM(RET, R64 target_pc) {
Write(REG_PC, Read(target_pc));
return memory;
}
} // namespace
DEF_ISEL(RET_64R_BRANCH_REG) = RET;
DEF_ISEL(BLR_64_BRANCH_REG) = CALL;
+112
View File
@@ -0,0 +1,112 @@
/*
* 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.
*/
namespace {
DEF_SEM(StorePairUpdateIndex32, R32 src1, R32 src2, MV64W dst_mem,
R64W dst_reg, ADDR next_addr) {
uint32v2_t vec = {};
vec = UInsertV32(vec, 0, Read(src1));
vec = UInsertV32(vec, 1, Read(src2));
UWriteV32(dst_mem, vec);
Write(dst_reg, Read(next_addr));
return memory;
}
DEF_SEM(StorePairUpdateIndex64, R64 src1, R64 src2, MV128W dst_mem,
R64W dst_reg, ADDR next_addr) {
uint64v2_t vec = {};
vec = UInsertV64(vec, 0, Read(src1));
vec = UInsertV64(vec, 1, Read(src2));
UWriteV64(dst_mem, vec);
Write(dst_reg, Read(next_addr));
return memory;
}
} // namespace
DEF_ISEL(STP_32_LDSTPAIR_PRE) = StorePairUpdateIndex32;
DEF_ISEL(STP_32_LDSTPAIR_POS) = StorePairUpdateIndex32;
DEF_ISEL(STP_64_LDSTPAIR_PRE) = StorePairUpdateIndex64;
DEF_ISEL(STP_64_LDSTPAIR_POS) = StorePairUpdateIndex64;
namespace {
DEF_SEM(LoadPairUpdateIndex32, R32W dst1, R32W dst2, MV64 src_mem,
R64W dst_reg, ADDR next_addr) {
auto vec = UReadV32(src_mem);
WriteZExt(dst1, UExtractV32(vec, 0));
WriteZExt(dst2, UExtractV32(vec, 1));
Write(dst_reg, Read(next_addr));
return memory;
}
DEF_SEM(LoadPairUpdateIndex64, R64W dst1, R64W dst2, MV128 src_mem,
R64W dst_reg, ADDR next_addr) {
auto vec = UReadV64(src_mem);
Write(dst1, UExtractV64(vec, 0));
Write(dst2, UExtractV64(vec, 1));
Write(dst_reg, Read(next_addr));
return memory;
}
} // namespace
DEF_ISEL(LDP_32_LDSTPAIR_PRE) = LoadPairUpdateIndex32;
DEF_ISEL(LDP_32_LDSTPAIR_POST) = LoadPairUpdateIndex32;
DEF_ISEL(LDP_64_LDSTPAIR_PRE) = LoadPairUpdateIndex64;
DEF_ISEL(LDP_64_LDSTPAIR_POST) = LoadPairUpdateIndex64;
namespace {
DEF_SEM(LoadPair32, R32W dst1, R32W dst2, MV64 src_mem) {
auto vec = UReadV32(src_mem);
WriteZExt(dst1, UExtractV32(vec, 0));
WriteZExt(dst2, UExtractV32(vec, 1));
return memory;
}
DEF_SEM(LoadPair64, R64W dst1, R64W dst2, MV128 src_mem) {
auto vec = UReadV64(src_mem);
Write(dst1, UExtractV64(vec, 0));
Write(dst2, UExtractV64(vec, 1));
return memory;
}
} // namespace
DEF_ISEL(LDP_32_LDSTPAIR_OFF) = LoadPair32;
DEF_ISEL(LDP_64_LDSTPAIR_OFF) = LoadPair64;
namespace {
template <typename T>
DEF_SEM(Load, R64W dst, T src) {
WriteZExt(dst, Read(src));
return memory;
}
} // namespace
DEF_ISEL(LDR_32_LDST_POS) = Load<M32>;
DEF_ISEL(LDR_64_LDST_POS) = Load<M64>;
DEF_ISEL(LDR_32_LOADLIT) = Load<M32>;
DEF_ISEL(LDR_64_LOADLIT) = Load<M64>;
+166
View File
@@ -0,0 +1,166 @@
/*
* 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.
*/
namespace {
// Used to select specializations of flags computations based on what operator
// is executed.
enum : uint32_t {
kLHS = 2415899639U,
kRHS = 70623199U
};
// Zero flags, tells us whether or not a value is zero.
template <typename T>
[[gnu::const]]
ALWAYS_INLINE static bool ZeroFlag(T res) {
return T(0) == res;
}
// Zero flags, tells us whether or not a value is zero.
template <typename T>
[[gnu::const]]
ALWAYS_INLINE static bool NotZeroFlag(T res) {
return T(0) != res;
}
// Sign flag, tells us if a result is signed or unsigned.
template <typename T>
[[gnu::const]]
ALWAYS_INLINE static bool SignFlag(T res) {
return 0 > Signed(res);
}
// Tests whether there is an even number of bits in the low order byte.
[[gnu::const]]
ALWAYS_INLINE static bool ParityFlag(uint8_t r0) {
return !__builtin_parity(static_cast<unsigned>(r0));
// auto r1 = r0 >> 1_u8;
// auto r2 = r1 >> 1_u8;
// auto r3 = r2 >> 1_u8;
// auto r4 = r3 >> 1_u8;
// auto r5 = r4 >> 1_u8;
// auto r6 = r5 >> 1_u8;
// auto r7 = r6 >> 1_u8;
//
// return !(1 & (r0 ^ r1 ^ r2 ^ r3 ^ r4 ^ r5 ^ r6 ^ r7));
}
struct tag_add {};
struct tag_sub {};
struct tag_div {};
struct tag_mul {};
// Generic overflow flag.
template <typename T>
struct Overflow;
// Computes an overflow flag when two numbers are added together.
template <>
struct Overflow<tag_add> {
template <typename T>
[[gnu::const]]
ALWAYS_INLINE static bool Flag(T lhs, T rhs, T res) {
static_assert(std::is_unsigned<T>::value,
"Invalid specialization of `Overflow::Flag` for addition.");
enum {
kSignShift = sizeof(T) * 8 - 1
};
const T sign_lhs = lhs >> kSignShift;
const T sign_rhs = rhs >> kSignShift;
const T sign_res = res >> kSignShift;
return 2 == (sign_lhs ^ sign_res) + (sign_rhs ^ sign_res);
}
};
// Computes an overflow flag when one number is subtracted from another.
template <>
struct Overflow<tag_sub> {
template <typename T>
[[gnu::const]]
ALWAYS_INLINE static bool Flag(T lhs, T rhs, T res) {
static_assert(std::is_unsigned<T>::value,
"Invalid specialization of `Overflow::Flag` for "
"subtraction.");
enum {
kSignShift = sizeof(T) * 8 - 1
};
const T sign_lhs = lhs >> kSignShift;
const T sign_rhs = rhs >> kSignShift;
const T sign_res = res >> kSignShift;
return 2 == (sign_lhs ^ sign_rhs) + (sign_lhs ^ sign_res);
}
};
// Computes an overflow flag when one number is multiplied with another.
template <>
struct Overflow<tag_mul> {
// Integer multiplication overflow check, where result is twice the width of
// the operands.
template <typename T, typename R>
[[gnu::const]]
ALWAYS_INLINE static bool Flag(
T, T, R res,
typename std::enable_if<sizeof(T) < sizeof(R),int>::type=0) {
return static_cast<R>(static_cast<T>(res)) != res;
}
// Signed integer multiplication overflow check, where the result is
// truncated to the size of the operands.
template <typename T>
[[gnu::const]]
ALWAYS_INLINE static bool Flag(
T lhs, T rhs, T,
typename std::enable_if<std::is_signed<T>::value,int>::type=0) {
auto lhs_wide = SExt(lhs);
auto rhs_wide = SExt(rhs);
return Flag<T, decltype(lhs_wide)>(lhs, rhs, lhs_wide * rhs_wide);
}
};
// Generic carry flag.
template <typename Tag>
struct Carry;
// Computes an carry flag when two numbers are added together.
template <>
struct Carry<tag_add> {
template <typename T>
[[gnu::const]]
ALWAYS_INLINE static bool Flag(T lhs, T rhs, T res) {
static_assert(std::is_unsigned<T>::value,
"Invalid specialization of `Carry::Flag` for addition.");
return res < lhs || res < rhs;
}
};
// Computes an carry flag when one number is subtracted from another.
template <>
struct Carry<tag_sub> {
template <typename T>
[[gnu::const]]
ALWAYS_INLINE static bool Flag(T lhs, T rhs, T) {
static_assert(std::is_unsigned<T>::value,
"Invalid specialization of `Carry::Flag` for addition.");
return lhs < rhs;
}
};
} // namespace
@@ -14,31 +14,24 @@
* limitations under the License.
*/
#ifndef REMILL_ARCH_X86_ARCH_H_
#define REMILL_ARCH_X86_ARCH_H_
namespace {
#include "remill/Arch/Arch.h"
DEF_SEM(DoNOP) {
return memory;
}
namespace remill {
// In the case of remill, the `addr` is already shifted and/or masked.
// In the case of McSema, the address is already resolved and the cross-
// reference information includes the masking of the high or low bits
// so that the result mirrors the goal.
DEF_SEM(AddToAddress, R64W dst, I64 addr) {
Write(dst, Read(addr));
return memory;
}
class X86Arch : public Arch {
public:
X86Arch(OSName os_name_, ArchName arch_name_);
} // namespace
virtual ~X86Arch(void);
DEF_ISEL(NOP) = DoNOP;
void PrepareModule(llvm::Module *mod) const override;
uint64_t ProgramCounter(const ArchState *state) const override;
// Decode an instruction.
Instruction *DecodeInstruction(
uint64_t address, const std::string &instr_bytes) const override;
private:
X86Arch(void) = delete;
};
} // namespace remill
#endif // REMILL_ARCH_X86_ARCH_H_
DEF_ISEL(ADRP_R64W_U) = AddToAddress;
DEF_ISEL(ADR_R64W_U) = AddToAddress;
+44 -6
View File
@@ -14,8 +14,8 @@
* limitations under the License.
*/
#include <glog/logging.h>
#include <gflags/gflags.h>
#include <glog/logging.h>
#include <memory>
#include <unordered_map>
@@ -24,9 +24,11 @@
#include "remill/Arch/Name.h"
#include "remill/OS/OS.h"
DEFINE_string(arch, "", "Architecture of the code being translated. "
"Valid architectures: x86, amd64 (with or without "
"`_avx` or `_avx512` appended).");
DEFINE_string(arch, "",
"Architecture of the code being translated. "
"Valid architectures: x86, amd64 (with or without "
"`_avx` or `_avx512` appended), aarch64, "
"mips32, mips64");
DECLARE_string(os);
@@ -36,16 +38,18 @@ namespace {
static unsigned AddressSize(ArchName arch_name) {
switch (arch_name) {
case kArchInvalid:
LOG(FATAL)
<< "Cannot get address size for invalid arch.";
LOG(FATAL) << "Cannot get address size for invalid arch.";
return 0;
case kArchX86:
case kArchX86_AVX:
case kArchX86_AVX512:
case kArchMips32:
return 32;
case kArchAMD64:
case kArchAMD64_AVX:
case kArchAMD64_AVX512:
case kArchMips64:
case kArchAArch64LittleEndian:
return 64;
}
}
@@ -71,6 +75,16 @@ const Arch *Arch::Get(OSName os_name_, ArchName arch_name_) {
LOG(FATAL) << "Unrecognized architecture.";
return nullptr;
case kArchAArch64LittleEndian: {
static ArchCache gArchAArch64LE;
auto &arch = gArchAArch64LE[os_name_];
if (!arch) {
DLOG(INFO) << "Using architecture: AArch64, feature set: Little Endian";
arch = ArchPtr(GetAArch64(os_name_, arch_name_));
}
return arch.get();
}
case kArchX86: {
static ArchCache gArchX86;
auto &arch = gArchX86[os_name_];
@@ -81,6 +95,26 @@ const Arch *Arch::Get(OSName os_name_, ArchName arch_name_) {
return arch.get();
}
case kArchMips32: {
static ArchCache gArchMips;
auto &arch = gArchMips[os_name_];
if (!arch) {
DLOG(INFO) << "Using architecture: 32-bit MIPS";
arch = ArchPtr(GetMips(os_name_, arch_name_));
}
return arch.get();
}
case kArchMips64: {
static ArchCache gArchMips64;
auto &arch = gArchMips64[os_name_];
if (!arch) {
DLOG(INFO) << "Using architecture: 64-bit MIPS";
arch = ArchPtr(GetMips(os_name_, arch_name_));
}
return arch.get();
}
case kArchX86_AVX: {
static ArchCache gArchX86_AVX;
auto &arch = gArchX86_AVX[os_name_];
@@ -134,6 +168,10 @@ const Arch *Arch::Get(OSName os_name_, ArchName arch_name_) {
return nullptr;
}
const Arch *Arch::GetMips(OSName, ArchName) {
return nullptr;
}
const Arch *GetGlobalArch(void) {
return Arch::Get(GetOSName(FLAGS_os), GetArchName(FLAGS_arch));
}
+9 -6
View File
@@ -46,11 +46,9 @@ class Arch {
virtual void PrepareModule(llvm::Module *mod) const = 0;
// Decode an instruction.
virtual Instruction *DecodeInstruction(
uint64_t address,
const std::string &instr_bytes) const = 0;
virtual uint64_t ProgramCounter(const ArchState *state) const = 0;
virtual bool DecodeInstruction(
uint64_t address, const std::string &instr_bytes,
Instruction &inst) const = 0;
// Number of bits in an address.
const OSName os_name;
@@ -61,10 +59,15 @@ class Arch {
Arch(OSName os_name_, ArchName arch_name_);
private:
// Defined in `remill/Arch/X86/Arch.cpp`.
static const Arch *GetX86(OSName os, ArchName arch_name);
// Defined in `remill/Arch/Mips/Arch.cpp`.
static const Arch *GetMips(OSName os, ArchName arch_name);
// Defined in `remill/Arch/AArch64/Arch.cpp`.
static const Arch *GetAArch64(OSName os, ArchName arch_name);
Arch(void) = delete;
};
+76 -1
View File
@@ -27,6 +27,12 @@ namespace remill {
Operand::Register::Register(void)
: size(0) {}
Operand::ShiftRegister::ShiftRegister(void)
: shift_size(0),
extract_size(0),
shift_op(Operand::ShiftRegister::kShiftInvalid),
extend_op(Operand::ShiftRegister::kExtendInvalid) {}
Operand::Immediate::Immediate(void)
: val(0),
is_signed(false) {}
@@ -64,6 +70,52 @@ std::string Operand::Debug(void) const {
ss << "(REG_" << reg.size << " " << reg.name << ")";
break;
case Operand::kTypeShiftRegister:
ss << "(REG_" << reg.size << " ";
switch (shift_reg.extend_op) {
case Operand::ShiftRegister::kExtendInvalid:
ss << reg.name;
break;
case Operand::ShiftRegister::kExtendSigned:
ss << "sext(" << reg.name << "[" << (shift_reg.extract_size - 1)
<< ":0])";
break;
case Operand::ShiftRegister::kExtendUnsigned:
ss << "zext(" << reg.name << "[" << (shift_reg.extract_size - 1)
<< ":0])";
break;
}
switch (shift_reg.shift_op) {
case Operand::ShiftRegister::kShiftInvalid:
break;
case Operand::ShiftRegister::kShiftLeftWithZeroes:
ss << "<<0";
break;
case Operand::ShiftRegister::kShiftLeftWithOnes:
ss << "<<1";
break;
case Operand::ShiftRegister::kShiftUnsignedRight:
ss << "u>>";
break;
case Operand::ShiftRegister::kShiftSignedRight:
ss << "s>>";
break;
case Operand::ShiftRegister::kShiftRightAround:
ss << "ror";
break;
}
ss << " " << shift_reg.shift_size << ")";
break;
case Operand::kTypeImmediate:
ss << "(";
if (imm.is_signed) {
@@ -123,6 +175,18 @@ Instruction::Instruction(void)
is_atomic_read_modify_write(false),
category(Instruction::kCategoryInvalid) {}
void Instruction::Reset(void) {
pc = 0;
next_pc = 0;
branch_taken_pc = 0;
branch_not_taken_pc = 0;
arch_name = kArchInvalid;
operand_size = 0;
is_atomic_read_modify_write = false;
category = Instruction::kCategoryInvalid;
operands.clear();
}
std::string Instruction::Serialize(void) const {
std::stringstream ss;
ss << "(";
@@ -139,9 +203,20 @@ std::string Instruction::Serialize(void) const {
case kArchX86_AVX512:
ss << "X86_";
break;
case kArchMips32:
ss << "MIPS32_";
break;
case kArchMips64:
ss << "MIPS64_";
break;
case kArchAArch64LittleEndian:
ss << "AArch64_";
break;
}
ss << "INSTR 0x" << std::hex << pc << " "
ss << "INSTR " << std::hex << pc << " "
<< std::dec << (next_pc - pc) << " ";
if (is_atomic_read_modify_write) {
ss << "ATOMIC ";
}
+34 -12
View File
@@ -23,7 +23,6 @@
namespace remill {
class Arch;
class X86Arch;
enum ArchName : unsigned;
@@ -36,6 +35,7 @@ class Operand {
enum Type {
kTypeInvalid,
kTypeRegister,
kTypeShiftRegister,
kTypeImmediate,
kTypeAddress
} type;
@@ -56,9 +56,34 @@ class Operand {
~Register(void) = default;
std::string name;
size_t size;
size_t size; // In bits.
} reg;
class ShiftRegister {
public:
ShiftRegister(void);
Register reg;
uint64_t shift_size;
uint64_t extract_size;
enum Shift : unsigned {
kShiftInvalid,
kShiftLeftWithZeroes, // Shift left, filling low order bits with zero.
kShiftLeftWithOnes, // Shift left, filling low order bits with one.
kShiftUnsignedRight, // Also know as logical shift right.
kShiftSignedRight, // Also know as arithmetic shift right.
kShiftRightAround // Rotate right.
} shift_op;
enum Extend : unsigned {
kExtendInvalid,
kExtendUnsigned,
kExtendSigned,
} extend_op;
} shift_reg;
// kTypeImmediate.
class Immediate {
public:
@@ -86,8 +111,8 @@ class Operand {
Register base_reg;
Register index_reg;
int64_t scale;
int64_t displacement;
uint64_t address_size;
int64_t displacement; // In bytes.
uint64_t address_size; // In bits.
Kind kind;
inline bool IsMemoryAccess(void) const {
@@ -110,12 +135,15 @@ class Operand {
class Instruction {
public:
~Instruction(void) = default;
Instruction(void);
void Reset(void);
// Name of semantics function that implements this instruction.
std::string function;
// The disassembly of this instruction.
std::string disassembly;
// // The disassembly of this instruction.
// std::string disassembly;
// Program counter for this instruction and the next instruction.
uint64_t pc;
@@ -221,14 +249,8 @@ class Instruction {
inline bool IsNoOp(void) const {
return kCategoryNoOp == category;
}
private:
friend class X86Arch;
Instruction(void);
};
} // namespace remill
#endif // REMILL_ARCH_INSTRUCTION_H_
+15 -1
View File
@@ -19,7 +19,6 @@
namespace remill {
ArchName GetArchName(const std::string &arch_name) {
if (arch_name == "x86") {
return kArchX86;
@@ -38,6 +37,15 @@ ArchName GetArchName(const std::string &arch_name) {
} else if (arch_name == "amd64_avx512") {
return kArchAMD64_AVX512;
} else if (arch_name == "aarch64") {
return kArchAArch64LittleEndian;
} else if (arch_name == "mips32") {
return kArchMips32;
} else if (arch_name == "mips64") {
return kArchMips64;
} else {
return kArchInvalid;
}
@@ -59,6 +67,12 @@ std::string GetArchName(ArchName arch_name) {
return "amd64_avx";
case kArchAMD64_AVX512:
return "amd64_avx512";
case kArchMips32:
return "mips32";
case kArchMips64:
return "mips64";
case kArchAArch64LittleEndian:
return "aarch64";
}
}
+8 -1
View File
@@ -23,12 +23,19 @@ namespace remill {
enum ArchName : uint32_t {
kArchInvalid,
kArchX86,
kArchX86_AVX,
kArchX86_AVX512,
kArchAMD64,
kArchAMD64_AVX,
kArchAMD64_AVX512
kArchAMD64_AVX512,
kArchMips32,
kArchMips64,
kArchAArch64LittleEndian
};
// Convert the string name of an architecture into a canonical form.
+6 -2
View File
@@ -50,15 +50,19 @@
#define DEF_ISEL(name) \
extern "C" constexpr auto ISEL_ ## name [[gnu::used]]
// Define a conditional execution function.
#define DEF_COND(name) \
extern "C" constexpr auto COND_ ## name [[gnu::used]]
// Define a semantics implementing function.
#define DEF_SEM(name, ...) \
ALWAYS_INLINE __attribute__((flatten)) static Memory *name ( \
Memory *memory, State &state, ##__VA_ARGS__) noexcept
Memory *memory, State &state, ##__VA_ARGS__)
// Define a semantics implementing function.
#define DEF_HELPER(name, ...) \
ALWAYS_INLINE __attribute__((flatten)) static auto name ( \
Memory *&memory, State &state, ##__VA_ARGS__) noexcept
Memory *&memory, State &state, ##__VA_ARGS__)
// An instruction where the implementation is the same for all operand sizes.
#define DEF_ISEL_ALL(name, func) \
+6
View File
@@ -29,8 +29,14 @@ class SyncHyperCall {
kX86EmulateInstruction,
kAMD64EmulateInstruction,
kMipsEmulateInstruction,
// TODO(pag): How to distinguish litte- and big-endian?
kAArch64EmulateInstruction,
kAssertPrivileged,
kIntegerOverflow // MIPS-specific.
};
} __attribute__((packed));
+41
View File
@@ -621,6 +621,11 @@ auto TruncTo(T val) -> typename IntegerType<DT>::BT {
Write(op, ZExtTo<decltype(op)>(val)); \
} while (false)
#define WriteSExt(op, val) \
do { \
Write(op, SExtTo<decltype(op)>(val)); \
} while (false)
#define SWriteV8(op, val) \
do { \
memory = _SWriteV8(memory, op, (val)); \
@@ -1160,6 +1165,42 @@ addr_t AddressOf(MnW<T> addr) {
return addr.addr;
}
template <typename T>
ALWAYS_INLINE static
addr_t AddressOf(MVn<T> addr) {
return addr.addr;
}
template <typename T>
ALWAYS_INLINE static
addr_t AddressOf(MVnW<T> addr) {
return addr.addr;
}
template <typename T>
ALWAYS_INLINE static
Mn<T> DisplaceAddress(Mn<T> addr, addr_t disp) {
return Mn<T>{addr.addr + disp};
}
template <typename T>
ALWAYS_INLINE static
MnW<T> DisplaceAddress(MnW<T> addr, addr_t disp) {
return MnW<T>{addr.addr + disp};
}
template <typename T>
ALWAYS_INLINE static
MVn<T> DisplaceAddress(MVn<T> addr, addr_t disp) {
return MVn<T>{addr.addr + disp};
}
template <typename T>
ALWAYS_INLINE static
MVnW<T> DisplaceAddress(MVnW<T> addr, addr_t disp) {
return MVnW<T>{addr.addr + disp};
}
template <typename T>
ALWAYS_INLINE static
T Select(bool cond, T if_true, T if_false) {
+158 -141
View File
@@ -17,26 +17,19 @@
#include <glog/logging.h>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <llvm/ADT/Triple.h>
#include <llvm/IR/Attributes.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Module.h>
#undef HAS_FEATURE_AVX
#undef HAS_FEATURE_AVX512
#undef ADDRESS_SIZE_BITS
#define HAS_FEATURE_AVX 1
#define HAS_FEATURE_AVX512 1
#define ADDRESS_SIZE_BITS 64
#include "remill/Arch/Arch.h"
#include "remill/Arch/Instruction.h"
#include "remill/Arch/Name.h"
#include "remill/Arch/X86/Arch.h"
#include "remill/Arch/X86/XED.h"
#include "remill/Arch/X86/Runtime/State.h"
#include "remill/BC/Version.h"
#include "remill/OS/OS.h"
@@ -146,7 +139,7 @@ static bool IsError(const xed_decoded_inst_t *xedd) {
XED_ICLASS_INVALID == iclass;
}
// Return the category of this instruction.
// Return the category of this instuction.
static Instruction::Category CreateCategory(const xed_decoded_inst_t *xedd) {
if (IsError(xedd)) {
return Instruction::kCategoryError;
@@ -262,10 +255,10 @@ std::map<xed_iform_enum_t, xed_iform_enum_t> kUnlockedIform = {
{XED_IFORM_NEG_LOCK_MEMv, XED_IFORM_NEG_MEMv},
};
// Name of this instruction function.
// Name of this instuction function.
static std::string InstructionFunctionName(const xed_decoded_inst_t *xedd) {
// If this instruction is marked as atomic via the `LOCK` prefix then we want
// If this instuction is marked as atomic via the `LOCK` prefix then we want
// to remove it because we will already be surrounding the call to the
// semantics function with the atomic begin/end intrinsics.
auto iform = xed_decoded_inst_get_iform_enum(xedd);
@@ -279,8 +272,8 @@ static std::string InstructionFunctionName(const xed_decoded_inst_t *xedd) {
std::string iform_name = xed_iform_enum_t2str(iform);
ss << iform_name;
// Some instructions are "scalable", i.e. there are variants of the
// instruction for each effective operand size. We represent these in
// Some instuctions are "scalable", i.e. there are variants of the
// instuction for each effective operand size. We represent these in
// the semantics files with `_<size>`, so we need to look up the correct
// selection.
if (xed_decoded_inst_get_attribute(xedd, XED_ATTRIBUTE_SCALABLE)) {
@@ -291,20 +284,20 @@ static std::string InstructionFunctionName(const xed_decoded_inst_t *xedd) {
return ss.str();
}
// Decode an instruction into the XED instruction format.
// Decode an instuction into the XED instuction format.
static bool DecodeXED(xed_decoded_inst_t *xedd,
const xed_state_t *mode,
const std::string &instr_bytes,
const std::string &inst_bytes,
uint64_t address) {
auto num_bytes = instr_bytes.size();
auto bytes = reinterpret_cast<const uint8_t *>(instr_bytes.data());
auto num_bytes = inst_bytes.size();
auto bytes = reinterpret_cast<const uint8_t *>(inst_bytes.data());
xed_decoded_inst_zero_set_mode(xedd, mode);
xed_decoded_inst_set_input_chip(xedd, XED_CHIP_INVALID);
auto err = xed_decode(xedd, bytes, static_cast<uint32_t>(num_bytes));
if (XED_ERROR_NONE != err) {
LOG(ERROR)
<< "Unable to decode instruction at " << std::hex << address
<< "Unable to decode instuction at " << std::hex << address
<< " with error: " << xed_error_enum_t2str(err) << ".";
return false;
}
@@ -365,7 +358,7 @@ static Operand::Register SegBaseRegOp(xed_reg_enum_t reg,
}
// Decode a memory operand.
static void DecodeMemory(Instruction *instr,
static void DecodeMemory(Instruction &inst,
const xed_decoded_inst_t *xedd,
const xed_operand_t *xedo,
int mem_index) {
@@ -379,11 +372,11 @@ static void DecodeMemory(Instruction *instr,
auto scale = xed_decoded_inst_get_scale(xedd, mem_index);
auto base_wide = xed_get_largest_enclosing_register(base);
auto size = xed_decoded_inst_get_operand_width(xedd);
auto instr_size = static_cast<int64_t>(xed_decoded_inst_get_length(xedd));
auto inst_size = static_cast<int64_t>(xed_decoded_inst_get_length(xedd));
// PC-relative memory accesses are relative to the next PC.
if (XED_REG_RIP == base_wide) {
disp += static_cast<int64_t>(instr_size);
disp += static_cast<int64_t>(inst_size);
}
// Deduce the implicit segment register if it is absent.
@@ -395,12 +388,12 @@ static void DecodeMemory(Instruction *instr,
}
// On AMD64, only the `FS` and `GS` segments are non-zero.
if (Is64Bit(instr->arch_name) &&
if (Is64Bit(inst.arch_name) &&
XED_REG_FS != segment &&
XED_REG_GS != segment) {
segment = XED_REG_INVALID;
// AGEN operands, e.g. for the `LEA` instruction, can be marked with an
// AGEN operands, e.g. for the `LEA` instuction, can be marked with an
// explicit segment, but it is ignored.
} else if (XED_OPERAND_AGEN == op_name) {
segment = XED_REG_INVALID;
@@ -412,7 +405,7 @@ static void DecodeMemory(Instruction *instr,
disp += static_cast<int64_t>(size / 8);
}
Operand op;
Operand op = {};
op.size = size;
op.type = Operand::kTypeAddress;
@@ -428,16 +421,16 @@ static void DecodeMemory(Instruction *instr,
// Rename the base register to use `PC` as the register name.
if (XED_REG_RIP == base_wide) {
op.addr.base_reg.name = "PC";
op.addr.displacement += static_cast<int64_t>(instr->NumBytes());
op.addr.displacement += static_cast<int64_t>(inst.NumBytes());
}
// We always pass destination operands first, then sources. Memory operands
// are represented by their addresses, and in the instruction implementations,
// are represented by their addresses, and in the instuction implementations,
// accessed via intrinsics.
if (xed_operand_written(xedo)) {
op.action = Operand::kActionWrite;
op.addr.kind = Operand::Address::kMemoryWrite;
instr->operands.push_back(op);
inst.operands.push_back(op);
}
if (xed_operand_read(xedo)) {
@@ -447,21 +440,21 @@ static void DecodeMemory(Instruction *instr,
} else {
op.addr.kind = Operand::Address::kMemoryRead;
}
instr->operands.push_back(op);
inst.operands.push_back(op);
}
}
// Decode an immediate constant.
static void DecodeImmediate(Instruction *instr,
static void DecodeImmediate(Instruction &inst,
const xed_decoded_inst_t *xedd,
xed_operand_enum_t op_name) {
auto val = 0ULL;
auto is_signed = false;
auto imm_size = xed_decoded_inst_get_immediate_width_bits(xedd);
CHECK(imm_size <= instr->operand_size)
CHECK(imm_size <= inst.operand_size)
<< "Immediate size is greater than effective operand size at "
<< std::hex << instr->pc << ".";
<< std::hex << inst.pc << ".";
if (XED_OPERAND_IMM0SIGNED == op_name ||
xed_operand_values_get_immediate_is_signed(xedd)) {
@@ -481,17 +474,17 @@ static void DecodeImmediate(Instruction *instr,
<< xed_operand_enum_t2str(op_name) << ".";
}
Operand op;
Operand op = {};
op.type = Operand::kTypeImmediate;
op.action = Operand::kActionRead;
op.size = imm_size;
op.imm.is_signed = is_signed;
op.imm.val = val;
instr->operands.push_back(op);
inst.operands.push_back(op);
}
// Decode a register operand.
static void DecodeRegister(Instruction *instr,
static void DecodeRegister(Instruction &inst,
const xed_decoded_inst_t *xedd,
const xed_operand_t *xedo,
xed_operand_enum_t op_name) {
@@ -499,7 +492,7 @@ static void DecodeRegister(Instruction *instr,
CHECK(XED_REG_INVALID != reg)
<< "Cannot get name of invalid register.";
Operand op;
Operand op = {};
op.type = Operand::kTypeRegister;
op.reg = RegOp(reg);
op.size = op.reg.size;
@@ -512,19 +505,19 @@ static void DecodeRegister(Instruction *instr,
// `RAX_write` on 64-bit builds, so we just want to notify that
// the operand size is 64 bits, but the register's width itself
// is still 32.
if (Is64Bit(instr->arch_name)) {
if (Is64Bit(inst.arch_name)) {
if (XED_REG_GPR32_FIRST <= reg && XED_REG_GPR32_LAST > reg) {
op.reg.name[0] = 'R'; // Convert things like `EAX` into `RAX`.
op.size = 64;
op.reg.size = 64;
} else if (XED_REG_XMM_FIRST <= reg && XED_REG_ZMM_LAST >= reg) {
if (kArchAMD64_AVX512 == instr->arch_name) {
if (kArchAMD64_AVX512 == inst.arch_name) {
op.reg.name[0] = 'Z'; // Convert things like `XMM` into `ZMM`.
op.reg.size = 512;
op.size = 512;
} else if (kArchAMD64_AVX == instr->arch_name) {
} else if (kArchAMD64_AVX == inst.arch_name) {
op.reg.name[0] = 'Y'; // Convert things like `XMM` into `YMM`.
op.reg.size = 256;
op.size = 256;
@@ -532,92 +525,92 @@ static void DecodeRegister(Instruction *instr,
}
}
instr->operands.push_back(op);
inst.operands.push_back(op);
}
if (xed_operand_read(xedo)) {
op.action = Operand::kActionRead;
instr->operands.push_back(op);
inst.operands.push_back(op);
}
}
static void DecodeConditionalInterrupt(Instruction *instr) {
static void DecodeConditionalInterrupt(Instruction &inst) {
// Condition variable.
Operand cond_op;
Operand cond_op = {};
cond_op.action = Operand::kActionWrite;
cond_op.type = Operand::kTypeRegister;
cond_op.reg.name = "BRANCH_TAKEN";
cond_op.reg.size = 8;
cond_op.size = 8;
instr->operands.push_back(cond_op);
inst.operands.push_back(cond_op);
}
// Operand representing the fall-through PC, which is the not-taken branch of
// a conditional jump, or the return address for a function call.
static void DecodeFallThroughPC(Instruction *instr,
static void DecodeFallThroughPC(Instruction &inst,
const xed_decoded_inst_t *xedd) {
auto pc_reg = Is64Bit(instr->arch_name) ? XED_REG_RIP : XED_REG_EIP;
auto pc_reg = Is64Bit(inst.arch_name) ? XED_REG_RIP : XED_REG_EIP;
auto pc_width = xed_get_register_width_bits64(pc_reg);
Operand not_taken_op;
Operand not_taken_op = {};
not_taken_op.action = Operand::kActionRead;
not_taken_op.type = Operand::kTypeAddress;
not_taken_op.size = pc_width;
not_taken_op.addr.address_size = pc_width;
not_taken_op.addr.base_reg.name = "PC";
not_taken_op.addr.base_reg.size = pc_width;
not_taken_op.addr.displacement = static_cast<int64_t>(instr->NumBytes());
not_taken_op.addr.displacement = static_cast<int64_t>(inst.NumBytes());
not_taken_op.addr.kind = Operand::Address::kControlFlowTarget;
instr->operands.push_back(not_taken_op);
inst.operands.push_back(not_taken_op);
instr->branch_not_taken_pc = instr->next_pc;
inst.branch_not_taken_pc = inst.next_pc;
}
// Decode a relative branch target.
static void DecodeConditionalBranch(Instruction *instr,
static void DecodeConditionalBranch(Instruction &inst,
const xed_decoded_inst_t *xedd) {
auto pc_reg = Is64Bit(instr->arch_name) ? XED_REG_RIP : XED_REG_EIP;
auto pc_reg = Is64Bit(inst.arch_name) ? XED_REG_RIP : XED_REG_EIP;
auto pc_width = xed_get_register_width_bits64(pc_reg);
auto disp = static_cast<int64_t>(
xed_decoded_inst_get_branch_displacement(xedd));
// Condition variable.
Operand cond_op;
Operand cond_op = {};
cond_op.action = Operand::kActionWrite;
cond_op.type = Operand::kTypeRegister;
cond_op.reg.name = "BRANCH_TAKEN";
cond_op.reg.size = 8;
cond_op.size = 8;
instr->operands.push_back(cond_op);
inst.operands.push_back(cond_op);
// Taken branch.
Operand taken_op;
Operand taken_op = {};
taken_op.action = Operand::kActionRead;
taken_op.type = Operand::kTypeAddress;
taken_op.size = pc_width;
taken_op.addr.address_size = pc_width;
taken_op.addr.base_reg.name = "PC";
taken_op.addr.base_reg.size = pc_width;
taken_op.addr.displacement = disp + static_cast<int64_t>(instr->NumBytes());
taken_op.addr.displacement = disp + static_cast<int64_t>(inst.NumBytes());
taken_op.addr.kind = Operand::Address::kControlFlowTarget;
instr->operands.push_back(taken_op);
inst.operands.push_back(taken_op);
instr->branch_taken_pc = static_cast<uint64_t>(
static_cast<int64_t>(instr->next_pc) + disp);
inst.branch_taken_pc = static_cast<uint64_t>(
static_cast<int64_t>(inst.next_pc) + disp);
DecodeFallThroughPC(instr, xedd);
DecodeFallThroughPC(inst, xedd);
}
// Decode a relative branch target.
static void DecodeRelativeBranch(Instruction *instr,
static void DecodeRelativeBranch(Instruction &inst,
const xed_decoded_inst_t *xedd) {
auto pc_reg = Is64Bit(instr->arch_name) ? XED_REG_RIP : XED_REG_EIP;
auto pc_reg = Is64Bit(inst.arch_name) ? XED_REG_RIP : XED_REG_EIP;
auto pc_width = xed_get_register_width_bits64(pc_reg);
auto disp = static_cast<int64_t>(
xed_decoded_inst_get_branch_displacement(xedd));
// Taken branch.
Operand taken_op;
Operand taken_op = {};
taken_op.action = Operand::kActionRead;
taken_op.action = Operand::kActionRead;
taken_op.type = Operand::kTypeAddress;
@@ -625,33 +618,33 @@ static void DecodeRelativeBranch(Instruction *instr,
taken_op.addr.address_size = pc_width;
taken_op.addr.base_reg.name = "PC";
taken_op.addr.base_reg.size = pc_width;
taken_op.addr.displacement = disp + static_cast<int64_t>(instr->NumBytes());
taken_op.addr.displacement = disp + static_cast<int64_t>(inst.NumBytes());
taken_op.addr.kind = Operand::Address::kControlFlowTarget;
instr->operands.push_back(taken_op);
inst.operands.push_back(taken_op);
instr->branch_taken_pc = static_cast<uint64_t>(
static_cast<int64_t>(instr->next_pc) + disp);
inst.branch_taken_pc = static_cast<uint64_t>(
static_cast<int64_t>(inst.next_pc) + disp);
}
// Decode an operand.
static void DecodeOperand(Instruction *instr,
static void DecodeOperand(Instruction &inst,
const xed_decoded_inst_t *xedd,
const xed_operand_t *xedo) {
switch (auto op_name = xed_operand_name(xedo)) {
case XED_OPERAND_AGEN:
case XED_OPERAND_MEM0:
DecodeMemory(instr, xedd, xedo, 0);
DecodeMemory(inst, xedd, xedo, 0);
break;
case XED_OPERAND_MEM1:
DecodeMemory(instr, xedd, xedo, 1);
DecodeMemory(inst, xedd, xedo, 1);
break;
case XED_OPERAND_IMM0SIGNED:
case XED_OPERAND_IMM0:
case XED_OPERAND_IMM1_BYTES:
case XED_OPERAND_IMM1:
DecodeImmediate(instr, xedd, op_name);
DecodeImmediate(inst, xedd, op_name);
break;
case XED_OPERAND_PTR:
@@ -669,14 +662,14 @@ static void DecodeOperand(Instruction *instr,
case XED_OPERAND_REG6:
case XED_OPERAND_REG7:
case XED_OPERAND_REG8:
DecodeRegister(instr, xedd, xedo, op_name);
DecodeRegister(inst, xedd, xedo, op_name);
break;
case XED_OPERAND_RELBR:
if (Instruction::kCategoryConditionalBranch == instr->category) {
DecodeConditionalBranch(instr, xedd);
if (Instruction::kCategoryConditionalBranch == inst.category) {
DecodeConditionalBranch(inst, xedd);
} else {
DecodeRelativeBranch(instr, xedd);
DecodeRelativeBranch(inst, xedd);
}
break;
@@ -688,13 +681,23 @@ static void DecodeOperand(Instruction *instr,
}
}
} // namespace
class X86Arch : public Arch {
public:
X86Arch(OSName os_name_, ArchName arch_name_);
virtual ~X86Arch(void);
void PrepareModule(llvm::Module *mod) const override;
// Decode an instuction.
bool DecodeInstruction(
uint64_t address, const std::string &inst_bytes,
Instruction &inst) const override;
private:
X86Arch(void) = delete;
};
// TODO(pag): We pretend that these are singletons, but they aren't really!
const Arch *Arch::GetX86(
OSName os_name_, ArchName arch_name_) {
return new X86Arch(os_name_, arch_name_);
}
X86Arch::X86Arch(OSName os_name_, ArchName arch_name_)
: Arch(os_name_, arch_name_) {
@@ -713,63 +716,76 @@ X86Arch::~X86Arch(void) {}
// information for the target architecture.
void X86Arch::PrepareModule(llvm::Module *mod) const {
std::string dl;
std::string triple;
llvm::Triple triple;
// TODO(alessandro): This interface is spilling implementation-specific
// values (i.e.: mips).
switch (os_name) {
case kOSInvalid:
LOG(FATAL) << "Cannot convert module for an unrecognized OS.";
break;
case kOSLinux:
switch (arch_name) {
case kArchInvalid:
LOG(FATAL)
<< "Cannot convert module for an unrecognized architecture.";
break;
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 kArchAMD64:
case kArchAMD64_AVX:
case kArchAMD64_AVX512:
dl = "e-m:e-i64:64-f80:128-n8:16:32:64-S128";
triple = "x86_64-unknown-linux-gnu";
triple.setArch(llvm::Triple::x86_64);
break;
case kArchX86:
case kArchX86_AVX:
case kArchX86_AVX512:
dl = "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128";
triple = "i386-pc-linux-gnu";
triple.setArch(llvm::Triple::x86);
break;
default:
LOG(FATAL)
<< "Cannot prepare module for non-x86 architecture "
<< GetArchName(arch_name);
break;
}
break;
case kOSmacOS:
triple.setOS(llvm::Triple::MacOSX);
triple.setEnvironment(llvm::Triple::UnknownEnvironment);
triple.setVendor(llvm::Triple::Apple);
triple.setObjectFormat(llvm::Triple::MachO);
switch (arch_name) {
case kArchInvalid:
LOG(FATAL)
<< "Cannot convert module for an unrecognized architecture.";
break;
case kArchAMD64:
case kArchAMD64_AVX:
case kArchAMD64_AVX512:
dl = "e-m:o-i64:64-f80:128-n8:16:32:64-S128";
triple = "x86_64-apple-macosx10.10.0";
triple.setArch(llvm::Triple::x86_64);
break;
case kArchX86:
case kArchX86_AVX:
case kArchX86_AVX512:
dl = "e-m:o-p:32:32-f64:32:64-f80:128-n8:16:32-S128";
triple = "i386-apple-macosx10.10.0";
triple.setArch(llvm::Triple::x86);
break;
default:
LOG(FATAL)
<< "Cannot prepare module for non-x86 architecture "
<< GetArchName(arch_name);
}
break;
}
mod->setDataLayout(dl);
mod->setTargetTriple(triple);
mod->setTargetTriple(triple.normalize());
// Go and remove compile-time attributes added into the semantics. These
// can screw up later compilation. We purposefully compile semantics with
// things like auto-vectorization disabled so that it keeps the bitcode
// to a simpler subset of the available LLVM instruction set. If/when we
// to a simpler subset of the available LLVM instuction set. If/when we
// compile this bitcode back into machine code, we may want to use those
// features, and clang will complain if we try to do so if these metadata
// remain present.
@@ -789,76 +805,77 @@ void X86Arch::PrepareModule(llvm::Module *mod) const {
}
}
// Decode an instruction.
Instruction *X86Arch::DecodeInstruction(
// Decode an instuction.
bool X86Arch::DecodeInstruction(
uint64_t address,
const std::string &instr_bytes) const {
const std::string &inst_bytes,
Instruction &inst) const {
xed_decoded_inst_t xedd_;
xed_decoded_inst_t *xedd = &xedd_;
auto mode = 32 == address_size ? &kXEDState32 : &kXEDState64;
auto instr = new Instruction;
if (!DecodeXED(xedd, mode, instr_bytes, address)) {
return instr;
if (!DecodeXED(xedd, mode, inst_bytes, address)) {
return false;
}
instr->arch_name = arch_name;
instr->operand_size = xed_decoded_inst_get_operand_width(xedd);
instr->function = InstructionFunctionName(xedd);
instr->category = CreateCategory(xedd);
instr->pc = address;
instr->next_pc = address + xed_decoded_inst_get_length(xedd);
inst.arch_name = arch_name;
inst.operand_size = xed_decoded_inst_get_operand_width(xedd);
inst.function = InstructionFunctionName(xedd);
inst.category = CreateCategory(xedd);
inst.pc = address;
inst.next_pc = address + xed_decoded_inst_get_length(xedd);
// Wrap an instruction in atomic begin/end if it accesses memory with RMW
// Wrap an instuction in atomic begin/end if it accesses memory with RMW
// semantics or with a LOCK prefix.
if (xed_operand_values_get_atomic(xedd) ||
xed_operand_values_has_lock_prefix(xedd)) {
instr->is_atomic_read_modify_write = true;
inst.is_atomic_read_modify_write = true;
}
if (Instruction::kCategoryConditionalAsyncHyperCall == instr->category) {
DecodeConditionalInterrupt(instr);
if (Instruction::kCategoryConditionalAsyncHyperCall == inst.category) {
DecodeConditionalInterrupt(inst);
}
// Lift the operands. This creates the arguments for us to call the
// instruction implementation.
// instuction implementation.
auto xedi = xed_decoded_inst_inst(xedd);
auto num_operands = xed_decoded_inst_noperands(xedd);
for (auto i = 0U; i < num_operands; ++i) {
auto xedo = xed_inst_operand(xedi, i);
if (XED_OPVIS_SUPPRESSED != xed_operand_operand_visibility(xedo)) {
DecodeOperand(instr, xedd, xedo);
DecodeOperand(inst, xedd, xedo);
}
}
if (instr->IsFunctionCall()) {
DecodeFallThroughPC(instr, xedd);
if (inst.IsFunctionCall()) {
DecodeFallThroughPC(inst, xedd);
}
char buffer[256] = {'\0'};
xed_print_info_t info;
info.blen = 256;
info.buf = &(buffer[0]);
info.context = nullptr;
info.disassembly_callback = nullptr;
info.format_options_valid = 0;
info.p = xedd;
info.runtime_address = instr->pc;
info.syntax = XED_SYNTAX_INTEL;
if (xed_format_generic(&info)) {
instr->disassembly.assign(&(buffer[0]));
}
return instr;
// char buffer[256] = {'\0'};
// xed_print_info_t info;
// info.blen = 256;
// info.buf = &(buffer[0]);
// info.context = nullptr;
// info.disassembly_callback = nullptr;
// info.format_options_valid = 0;
// info.p = xedd;
// info.runtime_address = inst.pc;
// info.syntax = XED_SYNTAX_INTEL;
// if (xed_format_generic(&info)) {
// inst.disassembly.assign(&(buffer[0]));
// }
return true;
}
uint64_t X86Arch::ProgramCounter(const ArchState *state_) const {
auto state = reinterpret_cast<const State *>(state_);
if (32 == address_size) {
return state->gpr.rip.dword;
} else {
return state->gpr.rip.qword;
}
} // namespace
// TODO(pag): We pretend that these are singletons, but they aren't really!
const Arch *Arch::GetX86(
OSName os_name_, ArchName arch_name_) {
return new X86Arch(os_name_, arch_name_);
}
} // namespace remill
+5 -5
View File
@@ -32,7 +32,7 @@ Memory *__remill_basic_block(Memory *memory, State &state, addr_t curr_pc) {
// Note: These variables MUST be defined for all architectures.
auto &STATE = state;
auto &MEMORY = *memory;
auto &PC = state.gpr.rip.IF_64BIT_ELSE(qword, dword);
auto &PC = state.gpr.rip.aword;
auto &BRANCH_TAKEN = branch_taken;
// `PC` should already have the correct value, but it's nice to make sure
@@ -40,9 +40,9 @@ Memory *__remill_basic_block(Memory *memory, State &state, addr_t curr_pc) {
// uses to be able to depend on the optimizer not eliminating `curr_pc`.
PC = curr_pc;
// We will reference these variables from the bitcode side of things so that,
// given a decoded register name and an operation type (read or write),
// we can map the register to a specific field in the State structure.
// We will reference these variables from the bitcode side of things so that
// we can map the name of a decoded register to a specific field in the
// `State` structure.
auto &AH = state.gpr.rax.byte.high;
auto &BH = state.gpr.rbx.byte.high;
auto &CH = state.gpr.rcx.byte.high;
@@ -305,7 +305,7 @@ Memory *__remill_basic_block(Memory *memory, State &state, addr_t curr_pc) {
auto &ZF = state.aflag.zf;
// Lifted code will be placed here in clones versions of this function.
return nullptr;
return memory;
}
#pragma clang diagnostic pop
+14
View File
@@ -1,3 +1,17 @@
# 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.
cmake_minimum_required (VERSION 3.2)
project(x86_runtime BC)
+176 -45
View File
@@ -91,34 +91,39 @@ InstructionLifter::InstructionLifter(llvm::IntegerType *word_type_,
intrinsics(intrinsics_) {}
// Lift a single instruction into a basic block.
LiftStatus InstructionLifter::LiftIntoBlock(
Instruction *arch_instr, llvm::BasicBlock *block) {
bool InstructionLifter::LiftIntoBlock(
Instruction &arch_inst, llvm::BasicBlock *block) {
llvm::Function *func = block->getParent();
llvm::Module *module = func->getParent();
auto isel_func = GetInstructionFunction(module, arch_instr->function);
auto status = LiftStatus::kLifted;
auto isel_func = GetInstructionFunction(module, arch_inst.function);
if (Instruction::kCategoryInvalid == arch_instr->category) {
if (Instruction::kCategoryInvalid == arch_inst.category) {
LOG(ERROR)
<< "Cannot decode instruction bytes at "
<< std::hex << arch_inst.pc;
isel_func = GetInstructionFunction(module, "INVALID_INSTRUCTION");
status = LiftStatus::kInvalid;
arch_inst.operands.clear();
if (!isel_func) {
LOG(ERROR)
<< "INVALID_INSTRUCTION doesn't exist.";
return false;
}
}
if (!isel_func) {
LOG(ERROR)
<< "Cannot lift instruction at " << std::hex << arch_instr->pc << ", "
<< arch_instr->function << " doesn't exist.";
<< "Cannot lift instruction at " << std::hex << arch_inst.pc << ", "
<< arch_inst.function << " doesn't exist.";
isel_func = GetInstructionFunction(module, "UNSUPPORTED_INSTRUCTION");
if (!isel_func) {
LOG(ERROR)
<< "UNSUPPORTED_INSTRUCTION doesn't exist; not using it in place of "
<< arch_instr->function;
return LiftStatus::kError;
<< arch_inst.function;
return false;
}
status = LiftStatus::kUnsupported;
arch_instr->operands.clear();
arch_inst.operands.clear();
}
llvm::IRBuilder<> ir(block);
@@ -127,7 +132,7 @@ LiftStatus InstructionLifter::LiftIntoBlock(
auto pc_ptr = LoadProgramCounterRef(block);
// Begin an atomic block.
if (arch_instr->is_atomic_read_modify_write) {
if (arch_inst.is_atomic_read_modify_write) {
std::vector<llvm::Value *> args = {ir.CreateLoad(mem_ptr)};
ir.CreateStore(
ir.CreateCall(intrinsics->atomic_begin, args),
@@ -135,7 +140,7 @@ LiftStatus InstructionLifter::LiftIntoBlock(
}
std::vector<llvm::Value *> args;
args.reserve(arch_instr->operands.size() + 2);
args.reserve(arch_inst.operands.size() + 2);
// First two arguments to an instruction semantics function are the
// state pointer, and a pointer to the memory pointer.
@@ -145,18 +150,18 @@ LiftStatus InstructionLifter::LiftIntoBlock(
auto isel_func_type = isel_func->getFunctionType();
auto arg_num = 2U;
for (auto &op : arch_instr->operands) {
for (auto &op : arch_inst.operands) {
CHECK(arg_num < isel_func_type->getNumParams())
<< "Function " << arch_instr->function << ", implemented by "
<< "Function " << arch_inst.function << ", implemented by "
<< isel_func->getName().str() << ", should have at least "
<< arg_num << " arguments.";
auto arg_type = isel_func_type->getParamType(arg_num++);
auto operand = LiftOperand(arch_instr, block, arg_type, op);
auto operand = LiftOperand(arch_inst, block, arg_type, op);
auto op_type = operand->getType();
CHECK(op_type == arg_type)
<< "Lifted operand " << op.Debug() << " to "
<< arch_instr->function << " does not have the correct type. Expected "
<< arch_inst.function << " does not have the correct type. Expected "
<< LLVMThingToString(arg_type) << " but got "
<< LLVMThingToString(op_type) << ".";
@@ -168,7 +173,7 @@ LiftStatus InstructionLifter::LiftIntoBlock(
ir.CreateStore(
ir.CreateAdd(
ir.CreateLoad(pc_ptr),
llvm::ConstantInt::get(word_type, arch_instr->NumBytes())),
llvm::ConstantInt::get(word_type, arch_inst.NumBytes())),
pc_ptr);
// Pass in current value of the memory pointer.
@@ -178,14 +183,14 @@ LiftStatus InstructionLifter::LiftIntoBlock(
ir.CreateStore(ir.CreateCall(isel_func, args), mem_ptr);
// End an atomic block.
if (arch_instr->is_atomic_read_modify_write) {
if (arch_inst.is_atomic_read_modify_write) {
std::vector<llvm::Value *> args = {ir.CreateLoad(mem_ptr)};
ir.CreateStore(
ir.CreateCall(intrinsics->atomic_end, args),
mem_ptr);
}
return status;
return true;
}
namespace {
@@ -233,12 +238,115 @@ static llvm::Value *LoadWordRegValOrZero(llvm::BasicBlock *block,
} // namespace
llvm::Value *InstructionLifter::LiftShiftRegisterOperand(
Instruction &inst, llvm::BasicBlock *block,
llvm::Type *arg_type, Operand &op) {
llvm::Function *func = block->getParent();
llvm::Module *module = func->getParent();
auto &context = module->getContext();
auto &arch_reg = op.shift_reg.reg;
CHECK(arg_type->isIntegerTy())
<< "Expected " << arch_reg.name << " to be an integral type "
<< "for instruction at " << std::hex << inst.pc;
const llvm::DataLayout data_layout(module);
auto val = LoadRegValue(block, arch_reg.name);
auto val_type = val->getType();
auto val_size = data_layout.getTypeAllocSizeInBits(val_type);
auto word_size = data_layout.getTypeAllocSizeInBits(word_type);
const uint64_t zero = 0;
const uint64_t one = 1;
const uint64_t shift_size = op.shift_reg.shift_size;
const auto shift_val = llvm::ConstantInt::get(
val_type, shift_size % (val_size - one));
llvm::IRBuilder<> ir(block);
if (Operand::ShiftRegister::kExtendInvalid != op.shift_reg.extend_op) {
auto extract_type = llvm::Type::getIntNTy(
context, op.shift_reg.extract_size);
val = ir.CreateTrunc(val, extract_type);
switch (op.shift_reg.extend_op) {
case Operand::ShiftRegister::kExtendSigned:
val = ir.CreateSExt(val, val_type);
break;
case Operand::ShiftRegister::kExtendUnsigned:
val = ir.CreateZExt(val, val_type);
break;
default:
LOG(FATAL)
<< "Invalid extend operation type for instruction at "
<< std::hex << inst.pc;
break;
}
}
if (Operand::ShiftRegister::kShiftInvalid != op.shift_reg.shift_op) {
switch (op.shift_reg.shift_op) {
// Left shift.
case Operand::ShiftRegister::kShiftLeftWithZeroes:
val = ir.CreateShl(val, shift_val);
break;
// Masking shift left.
case Operand::ShiftRegister::kShiftLeftWithOnes: {
const auto mask_val = llvm::ConstantInt::get(
val_type, ~((~zero) << shift_size));
val = ir.CreateOr(ir.CreateShl(val, shift_val), mask_val);
break;
}
// Logical right shift.
case Operand::ShiftRegister::kShiftUnsignedRight:
val = ir.CreateLShr(val, shift_val);
break;
// Arithmetic right shift.
case Operand::ShiftRegister::kShiftSignedRight:
val = ir.CreateAShr(val, shift_val);
break;
// Rotate right.
case Operand::ShiftRegister::kShiftRightAround: {
const uint64_t shl_amount = (~shift_size + one) & (val_size - one);
const auto shl_val = llvm::ConstantInt::get(val_type, shl_amount);
const auto val1 = ir.CreateLShr(val, shift_val);
const auto val2 = ir.CreateShl(val, shl_val);
val = ir.CreateAnd(val1, val2);
break;
}
default:
LOG(FATAL)
<< "Invalid shift operation type for instruction at "
<< std::hex << inst.pc;
break;
}
}
if (word_size > val_size) {
val = ir.CreateZExt(val, word_type);
} else {
CHECK(word_size == val_size)
<< "The register " << arch_reg.name << " is too wide to be shifted.";
}
return val;
}
// Load a register operand. This deals uniformly with write- and read-operands
// for registers. In the case of write operands, the argument type is always
// a pointer. In the case of read operands, the argument type is sometimes
// a pointer (e.g. when passing a vector to an instruction semantics function).
llvm::Value *InstructionLifter::LiftRegisterOperand(
Instruction *, llvm::BasicBlock *block,
Instruction &inst, llvm::BasicBlock *block,
llvm::Type *arg_type, Operand &op) {
llvm::Function *func = block->getParent();
@@ -258,7 +366,8 @@ llvm::Value *InstructionLifter::LiftRegisterOperand(
} else {
CHECK(arg_type->isIntegerTy() || arg_type->isFloatingPointTy())
<< "Expected " << arch_reg.name << " to be an integral or float type.";
<< "Expected " << arch_reg.name << " to be an integral or float type "
<< "for instruction at " << std::hex << inst.pc;
auto val = LoadRegValue(block, arch_reg.name);
@@ -271,17 +380,20 @@ llvm::Value *InstructionLifter::LiftRegisterOperand(
if (val_size < arg_size) {
if (arg_type->isIntegerTy()) {
CHECK(val_type->isIntegerTy())
<< "Expected " << arch_reg.name << " to be an integral type.";
<< "Expected " << arch_reg.name << " to be an integral type "
<< "for instruction at " << std::hex << inst.pc;
CHECK(word_size == arg_size)
<< "Expected integer argument to be machine word size ("
<< word_size << " bits) but is is " << arg_size << " instead.";
<< word_size << " bits) but is is " << arg_size << " instead "
<< "in instruction at " << std::hex << inst.pc;
val = new llvm::ZExtInst(val, word_type, "", block);
} else if (arg_type->isFloatingPointTy()) {
CHECK(val_type->isFloatingPointTy())
<< "Expected " << arch_reg.name << " to be a floating point type.";
<< "Expected " << arch_reg.name << " to be a floating point type "
<< "for instruction at " << std::hex << inst.pc;
val = new llvm::FPExtInst(val, arg_type, "", block);
}
@@ -289,17 +401,20 @@ llvm::Value *InstructionLifter::LiftRegisterOperand(
} else if (val_size > arg_size) {
if (arg_type->isIntegerTy()) {
CHECK(val_type->isIntegerTy())
<< "Expected " << arch_reg.name << " to be an integral type.";
<< "Expected " << arch_reg.name << " to be an integral type "
<< "for instruction at " << std::hex << inst.pc;
CHECK(word_size == arg_size)
<< "Expected integer argument to be machine word size ("
<< word_size << " bits) but is is " << arg_size << " instead.";
<< word_size << " bits) but is is " << arg_size << " instead "
<< "in instruction at " << std::hex << inst.pc;
val = new llvm::TruncInst(val, arg_type, "", block);
} else if (arg_type->isFloatingPointTy()) {
CHECK(val_type->isFloatingPointTy())
<< "Expected " << arch_reg.name << " to be a floating point type.";
<< "Expected " << arch_reg.name << " to be a floating point type "
<< "for instruction at " << std::hex << inst.pc;
val = new llvm::FPTruncInst(val, arg_type, "", block);
}
@@ -310,29 +425,29 @@ llvm::Value *InstructionLifter::LiftRegisterOperand(
}
// Lift an immediate operand.
llvm::Value *InstructionLifter::LiftImmediateOperand(Instruction *,
llvm::Value *InstructionLifter::LiftImmediateOperand(Instruction &inst,
llvm::BasicBlock *,
llvm::Type *arg_type,
Operand &arch_op) {
if (arch_op.size > word_type->getBitWidth()) {
CHECK(arg_type->isIntegerTy(static_cast<uint32_t>(arch_op.size)))
<< "Argument to semantics function is not an integer. This may "
<< "not be surprising because the immediate operand is " <<
arch_op.size << " bits, but the machine word size is "
<< word_type->getBitWidth() << " bits.";
<< "Argument to semantics function for instruction at " << std::hex
<< inst.pc << " is not an integer. This may not be surprising because "
<< "the immediate operand is " << arch_op.size << " bits, but the "
<< "machine word size is " << word_type->getBitWidth() << " bits.";
CHECK(arch_op.size <= 64)
<< "Decode error! Immediate operands can be at most 64 bits! "
<< "Operand structure encodes a truncated " << arch_op.size << " bit "
<< "value.";
<< "value for instruction at " << std::hex << inst.pc;
return llvm::ConstantInt::get(
arg_type, arch_op.imm.val, arch_op.imm.is_signed);
} else {
CHECK(arg_type->isIntegerTy(word_type->getBitWidth()))
<< "Bad semantics function implementation. Integer constants that are "
<< "Bad semantics function implementation for instruction at "
<< std::hex << inst.pc << ". Integer constants that are "
<< "smaller than the machine word size should be represented as "
<< "machine word sized arguments to semantics functions.";
@@ -343,17 +458,19 @@ llvm::Value *InstructionLifter::LiftImmediateOperand(Instruction *,
// Zero-extend a value to be the machine word size.
llvm::Value *InstructionLifter::LiftAddressOperand(
Instruction *, llvm::BasicBlock *block, Operand &op) {
Instruction &inst, llvm::BasicBlock *block, Operand &op) {
auto &arch_addr = op.addr;
auto zero = llvm::ConstantInt::get(word_type, 0, false);
auto word_size = word_type->getBitWidth();
CHECK(word_size >= arch_addr.base_reg.size)
<< "Memory base register " << arch_addr.base_reg.name
<< "for instruction at " << std::hex << inst.pc
<< " is wider than the machine word size.";
CHECK(word_size >= arch_addr.index_reg.size)
<< "Memory index register " << arch_addr.base_reg.name
<< "for instruction at " << std::hex << inst.pc
<< " is wider than the machine word size.";
auto addr = LoadWordRegValOrZero(block, arch_addr.base_reg.name, zero);
@@ -399,7 +516,7 @@ llvm::Value *InstructionLifter::LiftAddressOperand(
}
// Lift an operand for use by the instruction.
llvm::Value *InstructionLifter::LiftOperand(Instruction *instr,
llvm::Value *InstructionLifter::LiftOperand(Instruction &inst,
llvm::BasicBlock *block,
llvm::Type *arg_type,
Operand &arch_op) {
@@ -409,15 +526,27 @@ llvm::Value *InstructionLifter::LiftOperand(Instruction *instr,
<< "Decode error! Cannot lift invalid operand.";
return nullptr;
case Operand::kTypeShiftRegister:
CHECK(Operand::kActionRead == arch_op.action)
<< "Can't write to a shift register operand "
<< "for instruction at " << std::hex << inst.pc;
CHECK(arch_op.size == arch_op.shift_reg.reg.size)
<< "Operand size and register size must match for register "
<< arch_op.shift_reg.reg.name << " in instruction at "
<< std::hex << inst.pc;
return LiftShiftRegisterOperand(inst, block, arg_type, arch_op);
case Operand::kTypeRegister:
CHECK(arch_op.size == arch_op.reg.size)
<< "Operand size and register size must match for register "
<< arch_op.reg.name << ".";
return LiftRegisterOperand(instr, block, arg_type, arch_op);
return LiftRegisterOperand(inst, block, arg_type, arch_op);
case Operand::kTypeImmediate:
return LiftImmediateOperand(instr, block, arg_type, arch_op);
return LiftImmediateOperand(inst, block, arg_type, arch_op);
case Operand::kTypeAddress:
if (arg_type != word_type) {
@@ -425,14 +554,16 @@ llvm::Value *InstructionLifter::LiftOperand(Instruction *instr,
<< "Expected that a memory operand should be represented by "
<< "machine word type. Argument type is "
<< LLVMThingToString(arg_type) << " and word type is "
<< LLVMThingToString(word_type);
<< LLVMThingToString(word_type) << " in instruction at "
<< std::hex << inst.pc;
}
return LiftAddressOperand(instr, block, arch_op);
return LiftAddressOperand(inst, block, arch_op);
}
LOG(FATAL)
<< "Got a Operand type of " << static_cast<int>(arch_op.type) << ".";
<< "Got a unknown operand type of " << static_cast<int>(arch_op.type)
<< " in instruction at " << std::hex << inst.pc;
return nullptr;
}
+12 -13
View File
@@ -30,13 +30,6 @@ class Instruction;
class IntrinsicTable;
class Operand;
enum class LiftStatus {
kLifted,
kInvalid,
kUnsupported,
kError
};
// Wraps the process of lifting an instruction into a block. This resolves
// the intended instruction target to a function, and ensures that the function
// is called with the appropriate arguments.
@@ -48,8 +41,8 @@ class InstructionLifter {
const IntrinsicTable *intrinsics_);
// Lift a single instruction into a basic block.
virtual LiftStatus LiftIntoBlock(
Instruction *instr, llvm::BasicBlock *block);
virtual bool LiftIntoBlock(
Instruction &inst, llvm::BasicBlock *block);
// Machine word type for this architecture.
llvm::IntegerType * const word_type;
@@ -59,25 +52,31 @@ class InstructionLifter {
protected:
// Lift an operand to an instruction.
virtual llvm::Value *LiftOperand(Instruction *instr,
virtual llvm::Value *LiftOperand(Instruction &inst,
llvm::BasicBlock *block,
llvm::Type *op_type,
Operand &op);
// Lift a register operand to a value.
virtual llvm::Value *LiftRegisterOperand(Instruction *instr,
virtual llvm::Value *LiftShiftRegisterOperand(Instruction &inst,
llvm::BasicBlock *block,
llvm::Type *arg_type,
Operand &reg);
// Lift a register operand to a value.
virtual llvm::Value *LiftRegisterOperand(Instruction &inst,
llvm::BasicBlock *block,
llvm::Type *arg_type,
Operand &reg);
// Lift an immediate operand.
virtual llvm::Value *LiftImmediateOperand(Instruction *instr,
virtual llvm::Value *LiftImmediateOperand(Instruction &inst,
llvm::BasicBlock *block,
llvm::Type *arg_type,
Operand &op);
// Lift an indirect memory operand to a value.
virtual llvm::Value *LiftAddressOperand(Instruction *instr,
virtual llvm::Value *LiftAddressOperand(Instruction &inst,
llvm::BasicBlock *block,
Operand &mem);
+52 -58
View File
@@ -29,17 +29,17 @@
#include <llvm/IR/BasicBlock.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/IntrinsicInst.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Metadata.h>
#include <llvm/IR/Module.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/Support/SourceMgr.h>
#include <llvm/Support/ToolOutputFile.h>
#include <llvm/Support/raw_ostream.h>
#include "remill/BC/ABI.h"
#include "remill/BC/Compat/BitcodeReaderWriter.h"
@@ -55,7 +55,6 @@ namespace remill {
// Initialize the attributes for a lifted function.
void InitFunctionAttributes(llvm::Function *function) {
// Make sure functions are treated as if they return. LLVM doesn't like
// mixing must-tail-calls with no-return.
function->removeFnAttr(llvm::Attribute::NoReturn);
@@ -79,8 +78,8 @@ void InitFunctionAttributes(llvm::Function *function) {
llvm::CallInst *AddTerminatingTailCall(llvm::Function *source_func,
llvm::Value *dest_func) {
if (source_func->isDeclaration()) {
llvm::IRBuilder<> ir(llvm::BasicBlock::Create(
source_func->getContext(), "", source_func));
llvm::IRBuilder<> ir(
llvm::BasicBlock::Create(source_func->getContext(), "", source_func));
std::vector<llvm::Value *> args;
for (llvm::Argument &arg : source_func->args()) {
@@ -100,8 +99,7 @@ llvm::CallInst *AddTerminatingTailCall(llvm::Function *source_func,
llvm::CallInst *AddTerminatingTailCall(llvm::BasicBlock *source_block,
llvm::Value *dest_func) {
CHECK(nullptr != dest_func)
<< "Target function/block does not exist!";
CHECK(nullptr != dest_func) << "Target function/block does not exist!";
LOG_IF(ERROR, source_block->getTerminator())
<< "Block already has a terminator; not adding fall-through call to: "
@@ -135,8 +133,7 @@ llvm::CallInst *AddTerminatingTailCall(llvm::BasicBlock *source_block,
// Find a local variable defined in the entry block of the function. We use
// this to find register variables.
llvm::Value *FindVarInFunction(llvm::BasicBlock *block,
std::string name,
llvm::Value *FindVarInFunction(llvm::BasicBlock *block, std::string name,
bool allow_failure) {
return FindVarInFunction(block->getParent(), name, allow_failure);
}
@@ -151,9 +148,8 @@ llvm::Value *FindVarInFunction(llvm::Function *function, std::string name,
}
}
CHECK(allow_failure)
<< "Could not find variable " << name << " in function "
<< function->getName().str();
CHECK(allow_failure) << "Could not find variable " << name << " in function "
<< function->getName().str();
return nullptr;
}
@@ -190,8 +186,8 @@ llvm::Value *LoadProgramCounterRef(llvm::BasicBlock *block) {
void StoreProgramCounter(llvm::BasicBlock *block, uint64_t pc) {
auto pc_ptr = LoadProgramCounterRef(block);
auto type = llvm::dyn_cast<llvm::PointerType>(pc_ptr->getType());
(void) new llvm::StoreInst(
llvm::ConstantInt::get(type->getElementType(), pc), pc_ptr, block);
(void)new llvm::StoreInst(llvm::ConstantInt::get(type->getElementType(), pc),
pc_ptr, block);
}
// Return the current memory pointer.
@@ -204,8 +200,8 @@ llvm::Value *LoadMemoryPointer(llvm::BasicBlock *block) {
// or not a conditional branch is taken.
llvm::Value *LoadBranchTaken(llvm::BasicBlock *block) {
llvm::IRBuilder<> ir(block);
auto cond = ir.CreateLoad(ir.CreateLoad(
FindVarInFunction(block->getParent(), "BRANCH_TAKEN")));
auto cond = ir.CreateLoad(
ir.CreateLoad(FindVarInFunction(block->getParent(), "BRANCH_TAKEN")));
auto true_val = llvm::ConstantInt::get(cond->getType(), 1);
return ir.CreateICmpEQ(cond, true_val);
}
@@ -234,19 +230,18 @@ llvm::Module *LoadModuleFromFile(llvm::LLVMContext *context,
auto module = mod_ptr.get();
mod_ptr.release();
CHECK(nullptr != module)
<< "Unable to parse module file: " << file_name << ".";
CHECK(nullptr != module) << "Unable to parse module file: " << file_name
<< ".";
auto ec = module->materializeAll(); // Just in case.
CHECK(!ec)
<< "Unable to materialize everything from " << file_name;
CHECK(!ec) << "Unable to materialize everything from " << file_name;
std::string error;
llvm::raw_string_ostream error_stream(error);
if (llvm::verifyModule(*module, &error_stream)) {
error_stream.flush();
LOG(FATAL)
<< "Error reading module from file " << file_name << ": " << error;
LOG(FATAL) << "Error reading module from file " << file_name << ": "
<< error;
}
return module;
@@ -263,20 +258,18 @@ void StoreModuleToFile(llvm::Module *module, std::string file_name) {
if (llvm::verifyModule(*module, &error_stream)) {
error_stream.flush();
LOG(FATAL)
<< "Error writing module to file " << file_name << ": " << error;
LOG(FATAL) << "Error writing module to file " << file_name << ": " << error;
}
#if LLVM_VERSION_NUMBER > LLVM_VERSION(3, 5)
std::error_code ec;
llvm::tool_output_file bc(tmp_name.c_str(), ec, llvm::sys::fs::F_RW);
CHECK(!ec)
<< "Unable to open output bitcode file for writing: " << tmp_name;
CHECK(!ec) << "Unable to open output bitcode file for writing: " << tmp_name;
#else
llvm::tool_output_file bc(tmp_name.c_str(), error, llvm::sys::fs::F_RW);
CHECK(error.empty() && !bc.os().has_error())
<< "Unable to open output bitcode file for writing: " << tmp_name
<< ": " << error;
<< "Unable to open output bitcode file for writing: " << tmp_name << ": "
<< error;
#endif
llvm::WriteBitcodeToFile(module, bc.os());
@@ -285,26 +278,31 @@ void StoreModuleToFile(llvm::Module *module, std::string file_name) {
RenameFile(tmp_name, file_name);
} else {
RemoveFile(tmp_name);
LOG(FATAL)
<< "Error writing bitcode to file: " << file_name << ".";
LOG(FATAL) << "Error writing bitcode to file: " << file_name << ".";
}
}
namespace {
#ifndef BUILD_SEMANTICS_DIR
# error "Macro `BUILD_SEMANTICS_DIR` must be defined."
# define BUILD_SEMANTICS_DIR
#error "Macro `BUILD_SEMANTICS_DIR` must be defined."
#define BUILD_SEMANTICS_DIR
#endif // BUILD_SEMANTICS_DIR
#ifndef BUILD_SEMANTICS_DIR_ARM
#error \
"Macro `BUILD_SEMANTICS_DIR_ARM` must be defined to support ARM architecture."
#define BUILD_SEMANTICS_DIR_ARM
#endif // BUILD_SEMANTICS_DIR
#ifndef INSTALL_SEMANTICS_DIR
# error "Macro `INSTALL_SEMANTICS_DIR` must be defined."
# define INSTALL_SEMANTICS_DIR
#error "Macro `INSTALL_SEMANTICS_DIR` must be defined."
#define INSTALL_SEMANTICS_DIR
#endif // INSTALL_SEMANTICS_DIR
static const char *gSemanticsSearchPaths[] = {
// Derived from the build.
BUILD_SEMANTICS_DIR "\0",
BUILD_SEMANTICS_DIR "\0", BUILD_SEMANTICS_DIR_ARM "\0",
INSTALL_SEMANTICS_DIR "\0",
};
@@ -326,8 +324,7 @@ 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 "";
}
@@ -383,16 +380,16 @@ void ForEachISel(llvm::Module *module, ISelCallback callback) {
llvm::Function *DeclareLiftedFunction(llvm::Module *module,
const std::string &name) {
auto bb = module->getFunction("__remill_basic_block");
CHECK(nullptr != bb)
<< "Cannot declare lifted function " << name << " because the "
<< " intrinsics __remill_basic_block cannot be found.";
CHECK(nullptr != bb) << "Cannot declare lifted function " << name
<< " because the "
<< " intrinsics __remill_basic_block cannot be found.";
auto func_type = bb->getFunctionType();
auto func = llvm::dyn_cast<llvm::Function>(
module->getOrInsertFunction(name, func_type));
CHECK(nullptr != func)
<< "Could not insert function " << name << " into module";
CHECK(nullptr != func) << "Could not insert function " << name
<< " into module";
InitFunctionAttributes(func);
@@ -426,8 +423,8 @@ void CloneFunctionInto(llvm::Function *source_func, llvm::Function *dest_func) {
// Clone the basic blocks and their instructions.
std::unordered_map<llvm::BasicBlock *, llvm::BasicBlock *> block_map;
for (auto &old_block : *source_func) {
auto new_block = llvm::BasicBlock::Create(
dest_func->getContext(), old_block.getName(), dest_func);
auto new_block = llvm::BasicBlock::Create(dest_func->getContext(),
old_block.getName(), dest_func);
value_map[&old_block] = new_block;
block_map[&old_block] = new_block;
@@ -483,10 +480,9 @@ void CloneFunctionInto(llvm::Function *source_func, llvm::Function *dest_func) {
// At this point, all we should have is a global.
auto global_val = llvm::dyn_cast<llvm::GlobalValue>(old_op_val);
if (!global_val) {
LOG(FATAL)
<< "Cannot clone value " << LLVMThingToString(old_op_val)
<< " into function " << func_name << " because it isn't "
<< "a global value.";
LOG(FATAL) << "Cannot clone value " << LLVMThingToString(old_op_val)
<< " into function " << func_name << " because it isn't "
<< "a global value.";
}
// If it's a global and we're in the same module, then use it.
@@ -499,21 +495,19 @@ void CloneFunctionInto(llvm::Function *source_func, llvm::Function *dest_func) {
// Declare the global in the new module.
llvm::GlobalValue *new_global_val = nullptr;
if (llvm::isa<llvm::Function>(global_val)) {
new_global_val = llvm::dyn_cast<llvm::GlobalValue>(
dest_mod->getOrInsertFunction(
global_val->getName(),
llvm::dyn_cast<llvm::FunctionType>(
GetValueType(global_val))));
new_global_val =
llvm::dyn_cast<llvm::GlobalValue>(dest_mod->getOrInsertFunction(
global_val->getName(), llvm::dyn_cast<llvm::FunctionType>(
GetValueType(global_val))));
} else if (llvm::isa<llvm::GlobalVariable>(global_val)) {
new_global_val = llvm::dyn_cast<llvm::GlobalValue>(
dest_mod->getOrInsertGlobal(
new_global_val =
llvm::dyn_cast<llvm::GlobalValue>(dest_mod->getOrInsertGlobal(
global_val->getName(), GetValueType(global_val)));
} else {
LOG(FATAL)
<< "Cannot clone value " << LLVMThingToString(old_op_val)
<< " into new module for function " << func_name;
LOG(FATAL) << "Cannot clone value " << LLVMThingToString(old_op_val)
<< " into new module for function " << func_name;
}
auto old_name = global_val->getName().str();
+97
View File
@@ -0,0 +1,97 @@
#!/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.
# Make sure we have `add-apt-repository`.
# Update sources list, and then install needed packages.
sudo apt-get update -yqq
sudo apt-get install -y git
sudo apt-get install -y python2.7
sudo apt-get install -y wget
sudo apt-get install -y realpath
sudo apt-get install -y zlib1g-dev
sudo apt-get install -y software-properties-common
sudo apt-get install -y build-essential
sudo apt-get install -y g++-4.9-multilib
sudo apt-get install -y gcc-4.9-multilib
sudo apt-get install -y libtinfo-dev
# General directory structure:
# /path/to/home/remill
# /path/to/home/remill-build
SCRIPTS_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
SRC_DIR=$( cd "$( dirname "${SCRIPTS_DIR}" )" && pwd)
PARENT_DIR=$( cd "$( dirname "${SRC_DIR}" )" && pwd)
BUILD_DIR=${PARENT_DIR}/remill-build
# Version name of Ubuntu (e.g. xenial, trusty).
UBUNTU_RELEASE=`lsb_release -sc`
# The version of LLVM that we will use to build remill, and also that remill
# will use to produce bitcode. It's good to match these up.
LLVM_VERSION=llvm40
mkdir -p ${BUILD_DIR}
cd ${BUILD_DIR}
printf "SRC_DIR=${SRC_DIR}\n"
printf "BUILD_DIR=${BUILD_DIR}\n"
# There are pre-build versions of various libraries for specific
# Ubuntu releases.
case ${UBUNTU_RELEASE} in
xenial)
OS_VERSION=ubuntu160402
;;
trusty)
OS_VERSION=ubuntu140405
;;
esac
LIBRARY_VERSION=libraries-${LLVM_VERSION}-${OS_VERSION}
# Download CMake.
if [[ ! -d "${BUILD_DIR}/cmake-3.2.0-Linux-x86_64" ]] ; then
wget https://cmake.org/files/v3.2/cmake-3.2.0-Linux-x86_64.sh
yes | /bin/bash cmake-3.2.0-Linux-x86_64.sh &>/dev/null
CMAKE_BIN_DIR=${BUILD_DIR}/cmake-3.2.0-Linux-x86_64/bin
fi
# Download pre-compiled version of cxx-common for this OS. This has things like
# google protobuf, gflags, glog, gtest, capstone, and llvm in it.
if [[ ! -d "${BUILD_DIR}/libraries" ]] ; then
wget https://s3.amazonaws.com/cxx-common/${LIBRARY_VERSION}.tar.gz
tar xf ${LIBRARY_VERSION}.tar.gz --warning=no-timestamp
rm ${LIBRARY_VERSION}.tar.gz
# Make sure modification times are not in the future.
find ${BUILD_DIR}/libraries -type f -exec touch {} \;
fi
# Tell the remill CMakeLists.txt where the extracted libraries are.
export TRAILOFBITS_LIBRARIES=${BUILD_DIR}/libraries
# Configure the remill build, specifying that it should use the pre-built
# Clang compiler binaries.
${CMAKE_BIN_DIR}/cmake \
-DCMAKE_C_COMPILER=${BUILD_DIR}/libraries/llvm/bin/clang \
-DCMAKE_CXX_COMPILER=${BUILD_DIR}/libraries/llvm/bin/clang++ \
${SRC_DIR}
# Build remill.
make
-94
View File
@@ -1,94 +0,0 @@
#!/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.
set -e
DIR=$(dirname $(dirname $( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )))
BUILD_DIR=${DIR}/build
LLVM_DIR=llvm
ARCH=$(uname -m)
LLVM_VER=3.9.0
case $(uname -s) in
Darwin)
OS=apple-darwin
FILE=clang+llvm-${LLVM_VER}-${ARCH}-${OS}.tar.xz
;;
Linux)
OS=linux-gnu
DISTRO=$(lsb_release -si | tr '[:upper:]' '[:lower:]')
DIST_VERSION=$(lsb_release -sr)
if [ $DIST_VERSION == "15.04" ]; then
DIST_VERSION="14.04"
fi
FILE=clang+llvm-${LLVM_VER}-${ARCH}-${OS}-${DISTRO}-${DIST_VERSION}.tar.xz
;;
*)
echo '[!] Unsupported OS'
exit 1
;;
esac
echo "[+] Creating '${BUILD_DIR}'"
mkdir -p ${BUILD_DIR}
pushd ${BUILD_DIR}
if [ ! -f ${FILE} ]; then
echo "[+] Downloading Clang+LLVM.."
wget http://llvm.org/releases/${LLVM_VER}/${FILE}
if [ "$?" != "0" ]; then
echo "[!] Unsupported operating system."
echo "[!] Check http://llvm.org/releases/${LLVM_VER}/ to see what LLVM"
echo "[!] packages are available."
exit 2
fi
fi
if [ ! -d ${LLVM_DIR} ]; then
echo "[+] Extracting.."
mkdir ${LLVM_DIR}
tar xf ${FILE} -C ${LLVM_DIR} --strip-components=1
fi
LLVM_DIR=${BUILD_DIR}/${LLVM_DIR}
LLVM_DIR=$(realpath ${LLVM_DIR})
export LLVM_INSTALL_PREFIX=${LLVM_DIR}
if [ ! -d ${BUILD_DIR}/cxx-common ]; then
echo "[+] Getting cxx-common"
git clone git@github.com:trailofbits/cxx-common.git
fi
echo "[+] Installing cxx-common"
export TRAILOFBITS_LIBRARIES=${BUILD_DIR}/libraries
mkdir -p ${TRAILOFBITS_LIBRARIES}
export CC=${LLVM_INSTALL_PREFIX}/bin/clang
export CXX=${LLVM_INSTALL_PREFIX}/bin/clang++
${BUILD_DIR}/cxx-common/build.sh --targets xed,glog,gflags,gtest ${TRAILOFBITS_LIBRARIES}
export CC=
export CXX=
echo "[+] Running cmake"
cmake ${DIR}
echo "[+] Building"
make build_x86_tests
echo "[+] Testing"
make tests
ctest
-35
View File
@@ -1,35 +0,0 @@
#!/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.
DIR=$(dirname $(dirname $( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )))
if [[ "${TRAVIS_OS_NAME}" = "linux" ]] ; then
# Print out useful information to see what Linux environment is being used
# by Travis.
uname -a
lsb_release -a
sudo apt-get update -yqq
sudo apt-get --force-yes purge isc-dhcp-client
sudo apt-get install -y isc-dhcp-client
#sudo apt-get upgrade -yqq
$DIR/scripts/travis/install_linux.sh
fi
if [[ "${TRAVIS_OS_NAME}" = "osx" ]] ; then
$DIR/scripts/travis/install_macos.sh
fi
-39
View File
@@ -1,39 +0,0 @@
#!/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.
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
UBUNTU_RELEASE=`lsb_release -sc`
#sudo apt-get update -yqq
#sudo apt-get upgrade -yqq
# Make sure we have `add-apt-repository`.
sudo apt-get install -y software-properties-common
sudo apt-get install -y build-essential
# Add the CMake repository.
sudo add-apt-repository -y ppa:george-edison55/cmake-3.x
# Update sources list, and then install needed packages.
sudo apt-get update -yqq
sudo apt-get install -y git
sudo apt-get install -y python2.7
sudo apt-get install -y unzip
sudo apt-get install -y cmake
sudo apt-get install -y realpath
sudo apt-get install -y zlib1g-dev
# Build remill
${DIR}/build.sh
+5 -20
View File
@@ -86,35 +86,20 @@ static void AddFunctionToModule(llvm::Module *module,
remill::InstructionLifter lifter(word_type, &intrinsics);
auto block = &(func->front());
auto addr = test.test_begin;
while (addr < test.test_end) {
std::string instr_bytes;
auto bytes = reinterpret_cast<const char *>(addr);
instr_bytes.insert(instr_bytes.end(), bytes, bytes + 15);
std::unique_ptr<remill::Instruction> inst(
arch->DecodeInstruction(addr, instr_bytes));
CHECK(inst->IsValid())
remill::Instruction inst;
CHECK(arch->DecodeInstruction(addr, instr_bytes, inst))
<< "Can't decode test instruction in " << test.test_name;
switch (auto status = lifter.LiftIntoBlock(inst.get(), block)) {
case remill::LiftStatus::kError:
case remill::LiftStatus::kInvalid:
LOG(ERROR)
<< "Not lifting " << inst->Serialize();
remill::AddTerminatingTailCall(block, intrinsics.error);
return;
case remill::LiftStatus::kUnsupported:
LOG(ERROR)
<< "Unsupported instruction: " << inst->Serialize();
break;
default:
break;
}
CHECK(lifter.LiftIntoBlock(inst, block))
<< "Can't lift test instruction in " << test.test_name;
addr += inst->NumBytes();
addr += inst.NumBytes();
}
remill::AddTerminatingTailCall(block, intrinsics.missing_block);
+6 -5
View File
@@ -1,4 +1,3 @@
#!/usr/bin/env bash
# Copyright (c) 2017 Trail of Bits, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,8 +12,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
# test tools
#add_subdirectory(testdec)
brew install glog
$DIR/build.sh
# mcsema needs to be manually cloned into this repo.
if (EXISTS ${CMAKE_SOURCE_DIR}/tools/mcsema)
add_subdirectory(mcsema)
endif ()