Detect simplest cases of writes to the program counter while they're
being translated. This way, we avoid running expensive passes to collect
straightforward jump targets.
Note: if the PC-write value is the current PC, ignore it. Splitting the
basic block being created is problematic.
* Let `InstructionTranslator::newInstruction` return also the PC.
* `InstructionTranslator::translate`: make it aware of the current PC,
and make it abort in case of error and return a boolean to indicate
whether the translation should proceed or not.
* Simplify the fetch of a new address to translate.
* Let `InstructionTranslator::translateOpcode` return an error condition
in case of failure, instead of asserting.
* Don't start exploration from `VirtualAddress` but just add it as a
block to explore. Then start the translation with
`JumpTargetManager::peek` as usual.
* Remove the unreachable instruction we were using as a delimiter to
create new local variables, since it make the module invalid. Use the
fake branch to the dispatcher instead.
`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.
* s/`importGlobalData`/`parseELF`/
* Save the entry point specified in the ELF header, which will be used
if the user doesn't provide an address.
* Let parse `parseELF` take care of informing libtinycode about what
has to be mmap'd and where.
* Remove some support scripts used during testing, now no longer
necessary.
* Various cleanups
* 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`.
In `TranslateDirectBranchesPass::getNextPC` we were using a normal
iterator backward with a faulty exit condition that lead us to skip the
first instruction in a basic block.
This patch implements a proper solution: use `reverse_iterator`.
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`.
* Use `llvm::object` framework to obtain useful information from the ELF
binary such as pointer size and endianess.
* Introduce `CodeGenerator::importGlobalData`: import global (read-only
and writeable data) from the input binary directly into the generated
module.
* Introduce the `--linking-info` parameter: path to a CSV file where
sections containing global data extracted from the input binary are
listed with their name, start and end address.
* Expand the `Architecture` class with constructors and support accessor
methods.
`cpu_loop_exit` is used by QEMU to get back control from the translated
code while running an helper. Here we complete the inversion of the
hierarchy by transforming calls to `cpu_loop_exit` into calls to our
customized `cpu_loop`, which will handle syscalls and the like.
Since originally `cpu_loop_exit` was a noreturn function, we also need to
ensure that the semantic of its usage is preserved. We do this by setting
a global variable (`cpu_loop_exiting`) right after the call to
`cpu_loop_exit` and forcing the whole call stack to return immediately
if it's true.
* Introduce `CpuLoopExitPass`: replace all the calls to
`cpu_loop_exit` with a call to `cpu_loop`, a store true to
`cpu_loop_exiting` and a return. Then take all the callers and make
them return if `cpu_loop_exiting` is set. Once we get to the translated
function just reset `cpu_loop_exiting` to false.
* Introduce `VariableManager::computeEnvAddress`: generate code to
compute the offset of env . This computation is useful when
reference to the CPU state must be arificially introduced. This code
will be flattened by `CorrectCPUStateUsagePass` later.
`cpu_loop` is the main program loop used by QEMU during emulation. Here
we are interested in transforming it in a simple handler of exceptions
(e.g. signals and syscalls).
* Introduce `CpuLoopFunctionPass`: remove the outermost backedge in
`cpu_loop` and replace the call to cpu_*_exec with the exception index.
* Implement the support function `find_unique` which returns the only
element in a range satisfying a predicate, or, otherwise, asserts.
* Expand the function to replace with no-op or abort
* Factor out the code to replace function bodies (replaceFunction and
replaceFunctionWithRet)
* Replace functions before linking, directly in the helper module
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.
Currently we do not support access to pointer data types in the CPU state
structure. Now, since their usage is marginal, instead of failing at
compile-time we put an abort instruction when the code reaches that
point.
The same is done in case an access to an array member using a
non-constant index is performed.
* Move initialization and management of the structure describing the CPU
state (CPUStateType) into variablemanager.cpp.
* Support parts of CPU state outside "env" (e.g. the MIPSCPU
structure). Now "env" has an offset into the possibly larger CPU state
which we have to take into account where appropriate (see
VariableManager::envOffset).
* Link the helpers module into the generated module, including only what
is needed.
* Create some "no-op" or "abort" function corresponding to QEMU functions
not included in the helper module (e.g. logging and abort functions).
* Implement the CorrectCPUStateUsagePass pass, which starts from the
"env" global variable and looks for all its usages recursively, keeping
track of where pointers are pointing into the CPU state data structure,
and replaces all the load/stores with the global variable corresponding
to that specific field of the CPU state.
* After the linking phase, run SROA, the pass to adjust the CPU usage and
DCE.
* Let global variables have common linkage.
* Emit a call to an helper function (ExitTB) corresponding to each
exit_tb PTC instruction.
* Update TranslateDirectBranchesPass and
JumpTargetManager::translateIndirectJumps to look for the last write
to the PC before calls to ExitTB.
* Exposing the PC with JumpTargetManager::PC is not needed anymore. Users
from outside should only be interested in finding the the previous
write to the PC (using JumpTargetManager::getPrevPCWrite).