In-progress work on MSP430 support. Need a bunch of compatibility headers in the Remill runtime for when the standard headers aren't present. Also added the ability to disable u/int128_t support in the runtime headers, as that would have been a bag of worms.

This commit is contained in:
Peter Goodman
2021-01-10 23:14:26 -05:00
parent 37741957d6
commit 7c9a866db5
25 changed files with 1902 additions and 175 deletions
+5
View File
@@ -249,6 +249,7 @@ class Arch {
bool IsAArch64(void) const;
bool IsSPARC32(void) const;
bool IsSPARC64(void) const;
bool IsMSP430(void) const;
bool IsWindows(void) const;
bool IsLinux(void) const;
@@ -299,6 +300,10 @@ class Arch {
static ArchPtr GetSPARC64(
llvm::LLVMContext *context, OSName os, ArchName arch_name);
// Defined in `lib/Arch/MSP430/Arch.cpp`.
static ArchPtr GetMSP430(
llvm::LLVMContext *context, OSName os, ArchName arch_name);
mutable std::unique_ptr<ArchImpl> impl;
Arch(void) = delete;
+102
View File
@@ -0,0 +1,102 @@
/*
* Copyright (c) 2021 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.
*/
#pragma once
#pragma clang diagnostic push
#pragma clang diagnostic fatal "-Wpadded"
#include "remill/Arch/Runtime/State.h"
#include "remill/Arch/Runtime/Types.h"
struct Reg final {
addr_t word;
} __attribute__((packed));
struct NZCV {
bool n;
bool _0;
bool c;
bool _1;
bool z;
bool _2;
bool v;
bool _3;
};
union StatusReg {
addr_t word;
struct {
uint16_t gc:1; // Carry flag.
uint16_t z:1; // Zero flag.
uint16_t n:1; // Negative number flag.
uint16_t gie:1; // Global interrupt enable.
uint16_t cpuoff:1; // CPU off.
uint16_t oscoff:1; // Oscillator off.
uint16_t scg0:1; // System clock generator.
uint16_t scg1:1; // System clock generator.
uint16_t v:1; // Overflow flag.
uint16_t _reserved:7;
};
} __attribute__((packed));
static_assert(sizeof(StatusReg) == 2);
struct GPR {
Reg r0; // Program counter.
uint16_t _0;
Reg r1; // Stack pointer.
uint16_t _1;
StatusReg r2; // Status register.
uint16_t _2;
// NOTE(pag): `r3` is the constant zero.
Reg r4;
uint16_t _4;
Reg r5;
uint16_t _5;
Reg r6;
uint16_t _6;
Reg r7;
uint16_t _7;
Reg r8;
uint16_t _8;
Reg r9;
uint16_t _9;
Reg r10;
uint16_t _10;
Reg r11;
uint16_t _11;
Reg r12;
uint16_t _12;
Reg r13;
uint16_t _13;
Reg r14;
uint16_t _14;
Reg r15;
uint16_t _15;
} __attribute__((packed));
struct alignas(16) State : public ArchState {
GPR gpr;
NZCV nzcv;
uint32_t padding[3];
};
using MSP430State = State;
#pragma clang diagnostic pop
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2021 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.
*/
#pragma once
typedef RnW<uint16_t> R16W;
typedef Rn<uint16_t> R16;
typedef Mn<uint8_t> M8;
typedef Mn<uint16_t> M16;
typedef MnW<uint8_t> M8W;
typedef MnW<uint16_t> M16W;
typedef In<uint8_t> I8;
typedef In<uint16_t> I16;
typedef In<addr_t> PC;
typedef In<addr_t> IMM;
+18 -22
View File
@@ -16,49 +16,43 @@
#pragma once
// NOTE(pag): We don't even try to select MSP430 as Remill probably can't
// be compiled on that platform.
#define REMILL_ON_AMD64 0
#define REMILL_ON_X86 0
#define REMILL_ON_AARCH64 0
#define REMILL_ON_SPARC64 0
#define REMILL_ON_SPARC32 0
#define REMILL_ON_MSP430 0
#ifndef REMILL_ARCH
# if defined(__x86_64__)
# define REMILL_ARCH "amd64_avx"
# undef REMILL_ON_AMD64
# define REMILL_ON_AMD64 1
# define REMILL_ON_X86 0
# define REMILL_ON_AARCH64 0
# define REMILL_ON_SPARC64 0
# define REMILL_ON_SPARC32 0
# elif defined(__i386__) || defined(_M_X86)
# define REMILL_ARCH "x86"
# define REMILL_ON_AMD64 0
# undef REMILL_ON_X86
# define REMILL_ON_X86 1
# define REMILL_ON_AARCH64 0
# define REMILL_ON_SPARC64 0
# define REMILL_ON_SPARC32 0
# elif defined(__aarch64__)
# define REMILL_ARCH "aarch64"
# define REMILL_ON_AMD64 0
# define REMILL_ON_X86 0
# undef REMILL_ON_AARCH64
# define REMILL_ON_AARCH64 1
# define REMILL_ON_SPARC64 0
# define REMILL_ON_SPARC32 0
# elif defined(__sparc__) || defined(__sparc) || defined(__sparc_v8__) || defined(__sparc_v9__) || defined(__sparcv8) || defined(__sparcv9)
# define REMILL_ON_AMD64 0
# define REMILL_ON_X86 0
# define REMILL_ON_AARCH64 0
# if (defined(__LP64__) && __LP64__) || (defined(_LP64) && _LP64)
# define REMILL_ARCH "sparc64"
# undef REMILL_ON_SPARC64
# define REMILL_ON_SPARC64 1
# define REMILL_ON_SPARC32 0
# else
# define REMILL_ARCH "sparc32"
# define REMILL_ON_SPARC64 0
# undef REMILL_ON_SPARC32
# define REMILL_ON_SPARC32 1
# endif
# else
# error "Cannot infer current architecture."
# define REMILL_ARCH "invalid"
# define REMILL_ON_AMD64 0
# define REMILL_ON_X86 0
# define REMILL_ON_AARCH64 0
# define REMILL_ON_SPARC64 0
# define REMILL_ON_SPARC32 0
# endif
#endif
@@ -84,6 +78,8 @@ enum ArchName : uint32_t {
kArchSparc32,
kArchSparc64,
kArchMSP430,
};
ArchName GetArchName(const llvm::Triple &triple);
+29
View File
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2021 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.
*/
#pragma once
#ifndef __has_include
# define __has_include(...) 0
#endif
#ifndef __has_builtin
# define __has_builtin(...) 0
#endif
#ifndef __is_identifier
# define __is_identifier(...) 1
#endif
+20 -1
View File
@@ -25,21 +25,40 @@
#endif
#if 64 == ADDRESS_SIZE_BITS
# define IF_16BIT(...)
# define IF_32BIT(...)
# define IF_64BIT(...) __VA_ARGS__
# define _IF_16BIT(...)
# define _IF_32BIT(...)
# define _IF_64BIT(...) , __VA_ARGS__
# define IF_64BIT_ELSE(a, b) a
# define IF_32BIT_ELSE(a, b) b
# define IF_16BIT_ELSE(a, b) b
# define aword qword
#else
#elif 32 == ADDRESS_SIZE_BITS
# define IF_16BIT(...)
# define IF_32BIT(...) __VA_ARGS__
# define IF_64BIT(...)
# define _IF_16BIT(...)
# define _IF_32BIT(...) , __VA_ARGS__
# define _IF_64BIT(...)
# define IF_64BIT_ELSE(a, b) b
# define IF_32BIT_ELSE(a, b) a
# define IF_16BIT_ELSE(a, b) b
# define aword dword
#elif 16 == ADDRESS_SIZE_BITS
# define IF_16BIT(...) __VA_ARGS__
# define IF_32BIT(...)
# define IF_64BIT(...)
# define _IF_16BIT(...) , __VA_ARGS__
# define _IF_32BIT(...)
# define _IF_64BIT(...)
# define IF_64BIT_ELSE(a, b) b
# define IF_32BIT_ELSE(a, b) b
# define IF_16BIT_ELSE(a, b) a
# define aword word
#else
# error "Invalid number of bits in an address"
#endif
// Attributes that will force inlining of specific code.
+11 -3
View File
@@ -16,7 +16,11 @@
#pragma once
#include <cfloat>
#include "Builtin.h"
#if __has_include(<cfloat>)
# include <cfloat>
#endif
// Windows doesn't have the following macros defined
#ifndef _SW_INEXACT
@@ -33,10 +37,14 @@
# define _RC_CHOP 0x00000300 // chop
#endif
#include <cfenv>
#include <cmath>
#if __has_include(<cfenv>)
# include <cfenv>
#endif
#include "Math.h"
// macOS does not have this flag
#ifndef __FE_DENORM
# define __FE_DENORM 0x02
#endif
+1 -1
View File
@@ -16,7 +16,7 @@
#pragma once
#include <cstdint>
#include "Int.h"
class SyncHyperCall {
public:
+101
View File
@@ -0,0 +1,101 @@
/*
* Copyright (c) 2021 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.
*/
#pragma once
#include "Builtin.h"
#if __has_include(<cstdint>)
# include <cstdint>
#elif __has_include(<cinttypes>)
# include <cinttypes>
#else
#define REMILL_CUSTOM_INT_TYPES 1
using size_t = decltype(sizeof(int));
template <size_t kDesiredSize, typename... Ts>
struct TypeSelector;
template <size_t kDesiredSize, size_t kXorSize, typename... Ts>
struct TypeSelectorImpl;
template <size_t kDesiredSize, size_t kXorSize, typename T, typename... Ts>
struct TypeSelectorImpl<kDesiredSize, kXorSize, T, Ts...>
: public TypeSelector<kDesiredSize, Ts...> {};
template <size_t kDesiredSize, typename T, typename... Ts>
struct TypeSelectorImpl<kDesiredSize, 0, T, Ts...> {
using Type = T;
};
template <size_t kDesiredSize>
struct TypeSelector<kDesiredSize> {
using Type = void;
};
template <size_t kDesiredSize, typename T, typename... Ts>
struct TypeSelector<kDesiredSize, T, Ts...>
: public TypeSelectorImpl<kDesiredSize, sizeof(T) ^ kDesiredSize, T, Ts...> {};
using int8_t = signed char;
using uint8_t = unsigned char;
using int16_t = TypeSelector<2, short, int, long, long long>::Type;
using uint16_t = TypeSelector<2, unsigned short, unsigned, unsigned long, unsigned long long>::Type;
using int32_t = TypeSelector<4, int, long, long long>::Type;
using uint32_t = TypeSelector<4, unsigned, unsigned long, unsigned long long>::Type;
using int64_t = TypeSelector<8, int, long, long long>::Type;
using uint64_t = TypeSelector<8, unsigned, unsigned long, unsigned long long>::Type;
#endif // cstint, cinttypes
#if !defined(REMILL_DISABLE_INT128)
#if defined(__x86_64__) || defined(__i386__) || defined(_M_X86) || defined (__arm__)
typedef unsigned uint128_t __attribute__((mode(TI)));
typedef int int128_t __attribute__((mode(TI)));
#elif defined(__aarch64__)
typedef __uint128_t uint128_t;
typedef __int128_t int128_t;
#elif defined(__sparc__)
typedef __uint128_t uint128_t;
typedef __int128_t int128_t;
#elif !__is_identifier(_ExtInt)
typedef unsigned _ExtInt(128) uint128_t;
typedef signed _ExtInt(128) int128_t;
#else
#error "Unable to identify u/int128 type."
#endif
static_assert(sizeof(int128_t) == 16, "Invalid size for `int128_t`.");
static_assert(sizeof(uint128_t) == 16, "Invalid size for `uint128_t`.");
#endif // `!defined(REMILL_DISABLE_INT128)`
#ifdef REMILL_CUSTOM_INT_TYPES
namespace std {
inline namespace __remill {
using size_t = ::size_t;
using uint8_t = ::uint8_t;
using uint16_t = ::uint16_t;
using uint32_t = ::uint32_t;
using uint64_t = ::uint64_t;
using int8_t = ::int8_t;
using int16_t = ::int16_t;
using int32_t = ::int32_t;
using int64_t = ::int64_t;
} // namespace __remill
} // namespace std
#endif // REMILL_CUSTOM_INT_TYPES
+2
View File
@@ -162,9 +162,11 @@ __remill_compare_exchange_memory_32(Memory *, addr_t addr, uint32_t &expected,
__remill_compare_exchange_memory_64(Memory *, addr_t addr, uint64_t &expected,
uint64_t desired);
#if !defined(REMILL_DISABLE_INT128)
[[gnu::used]] extern Memory *
__remill_compare_exchange_memory_128(Memory *, addr_t addr, uint128_t &expected,
uint128_t &desired);
#endif
[[gnu::used]] extern Memory *__remill_fetch_and_add_8(Memory *, addr_t addr,
uint8_t &value);
+122
View File
@@ -0,0 +1,122 @@
/*
* Copyright (c) 2021 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.
*/
#pragma once
#if __has_include(<limits>)
# include <limits>
#else
#include "Int.h"
namespace std {
template <typename T>
struct numeric_limits;
template <>
struct numeric_limits<uint8_t> {
inline static uint8_t max(void) noexcept {
return static_cast<uint8_t>(~0u);
}
inline static uint8_t min(void) noexcept {
return static_cast<uint8_t>(0u);
}
};
template <>
struct numeric_limits<int8_t> {
inline static int8_t max(void) noexcept {
return static_cast<int8_t>(127);
}
inline static int8_t min(void) noexcept {
return static_cast<int8_t>(-128);
}
};
template <>
struct numeric_limits<uint16_t> {
inline static uint16_t max(void) noexcept {
return static_cast<uint16_t>(65535l);
}
inline static uint16_t min(void) noexcept {
return static_cast<uint16_t>(0u);
}
};
template <>
struct numeric_limits<int16_t> {
inline static int16_t max(void) noexcept {
return static_cast<int16_t>(32767l);
}
inline static int16_t min(void) noexcept {
return static_cast<int16_t>(-32768l);
}
};
template <>
struct numeric_limits<uint32_t> {
inline static uint32_t max(void) noexcept {
return static_cast<uint32_t>(4294967295ull);
}
inline static uint32_t min(void) noexcept {
return static_cast<uint32_t>(0u);
}
};
template <>
struct numeric_limits<int32_t> {
inline static int32_t max(void) noexcept {
return static_cast<int32_t>(2147483647ll);
}
inline static int32_t min(void) noexcept {
return static_cast<int32_t>(-2147483648ll);
}
};
template <>
struct numeric_limits<uint64_t> {
inline static uint64_t max(void) noexcept {
return static_cast<uint64_t>(18446744073709551615ull);
}
inline static uint64_t min(void) noexcept {
return static_cast<uint64_t>(0u);
}
};
template <>
struct numeric_limits<int64_t> {
inline static int64_t max(void) noexcept {
return static_cast<int64_t>(9223372036854775807ll);
}
inline static int64_t min(void) noexcept {
return static_cast<int64_t>(0x8000000000000000ull);
}
};
//template <>
//struct numeric_limits<int8_t> {
// inline static int8_t max(void) noexcept {
// return ~0_u8;
// }
// inline static int8_t min(void) noexcept {
// return 0_u8;
// }
//};
} // namespace std
#endif
+212
View File
@@ -0,0 +1,212 @@
/*
* Copyright (c) 2021 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.
*/
#pragma once
#include "Definitions.h"
#include "Builtin.h"
#include "Int.h"
typedef float float32_t;
static_assert(4 == sizeof(float32_t), "Invalid `float32_t` size.");
typedef double float64_t;
static_assert(8 == sizeof(float64_t), "Invalid `float64_t` size.");
typedef double float128_t;
static_assert(8 == sizeof(float128_t), "Invalid `float128_t` size.");
// TODO(pag): Assumes little endian.
struct float80_t final {
uint8_t data[10];
} __attribute__((packed));
static_assert(10 == sizeof(float80_t), "Invalid `float80_t` size.");
union nan32_t {
float32_t f;
uint32_t flat;
struct {
uint32_t payload : 22;
uint32_t is_quiet_nan : 1;
uint32_t exponent : 8;
uint32_t is_negative : 1;
} __attribute__((packed));
} __attribute__((packed));
static_assert(sizeof(float32_t) == sizeof(nan32_t),
"Invalid packing of `nan32_t`.");
union nan64_t {
float64_t d;
uint64_t flat;
struct {
uint64_t payload : 51;
uint64_t is_quiet_nan : 1;
uint64_t exponent : 11;
uint64_t is_negative : 1;
} __attribute__((packed));
} __attribute__((packed));
static_assert(sizeof(float64_t) == sizeof(nan64_t),
"Invalid packing of `nan64_t`.");
#if __has_include(<cmath>)
# include <cmath>
#else
#ifndef FP_NORMAL
# define FP_NORMAL 4
#endif
#ifndef FP_SUBNORMAL
# define FP_SUBNORMAL 3
#endif
#ifndef FP_ZERO
# define FP_ZERO 2
#endif
#ifndef FP_INFINITE
# define FP_INFINITE 1
#endif
#ifndef FP_NAN
# define FP_NAN 0
#endif
namespace remill_std {
ALWAYS_INLINE static bool signbit(float arg) {
# if __has_builtin(__builtin_signbitf)
return __builtin_signbitf(arg);
# else
nan32_t x = {arg};
return x.is_negative;
# endif
}
ALWAYS_INLINE static bool signbit(double arg) {
# if __has_builtin(__builtin_signbit)
return __builtin_signbit(arg);
# else
nan64_t x = {arg};
return x.is_negative;
# endif
}
ALWAYS_INLINE static bool signbit(long double arg) {
# if __has_builtin(__builtin_signbitl)
return __builtin_signbitl(arg);
# else
# error "Unsupported operation"
# endif
}
template <typename T>
ALWAYS_INLINE static bool signbit(T val) {
return (val >> (sizeof(T) - 1u)) & 1;
}
ALWAYS_INLINE static int fpclassify(float32_t x) {
#if __has_builtin(__builtin_fpclassify)
return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, x);
#else
nan32_t bits = {x};
#if __has_builtin(__builtin_isnan)
if (__builtin_isnan(x)) {
#else
if ((0x7F800001u <= bits.flat && bits.flat <= 0x7FBFFFFFu) ||
(0xFF800001u <= bits.flat && bits.flat <= 0xFFBFFFFFu) ||
(0x7FC00000u <= bits.flat && bits.flat <= 0x7FFFFFFFu) ||
(0xFFC00000u <= bits.flat && bits.flat <= 0xFFFFFFFFu)) {
#endif
return FP_NAN;
#if __has_builtin(__builtin_isinf)
} else if (__builtin_isinf(x)) {
#else
} else if (0x7F800000u == bits.flat || 0xFF800000u == bits.flat) {
#endif
return FP_INFINITE;
} else if (!x) {
return FP_ZERO;
#if __has_builtin(__builtin_isnormal)
} else if (!__builtin_isnormal(x)) {
#else
} else if (!bits.exponent) {
#endif
}
return FP_SUBNORMAL;
} else {
return FP_NORMAL;
}
#endif
}
ALWAYS_INLINE static int fpclassify(float64_t x) {
#if __has_builtin(__builtin_fpclassify)
return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, x);
#else
nan64_t bits = {x};
#if __has_builtin(__builtin_isnan)
if (__builtin_isnan(x)) {
#else
if ((0x7FF0000000000001 <= bits.flat &&
bits.flat <= 0x7FF7FFFFFFFFFFFFull) ||
(0xFFF0000000000001ull <= bits.flat &&
bits.flat <= 0xFFF7FFFFFFFFFFFFull) ||
(0x7FF8000000000000ull <= bits.flat &&
bits.flat <= 0x7FFFFFFFFFFFFFFFull) ||
(0xFFF8000000000000ull <= bits.flat &&
bits.flat <= 0xFFFFFFFFFFFFFFFFull)) {
#endif
return FP_NAN;
#if __has_builtin(__builtin_isinf)
} else if (__builtin_isinf(x)) {
#else
} else if (0x7FF0000000000000ull == bits.flat ||
0xFFF0000000000000ull == bits.flat) {
#endif
}
return FP_INFINITE;
} else if (!x) {
return FP_ZERO;
#if __has_builtin(__builtin_isnormal)
} else if (!__builtin_isnormal(x)) {
#else
} else if (!bits.exponent) {
#endif
return FP_SUBNORMAL;
} else {
return FP_NORMAL;
}
#endif
}
} // namespace remill_std
namespace std {
using namespace remill_std;
} // namespace std
#endif // `__has_include(<cmath>)`
+134 -62
View File
@@ -23,11 +23,14 @@ struct State;
namespace {
#if !defined(REMILL_DISABLE_INT128)
ALWAYS_INLINE static uint128_t __remill_read_memory_128(Memory *mem,
addr_t addr);
ALWAYS_INLINE static Memory *__remill_write_memory_128(Memory *mem, addr_t addr,
uint128_t val);
#endif
#define MAKE_UNDEF(n) \
ALWAYS_INLINE static uint##n##_t Undefined(uint##n##_t) { \
@@ -69,7 +72,10 @@ MAKE_SIGNED_MEM_ACCESS(8)
MAKE_SIGNED_MEM_ACCESS(16)
MAKE_SIGNED_MEM_ACCESS(32)
MAKE_SIGNED_MEM_ACCESS(64)
#if !defined(REMILL_DISABLE_INT128)
MAKE_SIGNED_MEM_ACCESS(128)
#endif
// Read a value directly.
ALWAYS_INLINE static bool _Read(Memory *, bool val) {
@@ -145,7 +151,10 @@ MAKE_MREAD(8, 8, uint, 8)
MAKE_MREAD(16, 16, uint, 16)
MAKE_MREAD(32, 32, uint, 32)
MAKE_MREAD(64, 64, uint, 64)
#if !defined(REMILL_DISABLE_INT128)
MAKE_MREAD(128, 128, uint, 128)
#endif
MAKE_MREAD(32, 32, float, f32)
MAKE_MREAD(64, 64, float, f64)
@@ -189,7 +198,10 @@ MAKE_MWRITE(8, 8, uint, uint, 8)
MAKE_MWRITE(16, 16, uint, uint, 16)
MAKE_MWRITE(32, 32, uint, uint, 32)
MAKE_MWRITE(64, 64, uint, uint, 64)
#if !defined(REMILL_DISABLE_INT128)
MAKE_MWRITE(128, 128, uint, uint, 128)
#endif
MAKE_MWRITE(32, 32, float, float, f32)
MAKE_MWRITE(64, 64, float, float, f64)
@@ -242,13 +254,15 @@ MAKE_READV(U, 8, bytes)
MAKE_READV(U, 16, words)
MAKE_READV(U, 32, dwords)
MAKE_READV(U, 64, qwords)
MAKE_READV(U, 128, dqwords)
MAKE_READV(S, 8, sbytes)
MAKE_READV(S, 16, swords)
MAKE_READV(S, 32, sdwords)
MAKE_READV(S, 64, sqwords)
#if !defined(REMILL_DISABLE_INT128)
MAKE_READV(U, 128, dqwords)
MAKE_READV(S, 128, sdqwords)
#endif
MAKE_READV(F, 32, floats)
MAKE_READV(F, 64, doubles)
@@ -285,13 +299,16 @@ MAKE_MREADV(U, 8, bytes, 8)
MAKE_MREADV(U, 16, words, 16)
MAKE_MREADV(U, 32, dwords, 32)
MAKE_MREADV(U, 64, qwords, 64)
MAKE_MREADV(U, 128, dqwords, 128)
MAKE_MREADV(S, 8, sbytes, s8)
MAKE_MREADV(S, 16, swords, s16)
MAKE_MREADV(S, 32, sdwords, s32)
MAKE_MREADV(S, 64, sqwords, s64)
#if !defined(REMILL_DISABLE_INT128)
MAKE_MREADV(U, 128, dqwords, 128)
MAKE_MREADV(S, 128, sdqwords, s128)
#endif
MAKE_MREADV(F, 32, floats, f32)
MAKE_MREADV(F, 64, doubles, f64)
@@ -333,13 +350,17 @@ MAKE_WRITEV(U, 8, bytes, VnW, uint8_t)
MAKE_WRITEV(U, 16, words, VnW, uint16_t)
MAKE_WRITEV(U, 32, dwords, VnW, uint32_t)
MAKE_WRITEV(U, 64, qwords, VnW, uint64_t)
MAKE_WRITEV(U, 128, dqwords, VnW, uint128_t)
MAKE_WRITEV(S, 8, sbytes, VnW, int8_t)
MAKE_WRITEV(S, 16, swords, VnW, int16_t)
MAKE_WRITEV(S, 32, sdwords, VnW, int32_t)
MAKE_WRITEV(S, 64, sqwords, VnW, int64_t)
#if !defined(REMILL_DISABLE_INT128)
MAKE_WRITEV(U, 128, dqwords, VnW, uint128_t)
MAKE_WRITEV(S, 128, sdqwords, VnW, int128_t)
#endif
MAKE_WRITEV(F, 32, floats, VnW, float32_t)
MAKE_WRITEV(F, 64, doubles, VnW, float64_t)
@@ -394,13 +415,16 @@ MAKE_MWRITEV(U, 8, bytes, 8, uint8_t)
MAKE_MWRITEV(U, 16, words, 16, uint16_t)
MAKE_MWRITEV(U, 32, dwords, 32, uint32_t)
MAKE_MWRITEV(U, 64, qwords, 64, uint64_t)
MAKE_MWRITEV(U, 128, dqwords, 128, uint128_t)
MAKE_MWRITEV(S, 8, sbytes, s8, int8_t)
MAKE_MWRITEV(S, 16, swords, s16, int16_t)
MAKE_MWRITEV(S, 32, sdwords, s32, int32_t)
MAKE_MWRITEV(S, 64, sqwords, s64, int64_t)
#if !defined(REMILL_DISABLE_INT128)
MAKE_MWRITEV(U, 128, dqwords, 128, uint128_t)
MAKE_MWRITEV(S, 128, sdqwords, s128, int128_t)
#endif
MAKE_MWRITEV(F, 32, floats, f32, float32_t)
MAKE_MWRITEV(F, 64, doubles, f64, float64_t)
@@ -418,10 +442,13 @@ MAKE_WRITE_REF(uint8_t)
MAKE_WRITE_REF(uint16_t)
MAKE_WRITE_REF(uint32_t)
MAKE_WRITE_REF(uint64_t)
MAKE_WRITE_REF(uint128_t)
MAKE_WRITE_REF(float32_t)
MAKE_WRITE_REF(float64_t)
#if !defined(REMILL_DISABLE_INT128)
MAKE_WRITE_REF(uint128_t)
#endif
#undef MAKE_WRITE_REF
#define MAKE_CMPXCHG(size, type_prefix, access_suffix) \
@@ -452,7 +479,10 @@ MAKE_CMPXCHG(8, uint, 8)
MAKE_CMPXCHG(16, uint, 16)
MAKE_CMPXCHG(32, uint, 32)
MAKE_CMPXCHG(64, uint, 64)
#if !defined(REMILL_DISABLE_INT128)
MAKE_CMPXCHG(128, uint, 128)
#endif
#undef MAKE_CMPXCHG
#define UCmpXchg(op, oldval, newval) _CmpXchg(memory, op, oldval, newval)
@@ -678,15 +708,18 @@ MAKE_CONVERT(int8_t, Int8)
MAKE_CONVERT(int16_t, Int16)
MAKE_CONVERT(int32_t, Int32)
MAKE_CONVERT(int64_t, Int64)
MAKE_CONVERT(int128_t, Int128)
MAKE_CONVERT(uint8_t, UInt8)
MAKE_CONVERT(uint16_t, UInt16)
MAKE_CONVERT(uint32_t, UInt32)
MAKE_CONVERT(uint64_t, UInt64)
MAKE_CONVERT(uint128_t, UInt128)
MAKE_CONVERT(float32_t, Float32)
MAKE_CONVERT(float64_t, Float64)
#if !defined(REMILL_DISABLE_INT128)
MAKE_CONVERT(int128_t, Int128)
MAKE_CONVERT(uint128_t, UInt128)
#endif
#undef MAKE_CONVERT
// Return the value as-is. This is useful when making many accessors using
@@ -851,8 +884,10 @@ ALWAYS_INLINE static auto TruncTo(T val) -> typename IntegerType<DT>::BT {
#define SReadV64(op) _SReadV64(memory, op)
#define UReadV64(op) _UReadV64(memory, op)
#if !defined(REMILL_DISABLE_INT128)
#define SReadV128(op) _SReadV128(memory, op)
#define UReadV128(op) _UReadV128(memory, op)
#endif
#define FReadV32(op) _FReadV32(memory, op)
#define FReadV64(op) _FReadV64(memory, op)
@@ -881,48 +916,57 @@ ALWAYS_INLINE static auto TruncTo(T val) -> typename IntegerType<DT>::BT {
// The purpose of the widening type is that Clang/LLVM will already extend
// the types of the inputs to their "natural" machine size, so we'll just
// make that explicit, where `addr_t` encodes the natural machine word.
// clang-format off
#define MAKE_OPS(name, op, make_int_op, make_float_op) \
make_int_op(U##name, uint8_t, addr_t, op) make_int_op( \
U##name##8, uint8_t, addr_t, \
op) make_int_op(U##name, uint16_t, addr_t, \
op) make_int_op(U##name##16, uint16_t, addr_t, op) \
make_int_op(U##name, uint32_t, addr_t, op) make_int_op( \
U##name##32, uint32_t, addr_t, \
op) make_int_op(U##name, uint64_t, uint64_t, \
op) make_int_op(U##name##64, uint64_t, uint64_t, op) \
make_int_op(U##name, uint128_t, uint128_t, op) make_int_op( \
U##name##128, uint128_t, uint128_t, \
op) make_int_op(S##name, int8_t, addr_diff_t, op) \
make_int_op(S##name##8, int8_t, addr_diff_t, op) make_int_op( \
S##name, int16_t, addr_diff_t, \
op) make_int_op(S##name##16, int16_t, addr_diff_t, op) \
make_int_op(S##name, int32_t, addr_diff_t, op) make_int_op( \
S##name##32, int32_t, addr_diff_t, op) \
make_int_op(S##name, int64_t, int64_t, op) make_int_op( \
S##name##64, int64_t, int64_t, \
op) make_int_op(S##name, int128_t, int128_t, op) \
make_int_op(S##name##128, int128_t, int128_t, op) \
make_float_op(F##name, float32_t, float32_t, op) \
make_float_op( \
F##name##32, float32_t, float32_t, \
op) make_float_op(F##name, float64_t, \
float64_t, op) \
make_float_op(F##name##64, float64_t, \
float64_t, op)
make_int_op(U##name, uint8_t, addr_t, op) \
make_int_op(U##name##8, uint8_t, addr_t, op) \
make_int_op(U##name, uint16_t, addr_t, op) \
make_int_op(U##name##16, uint16_t, addr_t, op) \
make_int_op(U##name, uint32_t, addr_t, op) \
make_int_op(U##name##32, uint32_t, addr_t, op) \
make_int_op(U##name, uint64_t, uint64_t, op) \
make_int_op(U##name##64, uint64_t, uint64_t, op) \
make_int_op(S##name, int8_t, addr_diff_t, op) \
make_int_op(S##name##8, int8_t, addr_diff_t, op) \
make_int_op(S##name, int16_t, addr_diff_t, op) \
make_int_op(S##name##16, int16_t, addr_diff_t, op) \
make_int_op(S##name, int32_t, addr_diff_t, op) \
make_int_op(S##name##32, int32_t, addr_diff_t, op) \
make_int_op(S##name, int64_t, int64_t, op) \
make_int_op(S##name##64, int64_t, int64_t, op) \
make_float_op(F##name, float32_t, float32_t, op) \
make_float_op(F##name##32, float32_t, float32_t, op) \
make_float_op(F##name, float64_t, float64_t, op) \
make_float_op(F##name##64, float64_t, float64_t, op)
MAKE_OPS(Add, +, MAKE_BINOP, MAKE_BINOP)
MAKE_OPS(Sub, -, MAKE_BINOP, MAKE_BINOP)
MAKE_OPS(Mul, *, MAKE_BINOP, MAKE_BINOP)
MAKE_OPS(Div, /, MAKE_BINOP, MAKE_BINOP)
MAKE_OPS(Rem, %, MAKE_BINOP, MAKE_NOP)
MAKE_OPS(And, &, MAKE_BINOP, MAKE_NOP)
MAKE_OPS(AndN, &~, MAKE_BINOP, MAKE_NOP)
MAKE_OPS(Or, |, MAKE_BINOP, MAKE_NOP)
MAKE_OPS(Xor, ^, MAKE_BINOP, MAKE_NOP)
MAKE_OPS(Shr, >>, MAKE_BINOP, MAKE_NOP)
MAKE_OPS(Shl, <<, MAKE_BINOP, MAKE_NOP)
MAKE_OPS(Neg, -, MAKE_UOP, MAKE_UOP)
MAKE_OPS(Not, ~, MAKE_UOP, MAKE_NOP)
#define MAKE_INT128OPS(name, op, make_int_op, make_float_op) \
make_int_op(U##name, uint128_t, uint128_t, op) \
make_int_op(U##name##128, uint128_t, uint128_t, op) \
make_int_op(S##name, int128_t, int128_t, op) \
make_int_op(S##name##128, int128_t, int128_t, op)
#define DO_MAKE_OPS(make_ops) \
make_ops(Add, +, MAKE_BINOP, MAKE_BINOP) \
make_ops(Sub, -, MAKE_BINOP, MAKE_BINOP) \
make_ops(Mul, *, MAKE_BINOP, MAKE_BINOP) \
make_ops(Div, /, MAKE_BINOP, MAKE_BINOP) \
make_ops(Rem, %, MAKE_BINOP, MAKE_NOP) \
make_ops(And, &, MAKE_BINOP, MAKE_NOP) \
make_ops(AndN, &~, MAKE_BINOP, MAKE_NOP) \
make_ops(Or, |, MAKE_BINOP, MAKE_NOP) \
make_ops(Xor, ^, MAKE_BINOP, MAKE_NOP) \
make_ops(Shr, >>, MAKE_BINOP, MAKE_NOP) \
make_ops(Shl, <<, MAKE_BINOP, MAKE_NOP) \
make_ops(Neg, -, MAKE_UOP, MAKE_UOP) \
make_ops(Not, ~, MAKE_UOP, MAKE_NOP)
// clang-format on
DO_MAKE_OPS(MAKE_OPS)
#if !defined(REMILL_DISABLE_INT128)
DO_MAKE_OPS(MAKE_INT128OPS)
#endif
template <typename T>
@@ -961,9 +1005,20 @@ MAKE_OPS(CmpLte, <=, MAKE_BOOLBINOP, MAKE_BOOLBINOP)
MAKE_OPS(CmpGt, >, MAKE_BOOLBINOP, MAKE_BOOLBINOP)
MAKE_OPS(CmpGte, >=, MAKE_BOOLBINOP, MAKE_BOOLBINOP)
#if !defined(REMILL_DISABLE_INT128)
MAKE_INT128OPS(CmpEq, ==, MAKE_BOOLBINOP, MAKE_BOOLBINOP)
MAKE_INT128OPS(CmpNeq, !=, MAKE_BOOLBINOP, MAKE_BOOLBINOP)
MAKE_INT128OPS(CmpLt, <, MAKE_BOOLBINOP, MAKE_BOOLBINOP)
MAKE_INT128OPS(CmpLte, <=, MAKE_BOOLBINOP, MAKE_BOOLBINOP)
MAKE_INT128OPS(CmpGt, >, MAKE_BOOLBINOP, MAKE_BOOLBINOP)
MAKE_INT128OPS(CmpGte, >=, MAKE_BOOLBINOP, MAKE_BOOLBINOP)
#endif
#undef MAKE_INT128OPS
#undef MAKE_UNOP
#undef MAKE_BINOP
#undef MAKE_OPS
#undef DO_MAKE_OPS
ALWAYS_INLINE static bool BAnd(bool a, bool b) {
return a && b;
@@ -1007,16 +1062,19 @@ ALWAYS_INLINE static bool BNot(bool a) {
return ret; \
}
// clang-format off
#define MAKE_BROADCASTS(op, make_int_broadcast, make_float_broadcast) \
make_int_broadcast(U##op, 8, bytes) make_int_broadcast(U##op, 16, words) \
make_int_broadcast(U##op, 32, dwords) \
make_int_broadcast(U##op, 64, qwords) \
make_int_broadcast(S##op, 8, sbytes) \
make_int_broadcast(S##op, 16, swords) \
make_int_broadcast(S##op, 32, sdwords) \
make_int_broadcast(S##op, 64, sqwords) \
make_float_broadcast(F##op, 32, floats) \
make_float_broadcast(F##op, 64, doubles)
make_int_broadcast(U##op, 8, bytes) \
make_int_broadcast(U##op, 16, words) \
make_int_broadcast(U##op, 32, dwords) \
make_int_broadcast(U##op, 64, qwords) \
make_int_broadcast(S##op, 8, sbytes) \
make_int_broadcast(S##op, 16, swords) \
make_int_broadcast(S##op, 32, sdwords) \
make_int_broadcast(S##op, 64, sqwords) \
make_float_broadcast(F##op, 32, floats) \
make_float_broadcast(F##op, 64, doubles)
// clang-format on
MAKE_BROADCASTS(Add, MAKE_BIN_BROADCAST, MAKE_BIN_BROADCAST)
MAKE_BROADCASTS(Sub, MAKE_BIN_BROADCAST, MAKE_BIN_BROADCAST)
@@ -1078,15 +1136,18 @@ MAKE_EXTRACTV(8, uint8_t, bytes, Unsigned, U)
MAKE_EXTRACTV(16, uint16_t, words, Unsigned, U)
MAKE_EXTRACTV(32, uint32_t, dwords, Unsigned, U)
MAKE_EXTRACTV(64, uint64_t, qwords, Unsigned, U)
MAKE_EXTRACTV(128, uint128_t, dqwords, Unsigned, U)
MAKE_EXTRACTV(8, int8_t, bytes, Signed, S)
MAKE_EXTRACTV(16, int16_t, words, Signed, S)
MAKE_EXTRACTV(32, int32_t, dwords, Signed, S)
MAKE_EXTRACTV(64, int64_t, qwords, Signed, S)
MAKE_EXTRACTV(128, int128_t, dqwords, Signed, S)
MAKE_EXTRACTV(32, float32_t, floats, Identity, F)
MAKE_EXTRACTV(64, float64_t, doubles, Identity, F)
#if !defined(REMILL_DISABLE_INT128)
MAKE_EXTRACTV(128, uint128_t, dqwords, Unsigned, U)
MAKE_EXTRACTV(128, int128_t, dqwords, Signed, S)
#endif
#undef MAKE_EXTRACTV
ALWAYS_INLINE static int8_t SAbs(int8_t val) {
@@ -1148,13 +1209,16 @@ MAKE_INSERTV(U, 8, uint8_t, bytes)
MAKE_INSERTV(U, 16, uint16_t, words)
MAKE_INSERTV(U, 32, uint32_t, dwords)
MAKE_INSERTV(U, 64, uint64_t, qwords)
MAKE_INSERTV(U, 128, uint128_t, dqwords)
MAKE_INSERTV(S, 8, int8_t, sbytes)
MAKE_INSERTV(S, 16, int16_t, swords)
MAKE_INSERTV(S, 32, int32_t, sdwords)
MAKE_INSERTV(S, 64, int64_t, sqwords)
#if !defined(REMILL_DISABLE_INT128)
MAKE_INSERTV(U, 128, uint128_t, dqwords)
MAKE_INSERTV(S, 128, int128_t, sdqwords)
#endif
MAKE_INSERTV(F, 32, float32_t, floats)
MAKE_INSERTV(F, 64, float64_t, doubles)
@@ -1175,13 +1239,16 @@ MAKE_UPDATEV(U, 8, uint8_t, bytes)
MAKE_UPDATEV(U, 16, uint16_t, words)
MAKE_UPDATEV(U, 32, uint32_t, dwords)
MAKE_UPDATEV(U, 64, uint64_t, qwords)
MAKE_UPDATEV(U, 128, uint128_t, dqwords)
MAKE_UPDATEV(S, 8, int8_t, sbytes)
MAKE_UPDATEV(S, 16, int16_t, swords)
MAKE_UPDATEV(S, 32, int32_t, sdwords)
MAKE_UPDATEV(S, 64, int64_t, sqwords)
#if !defined(REMILL_DISABLE_INT128)
MAKE_UPDATEV(U, 128, uint128_t, dqwords)
MAKE_UPDATEV(S, 128, int128_t, sdqwords)
#endif
MAKE_UPDATEV(F, 32, float32_t, floats)
MAKE_UPDATEV(F, 64, float64_t, doubles)
@@ -1201,13 +1268,16 @@ ALWAYS_INLINE static constexpr T _ZeroVec(void) {
#define UClearV16(...) _ZeroVec<uint16_t, decltype(__VA_ARGS__)>()
#define UClearV32(...) _ZeroVec<uint32_t, decltype(__VA_ARGS__)>()
#define UClearV64(...) _ZeroVec<uint64_t, decltype(__VA_ARGS__)>()
#define UClearV128(...) _ZeroVec<uint128_t, decltype(__VA_ARGS__)>()
#define SClearV8(...) _ZeroVec<int8_t, decltype(__VA_ARGS__)>()
#define SClearV16(...) _ZeroVec<int16_t, decltype(__VA_ARGS__)>()
#define SClearV32(...) _ZeroVec<int32_t, decltype(__VA_ARGS__)>()
#define SClearV64(...) _ZeroVec<int64_t, decltype(__VA_ARGS__)>()
#if !defined(REMILL_DISABLE_INT128)
#define UClearV128(...) _ZeroVec<uint128_t, decltype(__VA_ARGS__)>()
#define SClearV128(...) _ZeroVec<int128_t, decltype(__VA_ARGS__)>()
#endif
#define FClearV32(...) _ZeroVec<float32_t, decltype(__VA_ARGS__)>()
#define FClearV64(...) _ZeroVec<float64_t, decltype(__VA_ARGS__)>()
@@ -1440,6 +1510,7 @@ ALWAYS_INLINE static T Select(bool cond, T if_true, T if_false) {
// TODO(pag): Assumes little-endian.
#if !defined(REMILL_DISABLE_INT128)
ALWAYS_INLINE static uint128_t __remill_read_memory_128(Memory *mem,
addr_t addr) {
uint128_t low_qword = ZExt(__remill_read_memory_64(mem, addr));
@@ -1456,6 +1527,7 @@ ALWAYS_INLINE static Memory *__remill_write_memory_128(Memory *mem, addr_t addr,
mem = __remill_write_memory_64(mem, addr + 8, high_qword);
return mem;
}
#endif
// Issue #374: https://github.com/lifting-bits/remill/issues/374
//
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright (c) 2021 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.
*/
#pragma once
#include "Builtin.h"
#if __has_include(<type_traits>)
# include <type_traits>
#else
# include "Int.h"
namespace std {
template <typename T>
struct is_signed;
template <typename T>
struct is_unsigned;
#define MAKE_SIGNED(type, val) \
template <> \
struct is_signed<type> { \
static constexpr bool value = val; \
}
MAKE_SIGNED(int8_t, true);
MAKE_SIGNED(int16_t, true);
MAKE_SIGNED(int32_t, true);
MAKE_SIGNED(int64_t, true);
MAKE_SIGNED(uint8_t, false);
MAKE_SIGNED(uint16_t, false);
MAKE_SIGNED(uint32_t, false);
MAKE_SIGNED(uint64_t, false);
#undef MAKE_SIGNED
#define MAKE_UNSIGNED(type, val) \
template <> \
struct is_unsigned<type> { \
static constexpr bool value = val; \
}
MAKE_UNSIGNED(int8_t, false);
MAKE_UNSIGNED(int16_t, false);
MAKE_UNSIGNED(int32_t, false);
MAKE_UNSIGNED(int64_t, false);
MAKE_UNSIGNED(uint8_t, true);
MAKE_UNSIGNED(uint16_t, true);
MAKE_UNSIGNED(uint32_t, true);
MAKE_UNSIGNED(uint64_t, true);
#undef MAKE_UNSIGNED
template <typename A, typename B>
struct is_same {
static constexpr bool value = false;
};
template <typename A>
struct is_same<A, A> {
static constexpr bool value = true;
};
} // namespace std
#endif
+66 -84
View File
@@ -16,9 +16,10 @@
#pragma once
#include <cstdint>
#include <limits>
#include <type_traits>
#include "Int.h"
#include "Limits.h"
#include "TypeTraits.h"
#include "Float.h"
#if defined(__GNUG__) && !defined(__clang__)
# define COMPILING_WITH_GCC 1
@@ -36,69 +37,22 @@ struct Memory;
// Address in the source architecture type. We don't use a `uintptr_t` because
// that might be specific to the destination architecture type.
typedef uint16_t addr16_t;
typedef uint32_t addr32_t;
typedef uint64_t addr64_t;
typedef IF_64BIT_ELSE(addr64_t, addr32_t) addr_t;
typedef IF_64BIT_ELSE(int64_t, int32_t) addr_diff_t;
#if defined(__x86_64__) || defined(__i386__) || defined(_M_X86) || defined (__arm__)
typedef unsigned uint128_t __attribute__((mode(TI)));
typedef int int128_t __attribute__((mode(TI)));
#elif defined(__aarch64__)
typedef __uint128_t uint128_t;
typedef __int128_t int128_t;
#elif defined(__sparc__)
typedef __uint128_t uint128_t;
typedef __int128_t int128_t;
#if ADDRESS_SIZE_BITS == 64
typedef addr64_t addr_t;
typedef int64_t addr_diff_t;
#elif ADDRESS_SIZE_BITS == 32
typedef addr32_t addr_t;
typedef int32_t addr_diff_t;
#elif ADDRESS_SIZE_BITS == 16
typedef addr16_t addr_t;
typedef int16_t addr_diff_t;
#else
# error "Cannot determine (u)int128_t type of unuspported architecture."
# error "Invalid address size in bits"
#endif
static_assert(16 == sizeof(uint128_t), "Invalid `uint128_t` size.");
static_assert(16 == sizeof(int128_t), "Invalid `int128_t` size.");
typedef float float32_t;
static_assert(4 == sizeof(float32_t), "Invalid `float32_t` size.");
typedef double float64_t;
static_assert(8 == sizeof(float64_t), "Invalid `float64_t` size.");
typedef double float128_t;
static_assert(8 == sizeof(float128_t), "Invalid `float128_t` size.");
// TODO(pag): Assumes little endian.
struct float80_t final {
uint8_t data[10];
} __attribute__((packed));
static_assert(10 == sizeof(float80_t), "Invalid `float80_t` size.");
union nan32_t {
float32_t f;
struct {
uint32_t payload : 22;
uint32_t is_quiet_nan : 1;
uint32_t exponent : 8;
uint32_t is_negative : 1;
} __attribute__((packed));
} __attribute__((packed));
static_assert(sizeof(float32_t) == sizeof(nan32_t),
"Invalid packing of `nan32_t`.");
union nan64_t {
float64_t d;
struct {
uint64_t payload : 51;
uint64_t is_quiet_nan : 1;
uint64_t exponent : 11;
uint64_t is_negative : 1;
} __attribute__((packed));
} __attribute__((packed));
static_assert(sizeof(float64_t) == sizeof(nan64_t),
"Invalid packing of `nan64_t`.");
// Note: We are re-defining the `std::is_signed` type trait because we can't
// always explicitly specialize it inside of the `std` namespace.
@@ -120,6 +74,7 @@ struct is_unsigned {
#endif
};
#if !defined(REMILL_DISABLE_INT128)
template <>
struct is_signed<int128_t> {
static constexpr bool value = true;
@@ -139,6 +94,7 @@ template <>
struct is_unsigned<uint128_t> {
static constexpr bool value = true;
};
#endif
template <typename T>
struct VectorType;
@@ -204,10 +160,12 @@ MAKE_VECTOR(uint64_t, uint64, 2, 128, 16)
MAKE_VECTOR(uint64_t, uint64, 4, 256, 32)
MAKE_VECTOR(uint64_t, uint64, 8, 512, 64)
#if !defined(REMILL_DISABLE_INT128)
//MAKE_VECTOR(uint128_t, uint128, 0, 64, 8);
MAKE_VECTOR(uint128_t, uint128, 1, 128, 16)
MAKE_VECTOR(uint128_t, uint128, 2, 256, 32)
MAKE_VECTOR(uint128_t, uint128, 4, 512, 64)
#endif
MAKE_VECTOR(int8_t, int8, 1, 8, 1)
MAKE_VECTOR(int8_t, int8, 2, 16, 2)
@@ -235,10 +193,12 @@ MAKE_VECTOR(int64_t, int64, 2, 128, 16)
MAKE_VECTOR(int64_t, int64, 4, 256, 32)
MAKE_VECTOR(int64_t, int64, 8, 512, 64)
#if !defined(REMILL_DISABLE_INT128)
//MAKE_VECTOR(int128_t, int128, 0, 64, 8);
MAKE_VECTOR(int128_t, int128, 1, 128, 16)
MAKE_VECTOR(int128_t, int128, 2, 256, 32)
MAKE_VECTOR(int128_t, int128, 4, 512, 64)
#endif
MAKE_VECTOR(float, float32, 1, 32, 4)
MAKE_VECTOR(float, float32, 2, 64, 8)
@@ -317,11 +277,15 @@ static_assert(8 == sizeof(vec64_t), "Invalid structure packing of `vec64_t`.");
union vec128_t final {
#if !defined(REMILL_DISABLE_INT128)
// Make this type look like an `[1 x i128]` to LLVM. This is important for
// the cross-block alias analysis performed by remill-opt, as it enables
// remill-opt to more easily handle false dependencies.
uint128v1_t dqwords;
int128v1_t sdqwords;
#endif
uint8v16_t bytes;
uint16v8_t words;
uint32v4_t dwords;
@@ -333,7 +297,7 @@ union vec128_t final {
int16v8_t swords;
int32v4_t sdwords;
int64v2_t sqwords;
int128v1_t sdqwords;
} __attribute__((packed));
static_assert(16 == sizeof(vec128_t),
@@ -344,7 +308,6 @@ union vec256_t final {
uint16v16_t words;
uint32v8_t dwords;
uint64v4_t qwords;
uint128v2_t dqwords;
float32v8_t floats;
float64v4_t doubles;
@@ -352,7 +315,11 @@ union vec256_t final {
int16v16_t swords;
int32v8_t sdwords;
int64v4_t sqwords;
#if !defined(REMILL_DISABLE_INT128)
uint128v2_t dqwords;
int128v2_t sdqwords;
#endif
} __attribute__((packed));
static_assert(32 == sizeof(vec256_t),
@@ -363,7 +330,6 @@ union vec512_t final {
uint16v32_t words;
uint32v16_t dwords;
uint64v8_t qwords;
uint128v4_t dqwords;
float32v16_t floats;
float64v8_t doubles;
@@ -371,16 +337,14 @@ union vec512_t final {
int16v32_t swords;
int32v16_t sdwords;
int64v8_t sqwords;
#if !defined(REMILL_DISABLE_INT128)
uint128v4_t dqwords;
int128v4_t sdqwords;
#endif
} __attribute__((packed));
static_assert(64 == sizeof(vec512_t) && 64 == sizeof(vec512_t().bytes) &&
64 == sizeof(vec512_t().words) &&
64 == sizeof(vec512_t().dwords) &&
64 == sizeof(vec512_t().qwords) &&
64 == sizeof(vec512_t().dqwords) &&
64 == sizeof(vec512_t().floats) &&
64 == sizeof(vec512_t().doubles),
static_assert(64 == sizeof(vec512_t),
"Invalid structure packing of `vec512_t`.");
// An n-bit memory reference. This is implemented as an `addr_t`. Part of the
@@ -616,33 +580,38 @@ MAKE_SIGNED_INT_CHANGERS(int8_t, uint8_t)
MAKE_SIGNED_INT_CHANGERS(int16_t, uint16_t)
MAKE_SIGNED_INT_CHANGERS(int32_t, uint32_t)
MAKE_SIGNED_INT_CHANGERS(int64_t, uint64_t)
#if !defined(REMILL_DISABLE_INT128)
MAKE_SIGNED_INT_CHANGERS(int128_t, uint128_t)
#endif
MAKE_INT_TYPE(int8_t, int16_t)
MAKE_INT_TYPE(uint8_t, uint16_t)
static_assert(sizeof(NextLargerIntegerType<uint8_t>::BT) == 2, "Bad type.");
MAKE_INT_TYPE(int16_t, int32_t)
MAKE_INT_TYPE(uint16_t, uint32_t)
static_assert(sizeof(NextLargerIntegerType<uint16_t>::BT) == 4, "Bad type.");
static_assert(sizeof(NextSmallerIntegerType<uint16_t>::BT) == 1, "Bad type.");
MAKE_INT_TYPE(int32_t, int64_t)
MAKE_INT_TYPE(uint32_t, uint64_t)
MAKE_INT_TYPE(int64_t, int128_t)
MAKE_INT_TYPE(uint64_t, uint128_t)
static_assert(sizeof(NextLargerIntegerType<uint8_t>::BT) == 2, "Bad type.");
static_assert(sizeof(NextLargerIntegerType<uint16_t>::BT) == 4, "Bad type.");
static_assert(sizeof(NextLargerIntegerType<uint32_t>::BT) == 8, "Bad type.");
static_assert(sizeof(NextLargerIntegerType<uint64_t>::BT) == 16, "Bad type.");
static_assert(sizeof(NextSmallerIntegerType<uint16_t>::BT) == 1, "Bad type.");
static_assert(sizeof(NextSmallerIntegerType<uint32_t>::BT) == 2, "Bad type.");
static_assert(sizeof(NextSmallerIntegerType<uint64_t>::BT) == 4, "Bad type.");
#if !defined(REMILL_DISABLE_INT128)
MAKE_INT_TYPE(int64_t, int128_t)
MAKE_INT_TYPE(uint64_t, uint128_t)
static_assert(sizeof(NextLargerIntegerType<uint64_t>::BT) == 16, "Bad type.");
static_assert(sizeof(NextSmallerIntegerType<uint128_t>::BT) == 8, "Bad type.");
#endif
#undef MAKE_SIGNED_INT_CHANGERS
#undef MAKE_INT_TYPE
#if !defined(REMILL_DISABLE_INT128)
template <>
struct NextLargerIntegerType<uint128_t> {
typedef uint128_t BT;
@@ -652,6 +621,17 @@ template <>
struct NextLargerIntegerType<int128_t> {
typedef int128_t BT;
};
#else
template <>
struct NextLargerIntegerType<uint64_t> {
typedef uint64_t BT;
};
template <>
struct NextLargerIntegerType<int64_t> {
typedef int64_t BT;
};
#endif
// General integer type info. Useful for quickly changing between different
// integer types.
@@ -719,10 +699,6 @@ inline uint64_t operator"" _addr_t(unsigned long long value) {
return static_cast<addr_t>(value);
}
inline uint128_t operator"" _u128(unsigned long long value) {
return static_cast<uint128_t>(value);
}
inline int8_t operator"" _s8(unsigned long long value) {
return static_cast<int8_t>(value);
@@ -740,9 +716,15 @@ inline int64_t operator"" _s64(unsigned long long value) {
return static_cast<int64_t>(value);
}
#if !defined(REMILL_DISABLE_INT128)
inline uint128_t operator"" _u128(unsigned long long value) {
return static_cast<uint128_t>(value);
}
inline int128_t operator"" _s128(unsigned long long value) {
return static_cast<int128_t>(value);
}
#endif
# define auto_t(T) typename BaseType<T>::BT
+11
View File
@@ -78,6 +78,8 @@ static unsigned AddressSize(ArchName arch_name) {
case kArchInvalid:
LOG(FATAL) << "Cannot get address size for invalid arch.";
return 0;
case kArchMSP430:
return 16;
case kArchX86:
case kArchX86_AVX:
case kArchX86_AVX512:
@@ -207,6 +209,11 @@ auto Arch::Build(llvm::LLVMContext *context_, OSName os_name_,
DLOG(INFO) << "Using architecture: 64-bit SPARC";
return GetSPARC64(context_, os_name_, arch_name_);
}
case kArchMSP430: {
DLOG(INFO) << "Using architecture: TI MSP430";
return GetMSP430(context_, os_name_, arch_name_);
}
}
}
@@ -364,6 +371,10 @@ bool Arch::IsSPARC64(void) const {
return remill::kArchSparc64 == arch_name;
}
bool Arch::IsMSP430(void) const {
return remill::kArchMSP430 == arch_name;
}
bool Arch::IsWindows(void) const {
return remill::kOSWindows == os_name;
}
+2
View File
@@ -23,6 +23,7 @@ add_library(remill_arch STATIC
)
add_subdirectory(AArch64)
add_subdirectory(MSP430)
add_subdirectory(SPARC32)
add_subdirectory(SPARC64)
add_subdirectory(X86)
@@ -31,6 +32,7 @@ set_property(TARGET remill_arch PROPERTY POSITION_INDEPENDENT_CODE ON)
target_link_libraries(remill_arch LINK_PUBLIC
remill_arch_aarch64
remill_arch_msp430
remill_arch_sparc32
remill_arch_sparc64
remill_arch_x86
+1
View File
@@ -262,6 +262,7 @@ std::string Instruction::Serialize(void) const {
case kArchAArch64LittleEndian: ss << "AArch64"; break;
case kArchSparc32: ss << "SPARC32"; break;
case kArchSparc64: ss << "SPARC64"; break;
case kArchMSP430: ss << "MSP430"; break;
}
ss << " " << std::hex << pc;
+742
View File
@@ -0,0 +1,742 @@
/*
* Copyright (c) 2021 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 <glog/logging.h>
#include "remill/Arch/Arch.h"
#include "remill/Arch/Instruction.h"
#include "remill/Arch/Name.h"
#include "remill/BC/ABI.h"
#include "remill/BC/Util.h"
#include "remill/OS/OS.h"
// clang-format off
#define ADDRESS_SIZE_BITS 16
#define INCLUDED_FROM_REMILL
#include "remill/Arch/MSP430/Runtime/State.h"
// clang-format on
namespace remill {
namespace msp430 {
namespace {
static const std::string_view kSPRegName = "R1";
static const std::string_view kPCRegName = "R0";
} // namespace
class MSP430Arch final : public Arch {
public:
MSP430Arch(llvm::LLVMContext *context_, OSName os_name_, ArchName arch_name_)
: Arch(context_, os_name_, arch_name_) {}
virtual ~MSP430Arch(void) = default;
// Returns the name of the stack pointer register.
std::string_view StackPointerRegisterName(void) const final {
return kSPRegName;
}
// Returns the name of the program counter register.
std::string_view ProgramCounterRegisterName(void) const final {
return kPCRegName;
}
// Maximum number of bytes in an instruction.
uint64_t MaxInstructionSize(void) const final {
return 6;
}
// Default calling convention for this architecture.
llvm::CallingConv::ID DefaultCallingConv(void) const final {
return llvm::CallingConv::C;
}
// Populate the `__remill_basic_block` function with variables.
void PopulateBasicBlockFunction(llvm::Module *module,
llvm::Function *bb_func) const override;
llvm::Triple Triple(void) const final;
llvm::DataLayout DataLayout(void) const final;
// Decode an instruction.
bool DecodeInstruction(
uint64_t address, std::string_view instr_bytes,
Instruction &inst) const final;
// Returns `true` if memory access are little endian byte ordered.
bool MemoryAccessIsLittleEndian(void) const final {
return true;
}
// Returns `true` if a given instruction might have a delay slot.
bool MayHaveDelaySlot(const Instruction &) const final {
return false;
}
// Returns `true` if we should lift the semantics of `next_inst` as a delay
// slot of `inst`. The `branch_taken_path` tells us whether we are in the
// context of the taken path of a branch or the not-taken path of a branch.
virtual bool NextInstructionIsDelayed(const Instruction &,
const Instruction &,
bool) const final {
return false;
}
};
// Populate the `__remill_basic_block` function with variables.
void MSP430Arch::PopulateBasicBlockFunction(llvm::Module *module,
llvm::Function *bb_func) const {
#define OFFSET_OF(type, access) \
(reinterpret_cast<uintptr_t>(&reinterpret_cast<const volatile char &>( \
static_cast<type *>(nullptr)->access)))
#define REG(name, access, type) \
AddRegister(#name, type, OFFSET_OF(MSP430State, access), nullptr)
#define SUB_REG(name, access, type, parent_reg_name) \
AddRegister(#name, type, OFFSET_OF(MSP430State, access), #parent_reg_name)
auto &context = module->getContext();
auto u8 = llvm::Type::getInt8Ty(context);
auto u16 = llvm::Type::getInt16Ty(context);
auto zero_u16 = llvm::Constant::getNullValue(u16);
const auto entry_block = &bb_func->getEntryBlock();
llvm::IRBuilder<> ir(entry_block);
REG(r0, gpr.r0.word, u16);
SUB_REG(PC, gpr.r0.word, u16, r0);
REG(r1, gpr.r1.word, u16);
SUB_REG(SP, gpr.r1.word, u16, r1);
REG(r2, gpr.r2.word, u16);
SUB_REG(SR, gpr.r2.word, u16, sr);
// NOTE(pag): `r3` is hardwired zero.
ir.CreateStore(zero_u16, ir.CreateAlloca(u16, nullptr, "r3"));
ir.CreateAlloca(u16, nullptr, "ignore_write_to_r3");
REG(r4, gpr.r4.word, u16);
REG(r5, gpr.r5.word, u16);
REG(r6, gpr.r6.word, u16);
REG(r7, gpr.r7.word, u16);
REG(r8, gpr.r8.word, u16);
REG(r9, gpr.r9.word, u16);
REG(r10, gpr.r10.word, u16);
REG(r11, gpr.r11.word, u16);
REG(r12, gpr.r12.word, u16);
REG(r13, gpr.r13.word, u16);
REG(r14, gpr.r14.word, u16);
REG(r15, gpr.r15.word, u16);
REG(n, nzcv.n, u8);
REG(z, nzcv.z, u8);
REG(c, nzcv.c, u8);
REG(v, nzcv.v, u8);
const auto pc_arg = NthArgument(bb_func, kPCArgNum);
const auto state_ptr_arg = NthArgument(bb_func, kStatePointerArgNum);
ir.CreateStore(pc_arg, ir.CreateAlloca(u16, nullptr, "NEXT_PC"));
ir.CreateStore(
pc_arg, RegisterByName(kPCVariableName)->AddressOf(state_ptr_arg, ir),
false);
}
llvm::Triple MSP430Arch::Triple(void) const {
auto triple = BasicTriple();
triple.setArch(llvm::Triple::msp430);
return triple;
}
llvm::DataLayout MSP430Arch::DataLayout(void) const {
return llvm::DataLayout("e-m:e-p:16:16-i32:16-i64:16-f32:16-f64:16-a:8-n8:16-S16");
}
union InstFormat {
uint16_t flat;
struct Format0 {
uint16_t dest_reg:4;
uint16_t ad:2;
uint16_t bw:1;
uint16_t opcode:3;
uint16_t must_be_0b000100:6;
} __attribute__((packed)) f0;
struct Format1 {
int16_t pc_offset:10;
uint16_t cond:3;
uint16_t must_be_0b001:3;
} __attribute__((packed)) f1;
struct Format2 {
uint16_t dest_reg:4;
uint16_t as:2;
uint16_t bw:1;
uint16_t ad:1;
uint16_t src_reg:4;
uint16_t opcode:4;
} __attribute__((packed)) f2;
} __attribute__((packed));
static_assert(sizeof(InstFormat) == 2);
static const std::string_view kReadIntRegName[] = {
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
};
static const std::string_view kWriteIntRegName[] = {
"r0", "r1", "r2", "ignore_write_to_r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
};
// Indexed by the bits `opcode:bw`.
static const std::string_view kFormat0OpName[] = {
"RRC",
"RRCB",
"SWPB", // NOTE(pag): No distinction between byte/word form.
"SWPB", // NOTE(pag): No distinction between byte/word form.
"RRA",
"RRAB",
"SXT", // NOTE(pag): No distinction between byte/word form.
"SXT", // NOTE(pag): No distinction between byte/word form.
"PUSH",
"PUSHB",
"CALL", // NOTE(pag): No distinction between byte/word form.
"CALL", // NOTE(pag): No distinction between byte/word form.
"RETI", // NOTE(pag): No distinction between byte/word form.
"RETI", // NOTE(pag): No distinction between byte/word form.
{}, // `0b111` is unused.
{}, // `0b111` is unused.
};
// Add two fake operands for consistency of register writeback.
static void NoWriteBack(Instruction &inst) {
inst.operands.emplace_back();
auto &reg_update_op = inst.operands.back();
reg_update_op.action = Operand::kActionWrite;
reg_update_op.type = Operand::kTypeRegister;
reg_update_op.size = 16u;
reg_update_op.reg.name = kWriteIntRegName[3];
reg_update_op.reg.size = 16u;
inst.operands.emplace_back();
auto &new_reg_val_op = inst.operands.back();
new_reg_val_op.action = Operand::kActionRead;
new_reg_val_op.type = Operand::kTypeImmediate;
new_reg_val_op.size = 16u;
new_reg_val_op.imm.is_signed = false;
new_reg_val_op.imm.val = 0;
}
// Add the linear next program counter. The idea here is that the extended
// operands to MSP430 instructions are really double-indirections and more
// like pseudo operands.
static void AddNextPC(Instruction &inst) {
inst.operands.emplace_back();
auto &new_mem_addr_op = inst.operands.back();
new_mem_addr_op.addr.kind = Operand::Address::kAddressCalculation;
new_mem_addr_op.type = Operand::kTypeAddress;
new_mem_addr_op.size = 16;
new_mem_addr_op.action = Operand::kActionRead;
new_mem_addr_op.addr.address_size = 16u;
new_mem_addr_op.addr.base_reg.name = kReadIntRegName[0];
new_mem_addr_op.addr.base_reg.size = 16u;
new_mem_addr_op.addr.displacement = inst.next_pc - inst.pc;
}
// Direct read or write of a register, no writeback. We don't bother with
// specifying `8` or `16` bits for operand size here because we'll have a
// size-specific to handle that.
static void AddDirectRegisterOp(Instruction &inst, Operand::Action action,
unsigned reg_num, bool can_writeback) {
inst.operands.emplace_back();
auto &op = inst.operands.back();
op.type = Operand::kTypeRegister;
op.size = 16;
op.action = action;
if (Operand::kActionRead == action) {
inst.function += "_R";
if (reg_num == 3u) {
op.type = Operand::kTypeImmediate;
op.imm.is_signed = false;
op.imm.val = 0;
} else {
op.reg.name = kReadIntRegName[reg_num];
op.reg.size = 16;
}
} else {
inst.function += "_Rw";
op.reg.name = kWriteIntRegName[reg_num];
op.reg.size = 16;
}
if (can_writeback) {
NoWriteBack(inst);
}
}
// Direct read of a short immediate operand embedded in the instruction.
// No writeback.
static bool TryAddShortImmOp(Instruction &inst, Operand::Action action,
uint64_t val, bool can_writeback,
bool is_signed=false) {
if (Operand::kActionRead != action) {
return false;
}
inst.operands.emplace_back();
auto &op = inst.operands.back();
op.type = Operand::kTypeImmediate;
op.size = 16; // Let a byte-specific SEM interpret it otherwise.
op.action = Operand::kActionRead;
op.imm.is_signed = is_signed;
op.imm.val = val;
if (can_writeback) {
NoWriteBack(inst);
}
return false;
}
static bool TryAddAbsAddressOp(Instruction &inst, Operand::Action action,
unsigned access_size, bool can_writeback) {
// Make sure we can read the bytes.
auto offset = inst.next_pc - inst.pc;
inst.next_pc += 2u;
if (inst.bytes.size() < (inst.next_pc - inst.pc)) {
return false;
}
// Calculate the address.
uint16_t addr = inst.bytes[offset++];
addr |= static_cast<uint16_t>(inst.bytes[offset]) << 8u;
inst.operands.emplace_back();
auto &mem_addr_op = inst.operands.back();
inst.function += "_M";
if (access_size == 8) {
inst.function += "8";
} else {
inst.function += "16";
}
if (Operand::kActionWrite == action) {
mem_addr_op.addr.kind = Operand::Address::kMemoryWrite;
inst.function += "w";
} else {
mem_addr_op.addr.kind = Operand::Address::kMemoryRead;
}
mem_addr_op.type = Operand::kTypeAddress;
mem_addr_op.size = access_size;
mem_addr_op.action = action;
mem_addr_op.addr.address_size = 16;
mem_addr_op.addr.displacement = static_cast<int64_t>(static_cast<uint32_t>(addr));
if (can_writeback) {
NoWriteBack(inst);
}
return true;
}
// Try to add an operand like `x(Rn)`.
static bool TryAddIndexedAddressOp(Instruction &inst, Operand::Action action,
unsigned reg_num, unsigned access_size,
bool can_writeback) {
// Make sure we can read the bytes.
auto offset = inst.next_pc - inst.pc;
inst.next_pc += 2u;
if (inst.bytes.size() < (inst.next_pc - inst.pc)) {
return false;
}
// Calculate the index.
uint16_t udisp = inst.bytes[offset++];
udisp |= static_cast<uint16_t>(inst.bytes[offset]) << 8u;
inst.operands.emplace_back();
auto &mem_addr_op = inst.operands.back();
inst.function += "_M";
if (access_size == 8) {
inst.function += "8";
} else {
inst.function += "16";
}
if (Operand::kActionWrite == action) {
mem_addr_op.addr.kind = Operand::Address::kMemoryWrite;
inst.function += "w";
} else {
mem_addr_op.addr.kind = Operand::Address::kMemoryRead;
}
mem_addr_op.type = Operand::kTypeAddress;
mem_addr_op.size = access_size;
mem_addr_op.action = action;
mem_addr_op.addr.address_size = 16u;
mem_addr_op.addr.base_reg.name = kReadIntRegName[reg_num];
mem_addr_op.addr.base_reg.size = 16u;
mem_addr_op.addr.displacement =
static_cast<int64_t>(static_cast<int16_t>(udisp));
if (can_writeback) {
NoWriteBack(inst);
}
return true;
}
// Try to add an operand like `@Rn`.
static void AddRegIndirectMemOp(Instruction &inst, Operand::Action action,
unsigned reg_num, unsigned access_size,
bool can_writeback) {
inst.operands.emplace_back();
auto &mem_addr_op = inst.operands.back();
inst.function += "_M";
if (access_size == 8) {
inst.function += "8";
} else {
inst.function += "16";
}
if (Operand::kActionWrite == action) {
mem_addr_op.addr.kind = Operand::Address::kMemoryWrite;
inst.function += "w";
} else {
mem_addr_op.addr.kind = Operand::Address::kMemoryRead;
}
mem_addr_op.type = Operand::kTypeAddress;
mem_addr_op.size = access_size;
mem_addr_op.action = action;
mem_addr_op.addr.address_size = 16u;
mem_addr_op.addr.base_reg.name = kReadIntRegName[reg_num];
mem_addr_op.addr.base_reg.size = 16u;
mem_addr_op.addr.displacement = 0;
if (can_writeback) {
NoWriteBack(inst);
}
}
// Try to add an operand like `@Rn+`.
static void AddPostIncrementRegIndirectMemOp(Instruction &inst,
Operand::Action action,
unsigned reg_num,
unsigned access_size,
bool can_writeback) {
inst.operands.emplace_back();
auto &mem_addr_op = inst.operands.back();
inst.function += "_M";
if (access_size == 8) {
inst.function += "8";
} else {
inst.function += "16";
}
if (Operand::kActionWrite == action) {
mem_addr_op.addr.kind = Operand::Address::kMemoryWrite;
inst.function += "w";
} else {
mem_addr_op.addr.kind = Operand::Address::kMemoryRead;
}
mem_addr_op.type = Operand::kTypeAddress;
mem_addr_op.size = access_size;
mem_addr_op.action = action;
mem_addr_op.addr.address_size = 16u;
mem_addr_op.addr.base_reg.name = kReadIntRegName[reg_num];
mem_addr_op.addr.base_reg.size = 16u;
mem_addr_op.addr.displacement = 0;
if (can_writeback) {
inst.operands.emplace_back();
auto &reg_update_op = inst.operands.back();
reg_update_op.action = Operand::kActionWrite;
reg_update_op.type = Operand::kTypeRegister;
reg_update_op.size = 16u;
reg_update_op.reg.name = kWriteIntRegName[reg_num];
reg_update_op.reg.size = 16u;
inst.operands.emplace_back();
auto &new_mem_addr_op = inst.operands.back();
new_mem_addr_op.addr.kind = Operand::Address::kAddressCalculation;
new_mem_addr_op.type = Operand::kTypeAddress;
new_mem_addr_op.size = access_size;
new_mem_addr_op.action = Operand::kActionRead;
new_mem_addr_op.addr.address_size = 16u;
new_mem_addr_op.addr.base_reg.name = kReadIntRegName[reg_num];
new_mem_addr_op.addr.base_reg.size = 16u;
new_mem_addr_op.addr.displacement = access_size / 8u;
}
}
// All operands, are decoded as either register operands or memory operands,
// even if some of the register operands are technically constants. Each
// logical operand to an instruction generates three operands to a Remill
// semantic: an operand containing the value to use, and operand containing
// a register to update on a write-back operand, and a register containing
// the value to write-back.
static bool TryDecodeOperand(Instruction &inst, Operand::Action action,
uint16_t addressing_mode, uint16_t reg_num,
unsigned access_size, bool can_writeback) {
if (2 == reg_num) {
switch (addressing_mode & 0b11u) {
case 0b00u:
AddDirectRegisterOp(inst, action, reg_num, can_writeback);
return true;
case 0b01u:
return TryAddAbsAddressOp(inst, action, access_size, can_writeback);
case 0b10u:
return TryAddShortImmOp(inst, action, 4u, can_writeback);
case 0b11u:
return TryAddShortImmOp(inst, action, 8u, can_writeback);
}
} else if (3 == reg_num) {
switch (addressing_mode & 0b11u) {
case 0b00u:
return TryAddShortImmOp(inst, action, 0u, can_writeback);
case 0b01u:
return TryAddShortImmOp(inst, action, 1u, can_writeback);
case 0b10u:
return TryAddShortImmOp(inst, action, 2u, can_writeback);
case 0b11u:
return TryAddShortImmOp(
inst, action, static_cast<uint64_t>(static_cast<int64_t>(-1)),
can_writeback, true);
}
} else {
// Only the register direct and indexed register addressing modes are
// supported for destination operands.
if (action == Operand::kActionWrite && (addressing_mode & 0b10) != 0u) {
return false;
}
switch (addressing_mode & 0b11u) {
case 0b00u:
AddDirectRegisterOp(inst, action, reg_num, can_writeback);
return true;
case 0b01u:
return TryAddIndexedAddressOp(inst, action, reg_num, access_size,
can_writeback);
case 0b10u:
AddRegIndirectMemOp(inst, action, reg_num, access_size, can_writeback);
return true;
case 0b11u:
AddPostIncrementRegIndirectMemOp(inst, action, reg_num, access_size,
can_writeback);
return true;
}
}
return false;
}
static bool TryDecodeRRC(remill::Instruction &inst,
InstFormat::Format0 native) {
if (!TryDecodeOperand(inst, Operand::kActionWrite, native.ad,
native.dest_reg, native.bw ? 8u : 16u, true)) {
return false;
}
AddNextPC(inst);
return true;
}
static bool TryDecodeSWP(remill::Instruction &inst,
InstFormat::Format0 native) {
if (!TryDecodeOperand(inst, Operand::kActionWrite, native.ad,
native.dest_reg, 16u, true)) {
return false;
}
AddNextPC(inst);
return true;
}
static bool TryDecodeRRA(remill::Instruction &inst,
InstFormat::Format0 native) {
if (!TryDecodeOperand(inst, Operand::kActionWrite, native.ad,
native.dest_reg, native.bw ? 8u : 16u, true)) {
return false;
}
AddNextPC(inst);
return true;
}
static bool TryDecodeSXT(remill::Instruction &inst,
InstFormat::Format0 native) {
if (!TryDecodeOperand(inst, Operand::kActionWrite, native.ad,
native.dest_reg, 16u, true)) {
return false;
}
AddNextPC(inst);
return true;
}
static bool TryDecodePUSH(remill::Instruction &inst,
InstFormat::Format0 native) {
// CPU BUG: PUSH #4 and PUSH #8 do not work when the short encoding
// using @r2 and @r2+ is used.
if (native.dest_reg == 2 && (native.ad == 0b10u || native.ad == 0b11u)) {
return false;
}
if (!TryDecodeOperand(inst, Operand::kActionWrite, native.ad,
native.dest_reg, native.bw ? 8u : 16u, true)) {
return false;
}
AddNextPC(inst);
return true;
}
static bool TryDecodeCALL(remill::Instruction &inst,
InstFormat::Format0 native) {
return false;
}
static bool TryDecodeRETI(remill::Instruction &inst,
InstFormat::Format0 native) {
return false;
}
static bool TryDecodeInvalidFormat0(remill::Instruction &,
InstFormat::Format0 ) {
return false;
}
static bool (*kFormat0Decoders[])(remill::Instruction &,
InstFormat::Format0) = {
TryDecodeRRC,
TryDecodeSWP,
TryDecodeRRA,
TryDecodeSXT,
TryDecodePUSH,
TryDecodeCALL,
TryDecodeRETI,
TryDecodeInvalidFormat0
};
static bool TryDecodeFormat0(remill::Instruction &inst,
InstFormat::Format0 native) {
auto op_name = kFormat0OpName[(native.opcode << 1u) | native.bw];
if (op_name.empty()) {
return false;
}
inst.function = op_name;
return kFormat0Decoders[native.opcode](inst, native);
}
static bool TryDecodeFormat1(remill::Instruction &inst,
InstFormat::Format1 native) {
return false;
}
static bool TryDecodeFormat2(remill::Instruction &inst,
InstFormat::Format2 native) {
return false;
}
// Decode an instruction.
bool MSP430Arch::DecodeInstruction(
uint64_t address, std::string_view inst_bytes, Instruction &inst) const {
inst.pc = address;
inst.arch_name = arch_name;
inst.arch = this;
inst.category = Instruction::kCategoryInvalid;
inst.operands.clear();
inst.branch_taken_pc = 0;
inst.branch_not_taken_pc = 0;
inst.has_branch_taken_delay_slot = false;
inst.has_branch_not_taken_delay_slot = false;
// We use `inst.next_pc` as a sort of accumulator to track what pseudo
// operands will be implicitly "jumped over" by this instruction. E.g.
// adding an operand that does `@r0+` will increment `inst.next_pc`.
inst.next_pc = inst.pc + 2u;
// The instruction address must be even.
if (address & 1u) {
return false;
}
// All instructions are two bytes. Some instructions refer to operands that
// are embedded in memory beside the instruction itself, and so adjacent
// constants are a form of pseudo operand.
if (inst_bytes.size() != 2 && inst_bytes.size() != 4 &&
inst_bytes.size() != 6) {
return false;
}
if (!inst.bytes.empty() && inst.bytes.data() == inst_bytes.data()) {
inst.bytes.resize(inst_bytes.size());
} else {
inst.bytes = inst_bytes;
}
InstFormat format = {};
format.flat = static_cast<uint16_t>(inst.bytes[0]);
format.flat |= static_cast<uint16_t>(inst.bytes[1]) << 8u;
auto ret = false;
if (format.f0.must_be_0b000100 == 0b000100u) {
ret = TryDecodeFormat0(inst, format.f0);
} else if (format.f1.must_be_0b001 == 0b001u) {
ret = TryDecodeFormat1(inst, format.f1);
} else {
ret = TryDecodeFormat2(inst, format.f2);
}
if (!ret) {
inst.category = Instruction::kCategoryInvalid;
inst.operands.clear();
LOG(ERROR)
<< "Unable to decode: " << inst.Serialize();
return false;
}
return inst.IsValid();
}
} // namespace msp430
Arch::ArchPtr Arch::GetMSP430(
llvm::LLVMContext *context_, OSName os_name_, ArchName arch_name_) {
if (arch_name_ == kArchMSP430) {
return std::make_unique<msp430::MSP430Arch>(context_, os_name_, arch_name_);
} else {
LOG(FATAL)
<< "Invalid arch name passed to Arch::GetMSP430::"
<< GetArchName(arch_name_);
return {};
}
}
} // namespace remill
+41
View File
@@ -0,0 +1,41 @@
# Copyright (c) 2021 Trail of Bits, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
add_library(remill_arch_msp430 STATIC
"${REMILL_INCLUDE_DIR}/remill/Arch/Runtime/Definitions.h"
"${REMILL_INCLUDE_DIR}/remill/Arch/Runtime/HyperCall.h"
"${REMILL_INCLUDE_DIR}/remill/Arch/Runtime/Intrinsics.h"
"${REMILL_INCLUDE_DIR}/remill/Arch/Runtime/Operators.h"
"${REMILL_INCLUDE_DIR}/remill/Arch/Runtime/Runtime.h"
"${REMILL_INCLUDE_DIR}/remill/Arch/Runtime/State.h"
"${REMILL_INCLUDE_DIR}/remill/Arch/Runtime/Types.h"
"${REMILL_INCLUDE_DIR}/remill/Arch/MSP430/Runtime/State.h"
"${REMILL_INCLUDE_DIR}/remill/Arch/MSP430/Runtime/Types.h"
Arch.cpp
)
add_subdirectory(Runtime)
set_property(TARGET remill_arch_msp430 PROPERTY POSITION_INDEPENDENT_CODE ON)
target_link_libraries(remill_arch_msp430 LINK_PUBLIC
remill_settings
)
install(
TARGETS remill_arch_msp430
EXPORT remillTargets
)
+24
View File
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2021 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/MSP430/Runtime/State.h"
extern "C" {
// Instructions will be lifted into clones of this function.
[[gnu::used]] Memory *__remill_basic_block(State &, addr_t, Memory *);
} // extern C
+65
View File
@@ -0,0 +1,65 @@
# Copyright (c) 2021 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(msp430_runtime)
set(MSP430RUNTIME_SOURCEFILES
Instructions.cpp
BasicBlock.cpp
"${REMILL_LIB_DIR}/Arch/Runtime/Intrinsics.cpp"
)
set_source_files_properties(Instructions.cpp PROPERTIES COMPILE_FLAGS "-O3 -g0")
set_source_files_properties(BasicBlock.cpp PROPERTIES COMPILE_FLAGS "-O0 -g3")
if (REMILL_BARRIER_AS_NOP)
set(EXTRA_BC_FLAGS "-DREMILL_BARRIER_AS_NOP")
endif(REMILL_BARRIER_AS_NOP)
function(add_runtime_helper target_name)
message(" > Generating runtime target: ${target_name}")
# Visual C++ requires C++14
if(WIN32)
set(required_cpp_standard "c++14")
else()
set(required_cpp_standard "c++17")
endif()
add_runtime(${target_name}
SOURCES ${MSP430RUNTIME_SOURCEFILES}
ADDRESS_SIZE 16
DEFINITIONS "LITTLE_ENDIAN=1" "REMILL_DISABLE_INT128=1"
BCFLAGS "-m16" "--target=msp430-elf" "-std=${required_cpp_standard}" "${EXTRA_BC_FLAGS}"
INCLUDEDIRECTORIES "${REMILL_INCLUDE_DIR}" "${REMILL_SOURCE_DIR}"
INSTALLDESTINATION "${REMILL_INSTALL_SEMANTICS_DIR}"
DEPENDENCIES
"${REMILL_INCLUDE_DIR}/remill/Arch/Runtime/Float.h"
"${REMILL_INCLUDE_DIR}/remill/Arch/Runtime/State.h"
"${REMILL_INCLUDE_DIR}/remill/Arch/Runtime/Types.h"
"${REMILL_INCLUDE_DIR}/remill/Arch/Runtime/Operators.h"
"${REMILL_INCLUDE_DIR}/remill/Arch/Runtime/Intrinsics.h"
"${REMILL_INCLUDE_DIR}/remill/Arch/Runtime/HyperCall.h"
"${REMILL_INCLUDE_DIR}/remill/Arch/Runtime/Definitions.h"
"${REMILL_INCLUDE_DIR}/remill/Arch/MSP430/Runtime/State.h"
"${REMILL_INCLUDE_DIR}/remill/Arch/MSP430/Runtime/Types.h"
"${REMILL_LIB_DIR}/Arch/Runtime/Intrinsics.cpp"
)
endfunction()
add_runtime_helper(msp430)
+67
View File
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2021 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 "remill/Arch/MSP430/Runtime/State.h"
#include "remill/Arch/MSP430/Runtime/Types.h"
#define REG_PC state.gpr.r0.word
#define REG_SP state.gpr.r1.word
#define REG_SR state.gpr.r2.word
namespace {
template <typename T>
DEF_SEM(RRC, T src_dst, R16W writeback_reg, R16 writeback_val, PC next_pc) {
auto val = Read(src_dst);
auto carry_in = static_cast<uint16_t>(state.nzcv.c);
Write(writeback_reg, Read(writeback_val));
Write(REG_PC, Read(next_pc));
Write(src_dst, UOr(UShr(val, 1_u16), UShl(carry_in, 15_u16)));
state.nzcv.c = !!UAnd(val, 1_u16);
return memory;
}
DEF_SEM(RRCB_REG, R16W src_dst, R16W writeback_reg, R16 writeback_val, PC next_pc) {
auto val = Read(src_dst);
auto val_low = static_cast<uint8_t>(val);
auto carry_in = static_cast<uint8_t>(state.nzcv.c);
auto new_val_low = UOr(UShr(val_low, 1_u8), UShl(carry_in, 7_u8));
Write(writeback_reg, Read(writeback_val));
Write(REG_PC, Read(next_pc));
Write(src_dst, UOr(UAnd(val, 0xFF00_u16), ZExt(new_val_low)));
state.nzcv.c = !!UAnd(val_low, 1_u8);
return memory;
}
DEF_SEM(RRCB_MEM, M8W src_dst, R16W writeback_reg, R16 writeback_val, PC next_pc) {
auto val_low = static_cast<uint8_t>(Read(src_dst));
auto carry_in = static_cast<uint8_t>(state.nzcv.c);
Write(writeback_reg, Read(writeback_val));
Write(REG_PC, Read(next_pc));
Write(src_dst, UOr(UShr(val_low, 1_u8), UShl(carry_in, 7_u8)));
state.nzcv.c = !!UAnd(val_low, 1_u8);
return memory;
}
} // namespace
DEF_ISEL(RRC_Rw) = RRC<R16W>;
DEF_ISEL(RRC_M16w) = RRC<M16W>;
DEF_ISEL(RRCB_Rw) = RRCB_REG;
DEF_ISEL(RRCB_M8w) = RRCB_MEM;
+7
View File
@@ -25,6 +25,9 @@ ArchName GetArchName(const llvm::Triple &triple) {
case llvm::Triple::ArchType::x86: return kArchX86;
case llvm::Triple::ArchType::x86_64: return kArchAMD64;
case llvm::Triple::ArchType::aarch64: return kArchAArch64LittleEndian;
case llvm::Triple::ArchType::sparc: return kArchSparc32;
case llvm::Triple::ArchType::sparcv9: return kArchSparc32;
case llvm::Triple::ArchType::msp430: return kArchMSP430;
default: return kArchInvalid;
}
}
@@ -57,6 +60,9 @@ ArchName GetArchName(std::string_view arch_name) {
} else if (arch_name == "sparc64") {
return kArchSparc64;
} else if (arch_name == "msp430") {
return kArchMSP430;
} else {
return kArchInvalid;
}
@@ -75,6 +81,7 @@ static const std::string_view kArchNames[] = {
[kArchAArch64LittleEndian] = "aarch64",
[kArchSparc32] = "sparc32",
[kArchSparc64] = "sparc64",
[kArchMSP430] = "msp430",
};
} // namespace
+4 -2
View File
@@ -16,8 +16,6 @@
#include "remill/Arch/Runtime/Intrinsics.h"
#include "remill/Arch/Runtime/Operators.h"
#define USED(sym) __remill_mark_as_used(reinterpret_cast<const void *>(&sym))
// This is two big hacks:
@@ -70,6 +68,10 @@ extern "C" void __remill_mark_as_used(const void *);
USED(__remill_compare_exchange_memory_32);
USED(__remill_compare_exchange_memory_64);
#if !defined(REMILL_DISABLE_INT128)
USED(__remill_compare_exchange_memory_128);
#endif
USED(__remill_fetch_and_add_8);
USED(__remill_fetch_and_add_16);
USED(__remill_fetch_and_add_32);