remill
remill copied to clipboard
Support (partially) far calls
Hello
This PR is for early review only (my purpose is to support far call/ret in both 64-bit/compat modes). E.g.
9a 50 04 00 00 33 00 call far 0x33:0x450
There as some detail in remill for which I'm still confused:
https://github.com/trailofbits/remill/blob/d37ee6bc30b689480d9ef274e3f96fc7d76a190c/remill/Arch/X86/Semantics/CALL_RET.cpp#L21-L28
What is the purpose of _IF_32BIT macro in this line?
Write(WritePtr<addr_t>(next_sp _IF_32BIT(REG_SS_BASE)), Read(return_pc));
And a bizzare error, when I try to lift the instruction using:
remill-lift-7.0 --arch x86 --bytes 9a500400003300 --ir-out /dev/stdout
but get
F0802 19:25:22.794279 3980 Util.cpp:842] Cannot declare internal function _ZN12_GLOBAL__N_112CALL_FAR_PTRI2InIjES1_ItEEEP6MemoryS5_R5StateT_T0_S2_ as external in another module
Many thanks for any help.
The _IF_32BIT(REG_SS_BASE) expands to , REG_SS_BASE [1] when ADDRESS_SIZE_BITS == 32, and expands to nothing otherwise, meaning that the two-argument version of WritePtr is called [2]: WritePtr<addr_t>(next_sp, REG_SS_BASE), and so the writes to the stack end up doing next_sp + REG_SS_BASE. On amd64, it would just write to next_sp.
[1] https://github.com/trailofbits/remill/blob/487228c9b61a1dae7dd7a264d71b9905130bea02/remill/Arch/Runtime/Definitions.h#L38 [2] https://github.com/trailofbits/remill/blob/487228c9b61a1dae7dd7a264d71b9905130bea02/remill/Arch/Runtime/Operators.h#L1396-L1400
Does the return address get written in the "destination" address space? If so, it may make sense to have a sync hyper call, somewhat like...
template <typename S1, typename S2>
DEF_SEM(CALL_FAR_PTR, S1 target_pc, S2 target_seg, PC return_pc) {
// New---------
state.segment_to_load = Read(target_seg);
memory = __remill_sync_hyper_call(state, memory, SyncHyperCall::kX86GetMemoryForSegment);
// End new----------
HYPER_CALL = AsyncHyperCall::kX86CallFar;
addr_t next_sp = USub(REG_XSP, ADDRESS_SIZE_BYTES * 2);
// stack update
WriteZExt(WritePtr<addr_t>(next_sp + ADDRESS_SIZE_BYTES), Read(REG_CS.flat)); // _IF_32BIT?
Write(WritePtr<addr_t>(next_sp _IF_32BIT(REG_SS_BASE)), Read(return_pc));
// register update
Write(REG_XSP, Read(next_sp));
WriteZExt(REG_PC, Read(target_pc));
Write(REG_CS.flat, Read(target_seg));
return memory;
}
Where segment_to_load is added here [1], and the semantic of to-add kX86GetMemoryForSegment sync hyper call returns an opaque Memory * representing us being in the new address space, so that the writes to the stack can reflect this.
Some additional _IF_32BIT(..) will probably be needed.
You're likely in the best position to evaluate if these semantics are actually working, as I'm not particularly familiar with the intricacies of far calls/jumps. I've always kind of punted on them ;-)
That is a bizarre error... It probably has to do with the trace lifter not being sufficiently aggressive at cloning the function into the "final" module. Can you file an issue for it?
[1] https://github.com/trailofbits/remill/blob/8e0a28ebae9460bc71302ab9dc4b8fe4fbbf32a0/remill/Arch/Runtime/State.h#L31
Ping @tathanhdinh.
Hello @pgoodman,
Sorry for the delay, I'm on vacation this week. I will come back next week.
ping @tathanhdinh
Hi @pgoodman,
Sorry again for the late, following is a summary for the current progress:
- for a need of "natural" test cases for these instructions (since I'm afraid of "side-effects" that I may omit) so I've tried to check on several ring-0 traces to see if they're used, but I still cannot find them.
- but never mind, I will try with some "artificial" tests (i.e. loading a driver containing far call/ret)