mirror of
https://github.com/lifting-bits/remill
synced 2026-06-21 13:56:07 +00:00
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:
@@ -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;
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "Int.h"
|
||||
|
||||
class SyncHyperCall {
|
||||
public:
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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>)`
|
||||
@@ -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
|
||||
//
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 ®_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 ®_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
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user