aboutsummaryrefslogtreecommitdiff
path: root/src/aarch64/simulator-aarch64.h
diff options
context:
space:
mode:
authorChris Jones <70633990+chris-jones-arm@users.noreply.github.com>2023-07-28 12:25:32 +0100
committerGitHub <noreply@github.com>2023-07-28 12:25:32 +0100
commit3fae28899b8fd20afc3b12aa0cdc80519433f85e (patch)
tree74a910424e04d2b80c77f02ae487da89d69ce2bb /src/aarch64/simulator-aarch64.h
parentabca95f09c11d2631f8cf9e677dda1e2a410a29c (diff)
Simulator branch interception (#75)dev/vixl-interception
* Add maybe_unused to runtime call arguments Currently runtime calls cannot be done if the function to be called has no parameters because the compiler will give a "unused-but-set-parameter" warning which is treated as an error. Fix this by always using the 'arguments' parameter. Change-Id: I9f4b75ea8b6ae6fe03be33cefa45fa99f5485b7a * Add branch interception to VIXL simulator Simulated AARCH64 code, that is not written in using the macroassembler, can branch (change the simulated PC) to arbitrary function addresses. This works fine if that function is AARCH64 however if that function is a native (x86_64) C++ function then an error (likely SIGILL) will be thrown. To handle this case we need to "intercept" branches to these native (x86_64) C++ functions and instead either perform a runtime call to the function or provide a callback to manually handle the particular case. Add a mechanism to intercept functions as they are branched to within the VIXL simulator. This means that whenever a function X is branched to (e.g: bl X) instead, if provided, a callback function Y is called. If no callback is provided for the interception to function X then a runtime call will be done on function X. Branch interception objects consisting of the function to intercept: X, and an optional callback function Y are stored within the simulator and checked every unconditional branch to register. Change-Id: I874a6fa5b8f0581fe930a7a98f762031bdb2f591
Diffstat (limited to 'src/aarch64/simulator-aarch64.h')
-rw-r--r--src/aarch64/simulator-aarch64.h63
1 files changed, 63 insertions, 0 deletions
diff --git a/src/aarch64/simulator-aarch64.h b/src/aarch64/simulator-aarch64.h
index 1fdbb6f6..9a686ad9 100644
--- a/src/aarch64/simulator-aarch64.h
+++ b/src/aarch64/simulator-aarch64.h
@@ -2656,6 +2656,7 @@ class Simulator : public DecoderVisitor {
R DoRuntimeCall(R (*function)(P...),
std::tuple<P...> arguments,
local_index_sequence<I...>) {
+ USE(arguments);
return function(std::get<I>(arguments)...);
}
@@ -2786,6 +2787,27 @@ class Simulator : public DecoderVisitor {
SimPRegister& GetPTrue() { return pregister_all_true_; }
+ // A callback function, called when a function has been intercepted if a
+ // BranchInterception entry exists in branch_interceptions. The address of
+ // the intercepted function is passed to the callback. For usage see
+ // BranchInterception.
+ using InterceptionCallback = std::function<void(uint64_t)>;
+
+ // Register a new BranchInterception object. If 'function' is branched to
+ // (e.g: "bl function") in the future; instead, if provided, 'callback' will
+ // be called otherwise a runtime call will be performed on 'function'.
+ //
+ // For example: this can be used to always perform runtime calls on
+ // non-AArch64 functions without using the macroassembler.
+ template <typename R, typename... P>
+ void RegisterBranchInterception(R (*function)(P...),
+ InterceptionCallback callback = nullptr) {
+ uintptr_t addr = reinterpret_cast<uintptr_t>(function);
+ std::unique_ptr<BranchInterceptionAbstract> intercept = std::make_unique<
+ BranchInterception<R, P...>>(function, callback);
+ branch_interceptions_.insert(std::make_pair(addr, std::move(intercept)));
+ }
+
protected:
const char* clr_normal;
const char* clr_flag_name;
@@ -4667,6 +4689,41 @@ class Simulator : public DecoderVisitor {
// Simulate a runtime call.
void DoRuntimeCall(const Instruction* instr);
+ // A pure virtual struct that allows the templated BranchInterception struct
+ // to be stored. For more information see BranchInterception.
+ struct BranchInterceptionAbstract {
+ virtual ~BranchInterceptionAbstract() {};
+ // Call the callback_ if one exists, otherwise do a RuntimeCall.
+ virtual void operator()(Simulator* simulator) const = 0;
+ };
+
+ // An entry denoting a function to intercept when branched to during
+ // simulator execution. When a function is intercepted the callback will be
+ // called if one exists otherwise the function will be passed to
+ // RuntimeCall.
+ template <typename R, typename... P>
+ struct BranchInterception : public BranchInterceptionAbstract {
+ BranchInterception(R (*function)(P...),
+ InterceptionCallback callback = nullptr)
+ : function_(function), callback_(callback) {}
+
+ void operator()(Simulator* simulator) const VIXL_OVERRIDE {
+ if (callback_ == nullptr) {
+ Simulator::RuntimeCallStructHelper<R, P...>::Wrapper(simulator,
+ reinterpret_cast<uint64_t>(function_));
+ } else {
+ callback_(reinterpret_cast<uint64_t>(function_));
+ }
+ }
+
+ private:
+ // Pointer to the function that will be intercepted.
+ R (*function_)(P...);
+
+ // Function to be called instead of function_
+ InterceptionCallback callback_;
+ };
+
// Processor state ---------------------------------------
// Simulated monitors for exclusive access instructions.
@@ -4867,6 +4924,12 @@ class Simulator : public DecoderVisitor {
// A configurable size of SVE vector registers.
unsigned vector_length_;
+
+ // Store a map of addresses to be intercepted and their corresponding branch
+ // interception object, see 'BranchInterception'.
+ std::unordered_map<uintptr_t,
+ std::unique_ptr<BranchInterceptionAbstract>>
+ branch_interceptions_;
};
#if defined(VIXL_HAS_SIMULATED_RUNTIME_CALL_SUPPORT) && __cplusplus < 201402L