Skip to content

Commit dd14a17

Browse files
authored
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
1 parent 769c280 commit dd14a17

File tree

9 files changed

+252
-46
lines changed

9 files changed

+252
-46
lines changed

cmake/BCCompiler.cmake

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,13 @@ function(add_runtime target_name)
164164
set(additional_windows_settings "-D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH")
165165
endif()
166166

167+
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
168+
set(target_decl "-target" "x86_64-apple-macosx11.0.0")
169+
endif()
170+
171+
167172
add_custom_command(OUTPUT "${absolute_output_file_path}"
168-
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}"
173+
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}"
169174
MAIN_DEPENDENCY "${absolute_source_file_path}"
170175
${dependency_list_directive}
171176
COMMENT "Building BC object ${absolute_output_file_path}"

include/remill/Arch/Runtime/Intrinsics.h

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ __remill_write_memory_64(Memory *, addr_t, uint64_t);
5757
[[gnu::used, gnu::const]] extern float64_t __remill_read_memory_f64(Memory *,
5858
addr_t);
5959

60-
[[gnu::used]] extern Memory* __remill_read_memory_f80(Memory *, addr_t,
61-
native_float80_t&);
60+
[[gnu::used]] extern Memory *__remill_read_memory_f80(Memory *, addr_t,
61+
native_float80_t &);
6262

6363
[[gnu::used]] extern float128_t __remill_read_memory_f128(Memory *, addr_t);
6464

@@ -68,8 +68,8 @@ __remill_write_memory_f32(Memory *, addr_t, float32_t);
6868
[[gnu::used, gnu::const]] extern Memory *
6969
__remill_write_memory_f64(Memory *, addr_t, float64_t);
7070

71-
[[gnu::used]] extern Memory *__remill_write_memory_f80(Memory *, addr_t,
72-
const native_float80_t&);
71+
[[gnu::used]] extern Memory *
72+
__remill_write_memory_f80(Memory *, addr_t, const native_float80_t &);
7373

7474
[[gnu::used]] extern Memory *__remill_write_memory_f128(Memory *, addr_t,
7575
float128_t);
@@ -90,6 +90,39 @@ __remill_write_memory_f64(Memory *, addr_t, float64_t);
9090

9191
[[gnu::used, gnu::const]] extern float128_t __remill_undefined_f128(void);
9292

93+
[[gnu::used, gnu::const]] extern bool
94+
__remill_flag_computation_zero(bool result, ...);
95+
96+
[[gnu::used, gnu::const]] extern bool
97+
__remill_flag_computation_sign(bool result, ...);
98+
99+
[[gnu::used, gnu::const]] extern bool
100+
__remill_flag_computation_overflow(bool result, ...);
101+
102+
[[gnu::used, gnu::const]] extern bool
103+
__remill_flag_computation_carry(bool result, ...);
104+
105+
[[gnu::used, gnu::const]] extern bool __remill_compare_sle(bool result);
106+
107+
[[gnu::used, gnu::const]] extern bool __remill_compare_slt(bool result);
108+
109+
[[gnu::used, gnu::const]] extern bool __remill_compare_sge(bool result);
110+
111+
[[gnu::used, gnu::const]] extern bool __remill_compare_sgt(bool result);
112+
113+
114+
[[gnu::used, gnu::const]] extern bool __remill_compare_ule(bool result);
115+
116+
[[gnu::used, gnu::const]] extern bool __remill_compare_ult(bool result);
117+
118+
[[gnu::used, gnu::const]] extern bool __remill_compare_ugt(bool result);
119+
120+
[[gnu::used, gnu::const]] extern bool __remill_compare_uge(bool result);
121+
122+
[[gnu::used, gnu::const]] extern bool __remill_compare_eq(bool result);
123+
124+
[[gnu::used, gnu::const]] extern bool __remill_compare_neq(bool result);
125+
93126
// Generic error.
94127
[[gnu::used]] extern Memory *__remill_error(State &, addr_t addr, Memory *);
95128

