Skip to content

Commit

Permalink
Ian/recover flags (#559)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
2over12 authored Nov 19, 2021
1 parent 769c280 commit dd14a17
Show file tree
Hide file tree
Showing 9 changed files with 252 additions and 46 deletions.
7 changes: 6 additions & 1 deletion cmake/BCCompiler.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down
41 changes: 37 additions & 4 deletions include/remill/Arch/Runtime/Intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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);
Expand All @@ -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 *);

Expand Down
13 changes: 13 additions & 0 deletions include/remill/BC/IntrinsicTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down
18 changes: 18 additions & 0 deletions lib/Arch/Runtime/Intrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
5 changes: 3 additions & 2 deletions lib/Arch/X86/Semantics/COND_BR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
29 changes: 18 additions & 11 deletions lib/Arch/X86/Semantics/FLAGS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
};

Expand All @@ -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);
}
};

Expand All @@ -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
Expand All @@ -155,24 +158,28 @@ 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);
}
};

// Computes an carry flag when one number is subtracted from another.
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 { \
Expand Down
18 changes: 17 additions & 1 deletion lib/BC/IntrinsicTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
70 changes: 63 additions & 7 deletions tests/AArch64/Run.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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;
Expand Down
Loading

0 comments on commit dd14a17

Please sign in to comment.