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:
2over12
2021-11-19 17:26:26 -05:00
committed by GitHub
parent 769c280720
commit dd14a17cdc
9 changed files with 252 additions and 46 deletions
+6 -1
View File
@@ -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}"
+37 -4
View File
@@ -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 *);
+13
View File
@@ -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;
};
+18
View File
@@ -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);
}
+3 -2
View File
@@ -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;
+18 -11
View File
@@ -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 { \
+17 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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_) {