include/remill/BC/IntrinsicTable.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,19 @@ class IntrinsicTable {
8787
llvm::Function *undefined_f64;
8888
llvm::Function *undefined_f80;
8989

90+
91+
// Flag markers
92+
llvm::Function *flag_computation_zero;
93+
llvm::Function *flag_computation_sign;
94+
llvm::Function *flag_computation_overflow;
95+
llvm::Function *flag_computation_carry;
96+
97+
llvm::Function *compare_sle;
98+
llvm::Function *compare_sgt;
99+
llvm::Function *compare_eq;
100+
llvm::Function *compare_neq;
101+
102+
90103
private:
91104
IntrinsicTable(void) = delete;
92105
};

lib/Arch/Runtime/Intrinsics.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,22 @@ extern "C" void __remill_mark_as_used(const void *);
116116
USED(__remill_undefined_f32);
117117
USED(__remill_undefined_f64);
118118
USED(__remill_undefined_f80);
119+
120+
USED(__remill_flag_computation_zero);
121+
USED(__remill_flag_computation_overflow);
122+
USED(__remill_flag_computation_sign);
123+
USED(__remill_flag_computation_carry);
124+
125+
USED(__remill_compare_sle);
126+
USED(__remill_compare_slt);
127+
USED(__remill_compare_sgt);
128+
USED(__remill_compare_sge);
129+
130+
USED(__remill_compare_eq);
131+
USED(__remill_compare_neq);
132+
133+
USED(__remill_compare_ugt);
134+
USED(__remill_compare_uge);
135+
USED(__remill_compare_ult);
136+
USED(__remill_compare_ule);
119137
}

lib/Arch/X86/Semantics/COND_BR.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ DEF_SEM(JNLE, R8W cond, PC taken, PC not_taken,
2020
IF_32BIT_ELSE(R32W, R64W) pc_dst) {
2121
addr_t taken_pc = Read(taken);
2222
addr_t not_taken_pc = Read(not_taken);
23-
auto take_branch = BAnd(BNot(FLAG_ZF), BXnor(FLAG_SF, FLAG_OF));
23+
auto take_branch =
24+
__remill_compare_sgt(BAnd(BNot(FLAG_ZF), BXnor(FLAG_SF, FLAG_OF)));
2425
Write(cond, take_branch);
2526
Write(pc_dst, Select<addr_t>(take_branch, taken_pc, not_taken_pc));
2627
return memory;
@@ -170,7 +171,7 @@ DEF_SEM(JLE, R8W cond, PC taken, PC not_taken,
170171
IF_32BIT_ELSE(R32W, R64W) pc_dst) {
171172
addr_t taken_pc = Read(taken);
172173
addr_t not_taken_pc = Read(not_taken);
173-
auto take_branch = BOr(FLAG_ZF, BXor(FLAG_SF, FLAG_OF));
174+
auto take_branch = __remill_compare_sle(BOr(FLAG_ZF, BXor(FLAG_SF, FLAG_OF)));
174175
Write(cond, take_branch);
175176
Write(pc_dst, Select<addr_t>(take_branch, taken_pc, not_taken_pc));
176177
return memory;

lib/Arch/X86/Semantics/FLAGS.cpp

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,19 @@ enum : uint32_t { kLHS = 2415899639U, kRHS = 70623199U };
2525
// Zero flags, tells us whether or not a value is zero.
2626
template <typename T>
2727
[[gnu::const]] ALWAYS_INLINE static bool ZeroFlag(T res) {
28-
return T(0) == res;
28+
return __remill_flag_computation_zero(T(0) == res, res);
2929
}
3030

3131
// Zero flags, tells us whether or not a value is zero.
3232
template <typename T>
3333
[[gnu::const]] ALWAYS_INLINE static bool NotZeroFlag(T res) {
34-
return T(0) != res;
34+
return !__remill_flag_computation_zero(T(0) == res, res);
3535
}
3636

3737
// Sign flag, tells us if a result is signed or unsigned.
3838
template <typename T>
3939
[[gnu::const]] ALWAYS_INLINE static bool SignFlag(T res) {
40-
return 0 > Signed(res);
40+
return __remill_flag_computation_sign(0 > Signed(res), res);
4141
}
4242

4343
// Auxiliary carry flag. This is used for binary coded decimal operations and
@@ -97,7 +97,8 @@ struct Overflow<tag_add> {
9797
const T sign_lhs = lhs >> kSignShift;
9898
const T sign_rhs = rhs >> kSignShift;
9999
const T sign_res = res >> kSignShift;
100-
return 2 == ((sign_lhs ^ sign_res) + (sign_rhs ^ sign_res));
100+
return __remill_flag_computation_overflow(
101+
2 == ((sign_lhs ^ sign_res) + (sign_rhs ^ sign_res)), lhs, rhs, res);
101102
}
102103
};
103104

@@ -114,7 +115,8 @@ struct Overflow<tag_sub> {
114115
const T sign_lhs = lhs >> kSignShift;
115116
const T sign_rhs = rhs >> kSignShift;
116117
const T sign_res = res >> kSignShift;
117-
return 2 == ((sign_lhs ^ sign_rhs) + (sign_lhs ^ sign_res));
118+
return __remill_flag_computation_overflow(
119+
2 == ((sign_lhs ^ sign_rhs) + (sign_lhs ^ sign_res)), lhs, rhs, res);
118120
}
119121
};
120122

