diff --git a/cmake/BCCompiler.cmake b/cmake/BCCompiler.cmake index f66489af8..6387aa038 100644 --- a/cmake/BCCompiler.cmake +++ b/cmake/BCCompiler.cmake @@ -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}" diff --git a/include/remill/Arch/Runtime/Intrinsics.h b/include/remill/Arch/Runtime/Intrinsics.h index 76d306b07..2bc1ce2bf 100644 --- a/include/remill/Arch/Runtime/Intrinsics.h +++ b/include/remill/Arch/Runtime/Intrinsics.h @@ -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 *); diff --git a/include/remill/BC/IntrinsicTable.h b/include/remill/BC/IntrinsicTable.h index e1c6e0af8..c4c180e50 100644 --- a/include/remill/BC/IntrinsicTable.h +++ b/include/remill/BC/IntrinsicTable.h @@ -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; }; diff --git a/lib/Arch/Runtime/Intrinsics.cpp b/lib/Arch/Runtime/Intrinsics.cpp index e1a75b5bb..67e30b6b0 100644 --- a/lib/Arch/Runtime/Intrinsics.cpp +++ b/lib/Arch/Runtime/Intrinsics.cpp @@ -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); } diff --git a/lib/Arch/X86/Semantics/COND_BR.cpp b/lib/Arch/X86/Semantics/COND_BR.cpp index 3e91563d1..324ac47b4 100644 --- a/lib/Arch/X86/Semantics/COND_BR.cpp +++ b/lib/Arch/X86/Semantics/COND_BR.cpp @@ -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(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(take_branch, taken_pc, not_taken_pc)); return memory; diff --git a/lib/Arch/X86/Semantics/FLAGS.cpp b/lib/Arch/X86/Semantics/FLAGS.cpp index 9570a5ed4..24bf2dafd 100644 --- a/lib/Arch/X86/Semantics/FLAGS.cpp +++ b/lib/Arch/X86/Semantics/FLAGS.cpp @@ -25,19 +25,19 @@ enum : uint32_t { kLHS = 2415899639U, kRHS = 70623199U }; // Zero flags, tells us whether or not a value is zero. template [[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 [[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 [[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 { 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 { 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 { // the operands. template [[gnu::const]] ALWAYS_INLINE static bool - Flag(T, T, R res, + Flag(T lhs, T rhs, R res, typename std::enable_if::type = 0) { - return static_cast(static_cast(res)) != res; + return __remill_flag_computation_overflow( + static_cast(static_cast(res)) != res, lhs, rhs, res); } // Signed integer multiplication overflow check, where the result is @@ -155,7 +158,8 @@ struct Carry { [[gnu::const]] ALWAYS_INLINE static bool Flag(T lhs, T rhs, T res) { static_assert(std::is_unsigned::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 { template <> struct Carry { template - [[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::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 { \ diff --git a/lib/BC/IntrinsicTable.cpp b/lib/BC/IntrinsicTable.cpp index 4a4815b21..1a06972d7 100644 --- a/lib/BC/IntrinsicTable.cpp +++ b/lib/BC/IntrinsicTable.cpp @@ -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. diff --git a/tests/AArch64/Run.cpp b/tests/AArch64/Run.cpp index f62a4180f..508632288 100644 --- a/tests/AArch64/Run.cpp +++ b/tests/AArch64/Run.cpp @@ -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::type - gLiftedState; +std::aligned_storage::type gLiftedState; // Native state after running the native test case. -std::aligned_storage::type - gNativeState; +std::aligned_storage::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 gTranslatedFuncs; diff --git a/tests/X86/Run.cpp b/tests/X86/Run.cpp index 36c217729..e487258c4 100644 --- a/tests/X86/Run.cpp +++ b/tests/X86/Run.cpp @@ -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(lifted_state_bytes[i]) - << "] vs native [" << std::hex << static_cast(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(lifted_state_bytes[i]) << "] vs native [" + << std::hex << static_cast(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 &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_) {