Files
lifting-bits-remill/scripts/build.sh
T
Alex Cameron fb018c96e9 PowerPC Support (#645)
* Add skeleton for PPC

* Copyright notices

* Fill in some details for the PPC arch

* Start building a (wrong) PPC runtime

* Begin populating state structure

* First pass for EIS state structure

* Map registers to Sleigh register names

* More fixes

* add optional param

* Create handle unsupported and invalid instruction isels

* Correct typo

* Get a basic `remill-lift` invocation running without failure

* Fix capitalisation

* Set vle context reg

* Fix SleighDecoder signatures

* Set VLE context register in the Sleigh engine in addition to our
internal context reg mapping

* Capitalize reg names

* Add the flag registers for XER and CR

* Rename bitflag structures in PPC state

* PPC Sleigh patches (#643)

* Modified sleigh patch script to generate patches for multiple .sinc files

* update README with new examples of sleigh patch script invocation

* add ppc register definition

* add ppc sleigh patches

* fix issue with remill_insn_size definition

* regenerate sleigh patches for PPC

* update CMakeLists.txt to include PPC patches

* Add TEA signal as a register in the PPC state

* Uppercase the stack pointer register name

* Fix PPC instruction sizes

* initial PPC tests

* remove duplicate tests

* fix tests for e_stmvgprw/e_ldmvgprw

* add tests for loading/storing from special registers

* add tests with internal conditionals in pcode

* fix for pc reg and addr width not being the same... I suspect this issue is going to come up elsewhere

* add heuristic for flow from normal intrainstruction flow

* rework tests to allow testing for different sized registers

* add tests for overflow and record add

* fix bug with log printout

* add intrafunction control flow lifting

* handle edge case where there is no pcode op at the zero index

* Fix another inconsistency with mismatching address and PC reg size

* Allocate unique ptrs in the entry block

* Fix `INT_LEFT` and `INT_RIGHT` impl where shift exceeds bit width

* fix supiece lift?

* Add PPC emulate instruction to hyper call

* fix for pc reg and addr width not being the same... I suspect this issue is going to come up elsewhere

* add heuristic for flow from normal intrainstruction flow

* add intrafunction control flow lifting

* handle edge case where there is no pcode op at the zero index

* Fix another inconsistency with mismatching address and PC reg size

* fix supiece lift?

* Allocate unique ptrs in the entry block

* Fix `INT_LEFT` and `INT_RIGHT` impl where shift exceeds bit width

* fix int2float semantics

should use appropriate sized float based on the output size

* add tests for lifting int2float

* fix INT_{LEFT,RIGHT} semantics

should be `ICmpSGE` instead of `ICmpSGT`

* add cr0-7 registers

* fix formatting

* fix conditional branch test

* add test for compare

* re-enable rotate left word immediate and mask test

* genericize TestSpecOutput

* explicit instruction data size

* add test for syscall/callother (disabled)

* add tests for store/load word

* add test to convert from float to int

* specify intrinsic arg type, fixes null deref

* Add PPC emulate instruction to hyper call

* add headers + formatting

* remove old comment

* Map CRALL register

* Add basic LLVM data layout that specifies 32-bit addresses

* Remove unused variables

* convert auto* to auto when possible

* RegisterPrecondition -> RegisterCondition

* fix variable name

* convert any to variant

* use std::move

* bump to c++20, use concepts

* set arch in constructor since class isn't generic anyways

* formatting

* make type aliases

* bump cxx-common

* add comment

* clang format

* throw exception if register not found

* use const ref

* use shorthand for lambda capture values

* Add more detail to data layout to include proper stack alignment

* Compare to the correct size for SUBPIECE impl

* Add Sleigh message to error

* throw exception in else case

* throw runtime error if register value has incorrect type

* use reference instead of value

* get rid of unnecessary type alias

* formatting

* Propagate VLE context reg value into Sleigh

* Remove unnecessary whitespace

* Remove stale TODO and NOTE comments

* add additional parameter to test runner to specify decoding context

* drop llvm 14, bump macos version

* bump cxx-common, fix ci.yml mac build

* add test for unconditional relative negative branch

* add missing space to pcode debug log

* fix bug due to unordered_map, iteration order matters

* add error log in case we aren't able to adjust PC value

* use helper for getting register reference

* Revert "add optional param"

This reverts commit 51ed49f8cf.

* Remove remaining LLVM 14 compatibility code and configuration

* Add padding between CR and XER flags

* Use `enum class`

* Remove void cast

* Remove unnecessary variable

* Use initialiser lists where appropriate

* Remove redundant `else`

* Prefer `CHECK` over `assert`

* Polish PowerPC function initialisation with lambda

* zero out xer_so to fix tests

* log error when we see claim_eq with no usages

* Collapse namespace blocks

* Remove unnecessary `this->`

* Use `auto` where appropriate

* Remove unnecessary `else`

* Use `emplace` over `insert` for `std::map`

Co-authored-by: lkorenc <lukas.korencik@trailofbits.com>

* Use `constexpr` for VLE reg name

* Use module verification util

* Add `VerifyFunction` util and use where applicable

* Extract lambda to improve readability of flow categorisation

* Use type alias for context values

* Introduce type alias for block exit

* Create type alias for optional branch taken

* Refactor `PcodeCFGBuilder`

* Use lambda to avoid conditional mutation

* Extract duplicated bit-shift code generation into helper

* Simplify flow with ternary

* Add `GetBlock` helper

* Rename variables

* Move statement for clarity

* Create helpers for working with Sleigh context register values

* Convert loop to `std::copy`

* Add a comment explaining the use of set to de-duplicate and sort

* Refactor `IntraProcTransferCollector`

* Expose static method to easily use `IntraProcTransferCollector`

* Rename PPC related variables to include address width

* add docs to intrainstructionindex

* remove llvm 14 ifdefs

* don't log error if no claim_eqs were used

* update comments

* Cleanup exit visitors

---------

Co-authored-by: 2over12 <ian.smith@trailofbits.com>
Co-authored-by: William Tan <1284324+Ninja3047@users.noreply.github.com>
Co-authored-by: lkorenc <lukas.korencik@trailofbits.com>
2023-02-02 10:05:28 -05:00

408 lines
9.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# Copyright (c) 2019 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.
# 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 )
DOWNLOAD_DIR="$( cd "$( dirname "${SRC_DIR}" )" && pwd )/lifting-bits-downloads"
CURR_DIR=$( pwd )
BUILD_DIR="${CURR_DIR}/remill-build"
INSTALL_DIR=/usr/local
LLVM_VERSION=llvm-15
OS_VERSION=
ARCH_VERSION=
BUILD_FLAGS=
CXX_COMMON_VERSION="0.2.22"
# There are pre-build versions of various libraries for specific
# Ubuntu releases.
function GetUbuntuOSVersion
{
# Version name of OS (e.g. xenial, trusty).
source /etc/lsb-release
case "${DISTRIB_CODENAME}" in
groovy)
echo "[!] Ubuntu 20.10 is not supported; using libraries for Ubuntu 20.04 instead"
OS_VERSION=ubuntu-20.04
return 0
;;
focal)
OS_VERSION=ubuntu-20.04
return 0
;;
*)
echo "[x] Ubuntu ${DISTRIB_CODENAME} is not supported. Only xenial (16.04) and trusty (14.04) are supported."
return 1
;;
esac
}
# Figure out the architecture of the current machine.
function GetArchVersion
{
local version
version="$( uname -m )"
case "${version}" in
x86_64)
ARCH_VERSION=amd64
return 0
;;
x86-64)
ARCH_VERSION=amd64
return 0
;;
arm64 | aarch64)
ARCH_VERSION=arm64
return 0
;;
*)
echo "[x] ${version} architecture is not supported. Only aarch64 and x86_64 (i.e. amd64) are supported."
return 1
;;
esac
}
function DownloadVcpkgLibraries
{
local GITHUB_LIBS="${LIBRARY_VERSION}.tar.xz"
local URL="https://github.com/lifting-bits/cxx-common/releases/download/v${CXX_COMMON_VERSION}/${GITHUB_LIBS}"
mkdir -p "${DOWNLOAD_DIR}"
pushd "${DOWNLOAD_DIR}" || return 1
echo "Fetching: ${URL} and placing in ${DOWNLOAD_DIR}"
if ! curl -LO "${URL}"; then
return 1
fi
local TAR_OPTIONS="--warning=no-timestamp"
if [[ "$OSTYPE" == "darwin"* ]]; then
TAR_OPTIONS=""
fi
(
set -x
tar -xJf "${GITHUB_LIBS}" ${TAR_OPTIONS}
) || return $?
rm "${GITHUB_LIBS}"
popd || return 1
# Make sure modification times are not in the future.
find "${DOWNLOAD_DIR}/${LIBRARY_VERSION}" -type f -exec touch {} \;
return 0
}
# Attempt to detect the OS distribution name.
function GetOSVersion
{
source /etc/os-release
case "${ID,,}" in
*ubuntu*)
GetUbuntuOSVersion
return 0
;;
*debian*)
OS_VERSION=ubuntu-20.04
return 0
;;
*arch*)
OS_VERSION=ubuntu-20.04
return 0
;;
[Kk]ali)
OS_VERSION=ubuntu-20.04
return 0;
;;
*)
echo "[x] ${ID} is not yet a supported distribution."
return 1
;;
esac
}
# Download pre-compiled version of cxx-common for this OS. This has things like
# google protobuf, gflags, glog, gtest, capstone, and llvm in it.
function DownloadLibraries
{
# macOS packages
if [[ "${OSTYPE}" = "darwin"* ]]; then
# Compute an isysroot from the SDK root dir.
#local sdk_root="${SDKROOT}"
#if [[ "x${sdk_root}x" = "xx" ]]; then
# sdk_root=$(xcrun -sdk macosx --show-sdk-path)
#fi
#BUILD_FLAGS="${BUILD_FLAGS} -DCMAKE_OSX_SYSROOT=${sdk_root}"
# Min version supported
OS_VERSION="macos-11"
# Hard-coded to match pre-built binaries in CI
XCODE_VERSION="13.0"
if [[ "$(sw_vers -productVersion)" == "11."* ]]; then
echo "Found MacOS Big Sur"
# Uses 10.15 binaries
OS_VERSION="macos-11"
else
echo "WARNING: ****Likely unsupported MacOS Version****"
echo "WARNING: ****Using ${OS_VERSION}****"
fi
# Linux packages
elif [[ "${OSTYPE}" = "linux-gnu" ]]; then
if ! GetOSVersion; then
return 1
fi
else
echo "[x] OS ${OSTYPE} is not supported."
return 1
fi
if ! GetArchVersion; then
return 1
fi
VCPKG_TARGET_ARCH="${ARCH_VERSION}"
if [[ "${VCPKG_TARGET_ARCH}" == "amd64" ]]; then
VCPKG_TARGET_ARCH="x64"
fi
if [[ "${OS_VERSION}" == "macos-"* ]]; then
# TODO Figure out Xcode compatibility
LIBRARY_VERSION="vcpkg_${OS_VERSION}_${LLVM_VERSION}_xcode-${XCODE_VERSION}_${ARCH_VERSION}"
VCPKG_TARGET_TRIPLET="${VCPKG_TARGET_ARCH}-osx-rel"
else
# TODO Arch version
LIBRARY_VERSION="vcpkg_${OS_VERSION}_${LLVM_VERSION}_${ARCH_VERSION}"
VCPKG_TARGET_TRIPLET="${VCPKG_TARGET_ARCH}-linux-rel"
fi
echo "[-] Library version is ${LIBRARY_VERSION}"
if [[ ! -d "${DOWNLOAD_DIR}/${LIBRARY_VERSION}" ]]; then
if ! DownloadVcpkgLibraries; then
echo "[x] Unable to download vcpkg libraries build ${LIBRARY_VERSION}."
return 1
fi
fi
return 0
}
# Configure the build.
function Configure
{
# Configure the remill build, specifying that it should use the pre-built
# Clang compiler binaries.
(
set -x
cmake \
-DCMAKE_INSTALL_PREFIX="${INSTALL_DIR}" \
-DCMAKE_VERBOSE_MAKEFILE=True \
-DCMAKE_TOOLCHAIN_FILE="${DOWNLOAD_DIR}/${LIBRARY_VERSION}/scripts/buildsystems/vcpkg.cmake" \
-DVCPKG_TARGET_TRIPLET="${VCPKG_TARGET_TRIPLET}" \
${BUILD_FLAGS} \
"${SRC_DIR}"
) || exit $?
return $?
}
# Compile the code.
function Build
{
if [[ "$OSTYPE" == "darwin"* ]]; then
NPROC=$( sysctl -n hw.ncpu )
else
NPROC=$( nproc )
fi
(
set -x
cmake --build . -- -j"${NPROC}"
) || return $?
return $?
}
# Create the packages
function Package
{
remill_tag=$(cd "${SRC_DIR}" && git describe --tags --always --abbrev=0)
remill_commit=$(cd "${SRC_DIR}" && git rev-parse HEAD | cut -c1-7)
remill_version="${remill_tag:1}.${remill_commit}"
(
set -x
if [[ -d "install" ]]; then
rm -rf "install"
fi
mkdir "install"
export DESTDIR="$(pwd)/install"
cmake --build . \
--target install
cpack -D REMILL_DATA_PATH="${DESTDIR}" \
-R ${remill_version} \
--config "${SRC_DIR}/packaging/main.cmake"
) || return $?
return $?
}
# Get a LLVM version name for the build. This is used to find the version of
# cxx-common to download.
function GetLLVMVersion
{
case ${1} in
15)
LLVM_VERSION=llvm-15
return 0
;;
*)
# unknown option
echo "[x] Unknown or unsupported LLVM version ${1}. You may be able to manually build it with cxx-common."
return 1
;;
esac
return 1
}
function Help
{
echo "Beginner build script to get started"
echo ""
echo "Options:"
echo " --prefix Change the default (${INSTALL_DIR}) installation prefix."
echo " --llvm-version Change the default (15) LLVM version."
echo " --build-dir Change the default (${BUILD_DIR}) build directory."
echo " --debug Build with Debug symbols."
echo " --extra-cmake-args Extra CMake arguments to build with."
echo " --dyinst-frontend Build McSema with dyninst frontend as well."
echo " -h --help Print help."
}
function main
{
while [[ $# -gt 0 ]] ; do
key="$1"
case $key in
-h)
Help
exit 0
;;
--help)
Help
exit 0
;;
# Change the default installation prefix.
--prefix)
INSTALL_DIR=$(python3 -c "import os; import sys; sys.stdout.write(os.path.abspath('${2}'))")
echo "[+] New install directory is ${INSTALL_DIR}"
shift # past argument
;;
# Change the default LLVM version.
--llvm-version)
if ! GetLLVMVersion "${2}" ; then
return 1
fi
echo "[+] New LLVM version is ${LLVM_VERSION}"
shift
;;
# Change the default build directory.
--build-dir)
BUILD_DIR=$(python3 -c "import os; import sys; sys.stdout.write(os.path.abspath('${2}'))")
echo "[+] New build directory is ${BUILD_DIR}"
shift # past argument
;;
# Change the default download directory.
--download-dir)
DOWNLOAD_DIR=$(python3 -c "import os; import sys; sys.stdout.write(os.path.abspath('${2}'))")
echo "[+] New download directory is ${BUILD_DIR}"
shift # past argument
;;
# Make the build type to be a debug build.
--debug)
BUILD_FLAGS="${BUILD_FLAGS} -DCMAKE_BUILD_TYPE=Debug"
echo "[+] Enabling a debug build of remill"
;;
--extra-cmake-args)
BUILD_FLAGS="${BUILD_FLAGS} ${2}"
echo "[+] Will supply additional arguments to cmake: ${BUILD_FLAGS}"
shift
;;
# tell McSema to build dyninst frontend as well
--dyninst-frontend)
GetOSVersion
if [[ $OS_VERSION != ubuntu* ]] ; then
echo "[+] Dyninst frontend is supported only on Ubuntu, try at your own peril"
read -p "Continue? (Y/N): " confirm
case $confirm in
y|Y ) echo "Confirmed";;
n|N ) exit 1;;
* ) echo "Unknown option" && exit 1;;
esac
fi
BUILD_FLAGS="${BUILD_FLAGS} -DBUILD_MCSEMA_DYNINST_DISASS=1"
echo "[+] Will build dyninst frontend"
;;
*)
# unknown option
echo "[x] Unknown option: ${key}"
return 1
;;
esac
shift # past argument or value
done
mkdir -p "${BUILD_DIR}"
cd "${BUILD_DIR}" || exit 1
if ! (DownloadLibraries && Configure && Build && Package); then
echo "[x] Build aborted."
exit 1
fi
return $?
}
main "$@"
exit $?