@@ -126,10 +128,11 @@ struct Overflow<tag_mul> {
126128
// the operands.
127129
template <typename T, typename R>
128130
[[gnu::const]] ALWAYS_INLINE static bool
129-
Flag(T, T, R res,
131+
Flag(T lhs, T rhs, R res,
130132
typename std::enable_if<sizeof(T) < sizeof(R), int>::type = 0) {
131133

132-
return static_cast<R>(static_cast<T>(res)) != res;
134+
return __remill_flag_computation_overflow(
135+
static_cast<R>(static_cast<T>(res)) != res, lhs, rhs, res);
133136
}
134137

135138
// Signed integer multiplication overflow check, where the result is
@@ -155,24 +158,28 @@ struct Carry<tag_add> {
155158
[[gnu::const]] ALWAYS_INLINE static bool Flag(T lhs, T rhs, T res) {
156159
static_assert(std::is_unsigned<T>::value,
157160
"Invalid specialization of `Carry::Flag` for addition.");
158-
return res < lhs || res < rhs;
161+
return __remill_flag_computation_carry(res < lhs || res < rhs, lhs, rhs,
162+
res);
159163
}
160164
};
161165

162166
// Computes an carry flag when one number is subtracted from another.
163167
template <>
164168
struct Carry<tag_sub> {
165169
template <typename T>
166-
[[gnu::const]] ALWAYS_INLINE static bool Flag(T lhs, T rhs, T) {
170+
[[gnu::const]] ALWAYS_INLINE static bool Flag(T lhs, T rhs, T res) {
167171
static_assert(std::is_unsigned<T>::value,
168172
"Invalid specialization of `Carry::Flag` for addition.");
169-
return lhs < rhs;
173+
return __remill_flag_computation_carry(lhs < rhs, lhs, rhs, res);
170174
}
171175
};
172176

173177
} // namespace
174178

175-
#define UndefFlag(name) do { state.aflag.name = __remill_undefined_8(); } while (false)
179+
#define UndefFlag(name) \
180+
do { \
181+
state.aflag.name = __remill_undefined_8(); \
182+
} while (false)
176183

177184
#define ClearArithFlags() \
178185
do { \

lib/BC/IntrinsicTable.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,23 @@ IntrinsicTable::IntrinsicTable(llvm::Module *module)
130130
undefined_64(FindPureIntrinsic(module, "__remill_undefined_64")),
131131
undefined_f32(FindPureIntrinsic(module, "__remill_undefined_f32")),
132132
undefined_f64(FindPureIntrinsic(module, "__remill_undefined_f64")),
133-
undefined_f80(FindPureIntrinsic(module, "__remill_undefined_f80")) {
133+
undefined_f80(FindPureIntrinsic(module, "__remill_undefined_f80")),
134+
135+
// Flag computations
136+
flag_computation_zero(
137+
FindPureIntrinsic(module, "__remill_flag_computation_zero")),
138+
flag_computation_sign(
139+
FindPureIntrinsic(module, "__remill_flag_computation_sign")),
140+
flag_computation_overflow(
141+
FindPureIntrinsic(module, "__remill_flag_computation_overflow")),
142+
flag_computation_carry(
143+
FindPureIntrinsic(module, "__remill_flag_computation_carry")),
144+
// compares
145+
compare_sle(FindPureIntrinsic(module, "__remill_compare_sle")),
146+
compare_sgt(FindPureIntrinsic(module, "__remill_compare_sgt")),
147+
compare_eq(FindPureIntrinsic(module, "__remill_compare_eq")),
148+
compare_neq(FindPureIntrinsic(module, "__remill_compare_neq")) {
149+
134150

135151
// Make sure to set the correct attributes on this to make sure that
136152
// it's never optimized away.

tests/AArch64/Run.cpp

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,10 @@ extern "C" {
8888
// initial state for the lifted testcase. The lifted test case code mutates
8989
// this, and we require that after running the lifted testcase, `gStateBefore`
9090
// matches `gStateAfter`,
91-
std::aligned_storage<sizeof(State), alignof(State)>::type
92-
gLiftedState;
91+
std::aligned_storage<sizeof(State), alignof(State)>::type gLiftedState;
9392

9493
// Native state after running the native test case.
95-
std::aligned_storage<sizeof(State), alignof(State)>::type
96-
gNativeState;
94+
std::aligned_storage<sizeof(State), alignof(State)>::type gNativeState;
9795

9896
// Address of the native test to run. The `InvokeTestCase` function saves
9997
// 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) {
258256
return memory;
259257
}
260258

261-
Memory *__remill_sync_hyper_call(State &, Memory *,
262-
SyncHyperCall::Name) {
259+
Memory *__remill_sync_hyper_call(State &, Memory *, SyncHyperCall::Name) {
263260
abort();
264261
}
265262
// Read/write to I/O ports.
@@ -335,6 +332,65 @@ float128_t __remill_undefined_f128(void) {
335332
return {0};
336333
}
337334

335+
336+
bool __remill_flag_computation_zero(bool result, ...) {
337+
return result;
338+
}
339+
340+
bool __remill_flag_computation_sign(bool result, ...) {
341+
return result;
342+
}
343+
344+
bool __remill_flag_computation_overflow(bool result, ...) {
345+
return result;
346+
}
347+
348+
bool __remill_flag_computation_carry(bool result, ...) {
349+
return result;
350+
}
351+
352+
bool __remill_compare_sle(bool result) {
353+
return result;
354+
}
355+
356+
bool __remill_compare_slt(bool result) {
357+
return result;
358+
}
359+
360+
bool __remill_compare_sge(bool result) {
361+
return result;
362+
}
363+
364+
bool __remill_compare_sgt(bool result) {
365+
return result;
366+
}
367+
368+
369+
bool __remill_compare_ule(bool result) {
370+
return result;
371+
}
372+
373+
bool __remill_compare_ult(bool result) {
374+
return result;
375+
}
376+
377+
bool __remill_compare_ugt(bool result) {
378+
return result;
379+
}
380+
381+
bool __remill_compare_uge(bool result) {
382+
return result;
383+
}
384+
385+
bool __remill_compare_eq(bool result) {
386+
return result;
387+
}
388+
389+
bool __remill_compare_neq(bool result) {
390+
return result;
391+
}
392+
393+
338394
// Marks `mem` as being used. This is used for making sure certain symbols are
339395
// kept around through optimization, and makes sure that optimization doesn't
340396
// perform dead-argument elimination on any of the intrinsics.
@@ -344,7 +400,7 @@ void __remill_mark_as_used(void *mem) {
344400

345401
} // extern C
346402

347-
typedef Memory *(LiftedFunc)(State &, addr_t, Memory *);
403+
typedef Memory *(LiftedFunc) (State &, addr_t, Memory *);
348404

349405
// Mapping of test name to translated function.
350406
static std::map<uint64_t, LiftedFunc *> gTranslatedFuncs;

0 commit comments

Comments
 (0)