mirror of
https://github.com/lifting-bits/remill
synced 2026-06-21 13:56:07 +00:00
Ian/recover flags (#559)
* added cross compile semantics * sorta fix for m1 macs, shouldnt hardcode macosx version * added flag intrinsics * changed ordering for easy compare * made comparison hints easier to reference * added neq and eq instrinsics * added more intrinsics * added intrinsics to tests
This commit is contained in:
@@ -164,8 +164,13 @@ function(add_runtime target_name)
|
||||
set(additional_windows_settings "-D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH")
|
||||
endif()
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
set(target_decl "-target" "x86_64-apple-macosx11.0.0")
|
||||
endif()
|
||||
|
||||
|
||||
add_custom_command(OUTPUT "${absolute_output_file_path}"
|
||||
COMMAND "${CMAKE_BC_COMPILER}" ${include_directory_list} ${additional_windows_settings} "-DADDRESS_SIZE_BITS=${address_size}" ${definition_list} ${DEFAULT_BC_COMPILER_FLAGS} ${bc_flag_list} ${source_file_option_list} -c "${absolute_source_file_path}" -o "${absolute_output_file_path}"
|
||||
COMMAND "${CMAKE_BC_COMPILER}" ${include_directory_list} ${additional_windows_settings} ${target_decl} "-DADDRESS_SIZE_BITS=${address_size}" ${definition_list} ${DEFAULT_BC_COMPILER_FLAGS} ${bc_flag_list} ${source_file_option_list} -c "${absolute_source_file_path}" -o "${absolute_output_file_path}"
|
||||
MAIN_DEPENDENCY "${absolute_source_file_path}"
|
||||
${dependency_list_directive}
|
||||
COMMENT "Building BC object ${absolute_output_file_path}"
|
||||
|
||||
@@ -57,8 +57,8 @@ __remill_write_memory_64(Memory *, addr_t, uint64_t);
|
||||
[[gnu::used, gnu::const]] extern float64_t __remill_read_memory_f64(Memory *,
|
||||
addr_t);
|
||||
|
||||
[[gnu::used]] extern Memory* __remill_read_memory_f80(Memory *, addr_t,
|
||||
native_float80_t&);
|
||||
[[gnu::used]] extern Memory *__remill_read_memory_f80(Memory *, addr_t,
|
||||
native_float80_t &);
|
||||
|
||||
[[gnu::used]] extern float128_t __remill_read_memory_f128(Memory *, addr_t);
|
||||
|
||||
@@ -68,8 +68,8 @@ __remill_write_memory_f32(Memory *, addr_t, float32_t);
|
||||
[[gnu::used, gnu::const]] extern Memory *
|
||||
__remill_write_memory_f64(Memory *, addr_t, float64_t);
|
||||
|
||||
[[gnu::used]] extern Memory *__remill_write_memory_f80(Memory *, addr_t,
|
||||
const native_float80_t&);
|
||||
[[gnu::used]] extern Memory *
|
||||
__remill_write_memory_f80(Memory *, addr_t, const native_float80_t &);
|
||||
|
||||
[[gnu::used]] extern Memory *__remill_write_memory_f128(Memory *, addr_t,
|
||||
float128_t);
|
||||
@@ -90,6 +90,39 @@ __remill_write_memory_f64(Memory *, addr_t, float64_t);
|
||||
|
||||
[[gnu::used, gnu::const]] extern float128_t __remill_undefined_f128(void);
|
||||
|
||||
[[gnu::used, gnu::const]] extern bool
|
||||
__remill_flag_computation_zero(bool result, ...);
|
||||
|
||||
[[gnu::used, gnu::const]] extern bool
|
||||
__remill_flag_computation_sign(bool result, ...);
|
||||
|
||||
[[gnu::used, gnu::const]] extern bool
|
||||
__remill_flag_computation_overflow(bool result, ...);
|
||||
|
||||
[[gnu::used, gnu::const]] extern bool
|
||||
__remill_flag_computation_carry(bool result, ...);
|
||||
|
||||
[[gnu::used, gnu::const]] extern bool __remill_compare_sle(bool result);
|
||||
|
||||
[[gnu::used, gnu::const]] extern bool __remill_compare_slt(bool result);
|
||||
|
||||
[[gnu::used, gnu::const]] extern bool __remill_compare_sge(bool result);
|
||||
|
||||
[[gnu::used, gnu::const]] extern bool __remill_compare_sgt(bool result);
|
||||
|
||||
|
||||
[[gnu::used, gnu::const]] extern bool __remill_compare_ule(bool result);
|
||||
|
||||
[[gnu::used, gnu::const]] extern bool __remill_compare_ult(bool result);
|
||||
|
||||
[[gnu::used, gnu::const]] extern bool __remill_compare_ugt(bool result);
|
||||
|
||||
[[gnu::used, gnu::const]] extern bool __remill_compare_uge(bool result);
|
||||
|
||||
[[gnu::used, gnu::const]] extern bool __remill_compare_eq(bool result);
|
||||
|
||||
[[gnu::used, gnu::const]] extern bool __remill_compare_neq(bool result);
|
||||
|
||||
// Generic error.
|
||||
[[gnu::used]] extern Memory *__remill_error(State &, addr_t addr, Memory *);
|
||||
|
||||
|
||||
@@ -87,6 +87,19 @@ class IntrinsicTable {
|
||||
llvm::Function *undefined_f64;
|
||||
llvm::Function *undefined_f80;
|
||||
|
||||
|
||||
// Flag markers
|
||||
llvm::Function *flag_computation_zero;
|
||||
llvm::Function *flag_computation_sign;
|
||||
llvm::Function *flag_computation_overflow;
|
||||
llvm::Function *flag_computation_carry;
|
||||
|
||||
llvm::Function *compare_sle;
|
||||
llvm::Function *compare_sgt;
|
||||
llvm::Function *compare_eq;
|
||||
llvm::Function *compare_neq;
|
||||
|
||||
|
||||
private:
|
||||
IntrinsicTable(void) = delete;
|
||||
};
|
||||
|
||||
@@ -116,4 +116,22 @@ extern "C" void __remill_mark_as_used(const void *);
|
||||
USED(__remill_undefined_f32);
|
||||
USED(__remill_undefined_f64);
|
||||
USED(__remill_undefined_f80);
|
||||
|
||||
USED(__remill_flag_computation_zero);
|
||||
USED(__remill_flag_computation_overflow);
|
||||
USED(__remill_flag_computation_sign);
|
||||
USED(__remill_flag_computation_carry);
|
||||
|
||||
USED(__remill_compare_sle);
|
||||
USED(__remill_compare_slt);
|
||||
USED(__remill_compare_sgt);
|
||||
USED(__remill_compare_sge);
|
||||
|
||||
USED(__remill_compare_eq);
|
||||
USED(__remill_compare_neq);
|
||||
|
||||
USED(__remill_compare_ugt);
|
||||
USED(__remill_compare_uge);
|
||||
USED(__remill_compare_ult);
|
||||
USED(__remill_compare_ule);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ DEF_SEM(JNLE, R8W cond, PC taken, PC not_taken,
|
||||
IF_32BIT_ELSE(R32W, R64W) pc_dst) {
|
||||
addr_t taken_pc = Read(taken);
|
||||
addr_t not_taken_pc = Read(not_taken);
|
||||
auto take_branch = BAnd(BNot(FLAG_ZF), BXnor(FLAG_SF, FLAG_OF));
|
||||
auto take_branch =
|
||||
__remill_compare_sgt(BAnd(BNot(FLAG_ZF), BXnor(FLAG_SF, FLAG_OF)));
|
||||
Write(cond, take_branch);
|
||||
Write(pc_dst, Select<addr_t>(take_branch, taken_pc, not_taken_pc));
|
||||
return memory;
|
||||
@@ -170,7 +171,7 @@ DEF_SEM(JLE, R8W cond, PC taken, PC not_taken,
|
||||
IF_32BIT_ELSE(R32W, R64W) pc_dst) {
|
||||
addr_t taken_pc = Read(taken);
|
||||
addr_t not_taken_pc = Read(not_taken);
|
||||
auto take_branch = BOr(FLAG_ZF, BXor(FLAG_SF, FLAG_OF));
|
||||
auto take_branch = __remill_compare_sle(BOr(FLAG_ZF, BXor(FLAG_SF, FLAG_OF)));
|
||||
Write(cond, take_branch);
|
||||
Write(pc_dst, Select<addr_t>(take_branch, taken_pc, not_taken_pc));
|
||||
return memory;
|
||||
|
||||
@@ -25,19 +25,19 @@ enum : uint32_t { kLHS = 2415899639U, kRHS = 70623199U };
|
||||
// Zero flags, tells us whether or not a value is zero.
|
||||
template <typename T>
|
||||
[[gnu::const]] ALWAYS_INLINE static bool ZeroFlag(T res) {
|
||||
return T(0) == res;
|
||||
return __remill_flag_computation_zero(T(0) == res, res);
|
||||
}
|
||||
|
||||
// Zero flags, tells us whether or not a value is zero.
|
||||
template <typename T>
|
||||
[[gnu::const]] ALWAYS_INLINE static bool NotZeroFlag(T res) {
|
||||
return T(0) != res;
|
||||
return !__remill_flag_computation_zero(T(0) == res, res);
|
||||
}
|
||||
|
||||
// Sign flag, tells us if a result is signed or unsigned.
|
||||
template <typename T>
|
||||
[[gnu::const]] ALWAYS_INLINE static bool SignFlag(T res) {
|
||||
return 0 > Signed(res);
|
||||
return __remill_flag_computation_sign(0 > Signed(res), res);
|
||||
}
|
||||
|
||||
// Auxiliary carry flag. This is used for binary coded decimal operations and
|
||||
@@ -97,7 +97,8 @@ struct Overflow<tag_add> {
|
||||
const T sign_lhs = lhs >> kSignShift;
|
||||
const T sign_rhs = rhs >> kSignShift;
|
||||
const T sign_res = res >> kSignShift;
|
||||
return 2 == ((sign_lhs ^ sign_res) + (sign_rhs ^ sign_res));
|
||||
return __remill_flag_computation_overflow(
|
||||
2 == ((sign_lhs ^ sign_res) + (sign_rhs ^ sign_res)), lhs, rhs, res);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -114,7 +115,8 @@ struct Overflow<tag_sub> {
|
||||
const T sign_lhs = lhs >> kSignShift;
|
||||
const T sign_rhs = rhs >> kSignShift;
|
||||
const T sign_res = res >> kSignShift;
|
||||
return 2 == ((sign_lhs ^ sign_rhs) + (sign_lhs ^ sign_res));
|
||||
return __remill_flag_computation_overflow(
|
||||
2 == ((sign_lhs ^ sign_rhs) + (sign_lhs ^ sign_res)), lhs, rhs, res);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -126,10 +128,11 @@ struct Overflow<tag_mul> {
|
||||
// the operands.
|
||||
template <typename T, typename R>
|
||||
[[gnu::const]] ALWAYS_INLINE static bool
|
||||
Flag(T, T, R res,
|
||||
Flag(T lhs, T rhs, R res,
|
||||
typename std::enable_if<sizeof(T) < sizeof(R), int>::type = 0) {
|
||||
|
||||
return static_cast<R>(static_cast<T>(res)) != res;
|
||||
return __remill_flag_computation_overflow(
|
||||
static_cast<R>(static_cast<T>(res)) != res, lhs, rhs, res);
|
||||
}
|
||||
|
||||
// Signed integer multiplication overflow check, where the result is
|
||||
@@ -155,7 +158,8 @@ struct Carry<tag_add> {
|
||||
[[gnu::const]] ALWAYS_INLINE static bool Flag(T lhs, T rhs, T res) {
|
||||
static_assert(std::is_unsigned<T>::value,
|
||||
"Invalid specialization of `Carry::Flag` for addition.");
|
||||
return res < lhs || res < rhs;
|
||||
return __remill_flag_computation_carry(res < lhs || res < rhs, lhs, rhs,
|
||||
res);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -163,16 +167,19 @@ struct Carry<tag_add> {
|
||||
template <>
|
||||
struct Carry<tag_sub> {
|
||||
template <typename T>
|
||||
[[gnu::const]] ALWAYS_INLINE static bool Flag(T lhs, T rhs, T) {
|
||||
[[gnu::const]] ALWAYS_INLINE static bool Flag(T lhs, T rhs, T res) {
|
||||
static_assert(std::is_unsigned<T>::value,
|
||||
"Invalid specialization of `Carry::Flag` for addition.");
|
||||
return lhs < rhs;
|
||||
return __remill_flag_computation_carry(lhs < rhs, lhs, rhs, res);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#define UndefFlag(name) do { state.aflag.name = __remill_undefined_8(); } while (false)
|
||||
#define UndefFlag(name) \
|
||||
do { \
|
||||
state.aflag.name = __remill_undefined_8(); \
|
||||
} while (false)
|
||||
|
||||
#define ClearArithFlags() \
|
||||
do { \
|
||||
|
||||
@@ -130,7 +130,23 @@ IntrinsicTable::IntrinsicTable(llvm::Module *module)
|
||||
undefined_64(FindPureIntrinsic(module, "__remill_undefined_64")),
|
||||
undefined_f32(FindPureIntrinsic(module, "__remill_undefined_f32")),
|
||||
undefined_f64(FindPureIntrinsic(module, "__remill_undefined_f64")),
|
||||
undefined_f80(FindPureIntrinsic(module, "__remill_undefined_f80")) {
|
||||
undefined_f80(FindPureIntrinsic(module, "__remill_undefined_f80")),
|
||||
|
||||
// Flag computations
|
||||
flag_computation_zero(
|
||||
FindPureIntrinsic(module, "__remill_flag_computation_zero")),
|
||||
flag_computation_sign(
|
||||
FindPureIntrinsic(module, "__remill_flag_computation_sign")),
|
||||
flag_computation_overflow(
|
||||
FindPureIntrinsic(module, "__remill_flag_computation_overflow")),
|
||||
flag_computation_carry(
|
||||
FindPureIntrinsic(module, "__remill_flag_computation_carry")),
|
||||
// compares
|
||||
compare_sle(FindPureIntrinsic(module, "__remill_compare_sle")),
|
||||
compare_sgt(FindPureIntrinsic(module, "__remill_compare_sgt")),
|
||||
compare_eq(FindPureIntrinsic(module, "__remill_compare_eq")),
|
||||
compare_neq(FindPureIntrinsic(module, "__remill_compare_neq")) {
|
||||
|
||||
|
||||
// Make sure to set the correct attributes on this to make sure that
|
||||
// it's never optimized away.
|
||||
|
||||
+63
-7
@@ -88,12 +88,10 @@ extern "C" {
|
||||
// initial state for the lifted testcase. The lifted test case code mutates
|
||||
// this, and we require that after running the lifted testcase, `gStateBefore`
|
||||
// matches `gStateAfter`,
|
||||
std::aligned_storage<sizeof(State), alignof(State)>::type
|
||||
gLiftedState;
|
||||
std::aligned_storage<sizeof(State), alignof(State)>::type gLiftedState;
|
||||
|
||||
// Native state after running the native test case.
|
||||
std::aligned_storage<sizeof(State), alignof(State)>::type
|
||||
gNativeState;
|
||||
std::aligned_storage<sizeof(State), alignof(State)>::type gNativeState;
|
||||
|
||||
// Address of the native test to run. The `InvokeTestCase` function saves
|
||||
// the native program state but then needs a way to figure out where to go
|
||||
@@ -258,8 +256,7 @@ Memory *__remill_missing_block(State &, addr_t, Memory *memory) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
Memory *__remill_sync_hyper_call(State &, Memory *,
|
||||
SyncHyperCall::Name) {
|
||||
Memory *__remill_sync_hyper_call(State &, Memory *, SyncHyperCall::Name) {
|
||||
abort();
|
||||
}
|
||||
// Read/write to I/O ports.
|
||||
@@ -335,6 +332,65 @@ float128_t __remill_undefined_f128(void) {
|
||||
return {0};
|
||||
}
|
||||
|
||||
|
||||
bool __remill_flag_computation_zero(bool result, ...) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_flag_computation_sign(bool result, ...) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_flag_computation_overflow(bool result, ...) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_flag_computation_carry(bool result, ...) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_compare_sle(bool result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_compare_slt(bool result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_compare_sge(bool result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_compare_sgt(bool result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool __remill_compare_ule(bool result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_compare_ult(bool result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_compare_ugt(bool result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_compare_uge(bool result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_compare_eq(bool result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_compare_neq(bool result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Marks `mem` as being used. This is used for making sure certain symbols are
|
||||
// kept around through optimization, and makes sure that optimization doesn't
|
||||
// perform dead-argument elimination on any of the intrinsics.
|
||||
@@ -344,7 +400,7 @@ void __remill_mark_as_used(void *mem) {
|
||||
|
||||
} // extern C
|
||||
|
||||
typedef Memory *(LiftedFunc)(State &, addr_t, Memory *);
|
||||
typedef Memory *(LiftedFunc) (State &, addr_t, Memory *);
|
||||
|
||||
// Mapping of test name to translated function.
|
||||
static std::map<uint64_t, LiftedFunc *> gTranslatedFuncs;
|
||||
|
||||
+77
-20
@@ -422,6 +422,63 @@ float80_t __remill_undefined_f80(void) {
|
||||
return {0};
|
||||
}
|
||||
|
||||
bool __remill_flag_computation_zero(bool result, ...) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_flag_computation_sign(bool result, ...) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_flag_computation_overflow(bool result, ...) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_flag_computation_carry(bool result, ...) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_compare_sle(bool result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_compare_slt(bool result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_compare_sge(bool result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_compare_sgt(bool result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool __remill_compare_ule(bool result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_compare_ult(bool result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_compare_ugt(bool result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_compare_uge(bool result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_compare_eq(bool result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __remill_compare_neq(bool result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Marks `mem` as being used. This is used for making sure certain symbols are
|
||||
// kept around through optimization, and makes sure that optimization doesn't
|
||||
// perform dead-argument elimination on any of the intrinsics.
|
||||
@@ -501,7 +558,7 @@ static void ImportX87State(State *state) {
|
||||
}
|
||||
}
|
||||
|
||||
// Looks like X87 state.
|
||||
// Looks like X87 state.
|
||||
} else {
|
||||
DLOG(INFO) << "Importing FPU state.";
|
||||
for (size_t i = 0; i < 8; ++i) {
|
||||
@@ -865,24 +922,24 @@ static void RunWithFlags(const test::TestInfo *info, Flags flags,
|
||||
for (size_t i = 0; i < sizeof(State); ++i) {
|
||||
LOG_IF(ERROR, lifted_state_bytes[i] != native_state_bytes[i])
|
||||
<< "Bytes at offset " << i << " are different: "
|
||||
<< "lifted [" << std::hex << static_cast<unsigned int>(lifted_state_bytes[i])
|
||||
<< "] vs native [" << std::hex << static_cast<unsigned int>(native_state_bytes[i])
|
||||
<< "]\n" << std::dec
|
||||
<< "vec: " << offsetof(State, vec) << "\n"
|
||||
<< "aflag:" << offsetof(State, aflag) << "\n"
|
||||
<< "rflag:" << offsetof(State, rflag) << "\n"
|
||||
<< "seg:" << offsetof(State, seg) << "\n"
|
||||
<< "addr:" << offsetof(State, addr) << "\n"
|
||||
<< "gpr:" << offsetof(State, gpr) << "\n"
|
||||
<< "st:" << offsetof(State, st) << "\n"
|
||||
<< "mmx:" << offsetof(State, mmx) << "\n"
|
||||
<< "sw:" << offsetof(State, sw) << "\n"
|
||||
<< "xcr0:" << offsetof(State, xcr0) << "\n"
|
||||
<< "x87:" << offsetof(State, x87) << "\n"
|
||||
<< "seg_caches:" << offsetof(State, seg_caches) << "\n";
|
||||
<< "lifted [" << std::hex
|
||||
<< static_cast<unsigned int>(lifted_state_bytes[i]) << "] vs native ["
|
||||
<< std::hex << static_cast<unsigned int>(native_state_bytes[i])
|
||||
<< "]\n"
|
||||
<< std::dec << "vec: " << offsetof(State, vec) << "\n"
|
||||
<< "aflag:" << offsetof(State, aflag) << "\n"
|
||||
<< "rflag:" << offsetof(State, rflag) << "\n"
|
||||
<< "seg:" << offsetof(State, seg) << "\n"
|
||||
<< "addr:" << offsetof(State, addr) << "\n"
|
||||
<< "gpr:" << offsetof(State, gpr) << "\n"
|
||||
<< "st:" << offsetof(State, st) << "\n"
|
||||
<< "mmx:" << offsetof(State, mmx) << "\n"
|
||||
<< "sw:" << offsetof(State, sw) << "\n"
|
||||
<< "xcr0:" << offsetof(State, xcr0) << "\n"
|
||||
<< "x87:" << offsetof(State, x87) << "\n"
|
||||
<< "seg_caches:" << offsetof(State, seg_caches) << "\n";
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
}
|
||||
|
||||
if (gLiftedStack != gNativeStack) {
|
||||
@@ -960,12 +1017,12 @@ TEST_P(InstrTest, SemanticsMatchNative) {
|
||||
}
|
||||
}
|
||||
|
||||
std::string NameTest(const testing::TestParamInfo< InstrTest::ParamType > &test) {
|
||||
std::string NameTest(const testing::TestParamInfo<InstrTest::ParamType> &test) {
|
||||
return test.param->test_name;
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(GeneralInstrTest, InstrTest,
|
||||
testing::ValuesIn(gTests), NameTest);
|
||||
INSTANTIATE_TEST_SUITE_P(GeneralInstrTest, InstrTest, testing::ValuesIn(gTests),
|
||||
NameTest);
|
||||
|
||||
// Recover from a signal.
|
||||
static void RecoverFromError(int sig_num, siginfo_t *, void *context_) {
|
||||
|
||||
Reference in New Issue
Block a user