This commit registers for each jump target how we met it, as a flag. It
also keeps track of which pointers in global data have been involved in
materialization performed by SET: those who are not are of special
interest for us, since they are likely function pointers, and are
therefore marked with a specific flag.
* When generating the code for setting a label or jumping to it, give
sensible names to the new basic blocks.
* Keep track of the last seen PC during translation so it can be used to
obtain a sensible name for the basic block.
* Let `JumpTargetManager::getBlockAt` set a proper name to the basic
block before returning, if it doesn't already have one.
`forceFallthroughAfterHelper` handles the situation where there isn't a
PC-store between a call to an helper and to `exitTB`, in this case, we
force a branch to the fallthrough PC.
This commit also simplifies `InstructionTranslator::translateCall`:
remove jump to the dispatcher after a call to an helper in case the PC
was saved and it has changed. We don't really need to do this, QEMU will
generate a call to `exitTB` has necessary or
`forceFallthroughAfterHelper` will take care of the thing.
* The function now can take a `std::set` of basic blocks to ignore.
* The visitor function has now several options on how to proceed, and
can express them through its return value.
* A serious bug in the implementation was also fixed.
This pass helps us handling instructions like ARM's `blt` which compute
the result of the comparison by bit-fiddling with the bit sign of the
operands of a subtraction.
The idea is to have a series of known boolean expressions using `a`, `b'
and `c` as variables (e.g. the boolean expression corresponding to
"signed greater than") and compare their truth table against the one
being analyzed. In case of match, the comparison can be simplified.
`TranslateDirectBranchesPass` now optionally depends on `SETPass`. This
allows us to reuse information obtained by SET and OSRA to pin potential
jump targets we detected to an `exitTB` call. In practice this means
that before a call to `exitTB` a conditional branch or a switch is
present to check if the destination of the jump is one of those we
expected, and if not, go to the dispatcher if the estimated destinations
were marked as approximate, or fail otherwise. Since this feature is
currently WIP, we never fail, we always go the dispatcher instead.
Since the amount of successors might grow during the iterative discovery
process, record the amount of successors as a `exitTB` argument.
* New `exitTB` argument: estimated number of successors.
* Move the code of the old implementation of the pass to the
`pinConstantStore` function.
* Update routine for cleanup of post-exitTB instructions.
* `findNextExitTB`: more reliable implementation of the search for the
next call to `exitTB`.
* Keep a reference to the basic block handling the failure of the
dispatcher's switch, so we can use it to report failure of our jump
target estimation.
The iteartive basic block discovery process has been reorganized to
minimize the amount of passes we run (in particular SROA, constant
propagation and early CSE) and to proceed until we don't "pin" any new
branch instruction, and not only until we're not able to discover any
new basic block.
The logging output has also been reworked to be more informative.
Checking if a range of addresses belong to a segment should be
implemented by checking if the start and end address belong to the
address, the `Start <= Address && Address + Size < End` approach leads
to subtle errors when `Address` is close to the maximum representable
value due to an overflow.
* Import OSRA
* Improve the SET (aka `JumpTargetFromConstants`) by introducing the
`OperationsStack` class.
* Review `harvest` logic
* Allow to disable OSRA (along with the sumjump heuristic)
* Take the core of `getNextPC` out of it and move it to `getPC`, a
function returning both the current and the next PC. Also, fix a bug
when reaching the beginning of a basic block.
* Detect "reliable" jump targets: a "reliable" jump target is a jump
target obtained from a store to a PC but it's not a fallthrough jump.
`unknownPC` is an extern function we expect to be linked to the output
which is called when we have to crash due to an unexpected jump target.
* Remove unused references to register variables, now only need the
stack pointer
* Fix bug in how the auxiliary values were pushed on the stack.
* Push 0 HW_CAPs
* Implement some glib's functions
Give a new, useful, meaning to the `--entry` parameter: it's new purpose
is to be able to easily try to translate the code at a certain address.
In this sense, prevent global data harvesting if `--entry` is specified.
The handling of GVN options has also been improved.
If EarlyCSE didn't produce any new code pointer, we use
GlobalValueNumbering which usually leads to better results, in
particular if we remove `newpc` markers and if it can make use of alias
information, which we introduce to let the compiler know that
loads/stores to the CPU state will never alias loads/stores to normal
memory.
* Before generating any load/store instruction mark it with the
appropriate aliasing information.
* Update `JumpTargetManager::harvest` to run GVN
* Move the `Visited` set of `JumpTargetsFromConstantsPass` in
`JumpTargetManager`, even if currently we clear it at each invocation
of the pass
Some hand crafted assembly code perform a PC-relative jump of an
non-statically known amount. This patch introduces a simple hack to
handle such a situation by simply detecting it and marking as potential
jump targets all the instructions to come until the next jump.
This is implemented by the `JumpTargetManager::handleSumJump` and
`isSumJump` functions.
This commit also introduces a new implementation of `getNextPC` not
requiring the dominator tree.
The logic to implement harvesting of new code pointers when we're out of
them during translation, has been moved to `JumpTargetManager`. Its
interface has also been reduced and some logging has been introduced.
At the current stage, if there's nothing to `peek`, we first give a shot
of `SROA` and `TranslateDirectBranchesPass`, and then, if nothing came
out, we go for `EarlyCSE` and `JumpTargetsFromConstantsPass`.
Introduce an unreachable instruction after each emitted call to
`exit_tb` to terminate properly basic blocks. This patch also removes it
when appropropriate (i.e. in `TranslateDirectBranchesPass` and
`JumpTargetManager::translateIndirectJumps`).
Instead of taking note of the executable ranges exclusively, keep track
of all the segments in `CodeGenerator`. `JumpTargetManager` instead will
keep track of executable areas only.
* Introduce the `SegmentInfo` struct, which simply holds essential
information about the segment such as start and end address,
permissions and a reference to the global variable holding its content.
* Update `CodeGenerator` to keep a vector of `SegmentInfo`.
* `JumpTargetManager`: polish the constructor and make it take the vector
of `SegmentInfo`, from which the executable ranges are then extracted.
`JumpTargetManager::getPrevPCWrite` used to assert in case a write to the
PC is not immediately found before an `exit_tb`. Relax this constraint.
In the future we might re-introduce it if we handle a couple of common
cases.
In variable-length encoding architectures, sometimes you might have one
instruction containing another one. This is not a problem until you get
the next one, which would be translated twice, in fact, it's not at the
beginning of a basic block and the current mechanism to detect already
translated code does not handle it.
This patch makes `JumpTargetManager::newPC` check the whole map of the
translated instructions. This might have to be improved in the future.
* Introduce the `JumpTargetsFromConstantsPass` pass, which goes through
all the unvisited basic blocks looking for constants and trying to feed
them to `JumpTargetManager`, which will decide if they are code
pointers or not.
* To make life of `JumpTargetsFromConstantsPass` easier run
`EarlyCSEPass` before it, which is particularly useful to make explicit
constants that some architectures materialize in two steps (high and
low part).
* Remove the fake fallthrough workaround in `TranslateDirectBranchesPass`
which was used to register for exploration basic blocks after a direct
jump, which was necessary due to the fact that return instructions are
indirect jumps and were losing the basic blocks after function calls.
This is no longer necessary thanks to `JumpTargetsFromConstantsPass`.
Now, in `JumpTargetManager::getBlockAt`, before registering a new PC for
translation we check that the corresponding address was actually
contained in a segment marked as executable in the original binary. This
prevents translation of data, which is a problem in particular when we
will start to harvest possible code pointers from global data or
constants found in the code
* Register in `CodeGenerator::ExecutableRanges` address ranges which
contained executable code in the input ELF.
* In `JumpTargetManager::getBlockAt` check if the given PC was actually
in an executable memory area, and assert or return `nullptr` depending
on the `Try` parameter.
Before this patch the dispatcher area was created all at once at a final
stage, however it's useful also while translating, since it keeps all the
code reachable, which is particularly important to be able to build a
exhaustive dominator tree.
* Create the dispatcher area when a new instance of `JumpTargetManager`
is created.
* Create a fake conditional branch to the dispatcher at the beginning of
the `root` function.
* Incrementally build the dispatcher's switch case in
`JumpTargetManager::getBlockAt`.
Fixed a bug which lead to remove from the list of unexplored basic
blocks the wrong one while calling `JumpTargetManager::newPC` from
`InstructionTranslator::newInstruction`. This bug was due to the fact
that we were reading the address of the basic block associated with a PC
*after* erasing it from the `std::vector`.
In certain cases we have a call to `exitTB` right after an helper, in
particular in x86, after a syscall. We cannot know what the target
address will be, so we have to handle this as an indirect jump.
* `JumpTargetManager::getPrevPCWrite`: clean up.
* `JumpTargetManager::getPrevPCWrite`: while searching for stores to the
PC, also check for call instructions. If one is met, return nullptr.
* `TranslateDirectBranchesPass::runOnFunction` and
`JumpTargetManager::translateIndirectJumps`: insert new code before
`exitTB`, not the write to the PC.