* [PATCH] plugins: Add PC diversion API function
@ 2025-09-11 15:11 Florian Hofhammer
2025-09-19 10:59 ` Alex Bennée
0 siblings, 1 reply; 3+ messages in thread
From: Florian Hofhammer @ 2025-09-11 15:11 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, pierrick.bouvier, Richard Henderson,
Laurent Vivier, Warner Losh, Daniel P. Berrangé
This patch adds a plugin API function that allows diverting the program
counter during execution. A potential use case for this functionality is
to skip over parts of the code, e.g., by hooking into a specific
instruction and setting the PC to the next instruction in the callback.
Link: https://lists.nongnu.org/archive/html/qemu-devel/2025-08/msg00656.html
Signed-off-by: Florian Hofhammer <florian.hofhammer@epfl.ch>
---
include/qemu/qemu-plugin.h | 12 ++++++++++++
plugins/api.c | 9 +++++++++
2 files changed, 21 insertions(+)
diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index c450106af1..fe4e053c52 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -943,6 +943,18 @@ QEMU_PLUGIN_API
int qemu_plugin_write_register(struct qemu_plugin_register *handle,
GByteArray *buf);
+/**
+ * qemu_plugin_set_pc() - set the program counter for the current vCPU
+ *
+ * @vaddr: the new virtual (guest) address for the program counter
+ *
+ * This function sets the program counter for the current vCPU to @vaddr and
+ * resumes execution at that address. This function does not return.
+ */
+QEMU_PLUGIN_API
+G_NORETURN
+void qemu_plugin_set_pc(uint64_t vaddr);
+
/**
* qemu_plugin_read_memory_vaddr() - read from memory using a virtual address
*
diff --git a/plugins/api.c b/plugins/api.c
index eac04cc1f6..0511b72ebb 100644
--- a/plugins/api.c
+++ b/plugins/api.c
@@ -41,6 +41,7 @@
#include "qemu/log.h"
#include "system/memory.h"
#include "tcg/tcg.h"
+#include "exec/cpu-common.h"
#include "exec/gdbstub.h"
#include "exec/target_page.h"
#include "exec/translation-block.h"
@@ -457,6 +458,14 @@ int qemu_plugin_write_register(struct qemu_plugin_register *reg,
return gdb_write_register(current_cpu, buf->data, GPOINTER_TO_INT(reg) - 1);
}
+void qemu_plugin_set_pc(uint64_t vaddr)
+{
+ g_assert(current_cpu);
+
+ cpu_set_pc(current_cpu, vaddr);
+ cpu_loop_exit(current_cpu);
+}
+
bool qemu_plugin_read_memory_vaddr(uint64_t addr, GByteArray *data, size_t len)
{
g_assert(current_cpu);
--
2.51.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH] plugins: Add PC diversion API function
2025-09-11 15:11 [PATCH] plugins: Add PC diversion API function Florian Hofhammer
@ 2025-09-19 10:59 ` Alex Bennée
2025-09-23 9:42 ` Florian Hofhammer
0 siblings, 1 reply; 3+ messages in thread
From: Alex Bennée @ 2025-09-19 10:59 UTC (permalink / raw)
To: Florian Hofhammer
Cc: qemu-devel, pierrick.bouvier, Richard Henderson, Laurent Vivier,
Warner Losh, Daniel P. Berrangé
Florian Hofhammer <florian.hofhammer@epfl.ch> writes:
> This patch adds a plugin API function that allows diverting the program
> counter during execution. A potential use case for this functionality is
> to skip over parts of the code, e.g., by hooking into a specific
> instruction and setting the PC to the next instruction in the callback.
>
> Link:
> https://lists.nongnu.org/archive/html/qemu-devel/2025-08/msg00656.html
Thanks for taking the time to roll a proper patch. I assume this is
working for you OK?
If we were to go forward with up-streaming we would want expand a test
case to cover the new API. Maybe we could expand the syscall plugin with
an option to emulate a system call.
>
> Signed-off-by: Florian Hofhammer <florian.hofhammer@epfl.ch>
> ---
> include/qemu/qemu-plugin.h | 12 ++++++++++++
> plugins/api.c | 9 +++++++++
> 2 files changed, 21 insertions(+)
>
> diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
> index c450106af1..fe4e053c52 100644
> --- a/include/qemu/qemu-plugin.h
> +++ b/include/qemu/qemu-plugin.h
> @@ -943,6 +943,18 @@ QEMU_PLUGIN_API
> int qemu_plugin_write_register(struct qemu_plugin_register *handle,
> GByteArray *buf);
>
> +/**
> + * qemu_plugin_set_pc() - set the program counter for the current vCPU
> + *
> + * @vaddr: the new virtual (guest) address for the program counter
> + *
> + * This function sets the program counter for the current vCPU to @vaddr and
> + * resumes execution at that address. This function does not return.
> + */
> +QEMU_PLUGIN_API
> +G_NORETURN
> +void qemu_plugin_set_pc(uint64_t vaddr);
> +
The current potential foot guns I can see are:
- are we called from a callback with QEMU_PLUGIN_CB_RW_REGS
- could multiple hooks be wanting to set the PC?
Not doing the first could potentially loose you register values which
wouldn't be rectified until later in the block.
Currently we maintain the list qemu_plugin_insn.insn_cbs in the order
they are set. However if the callback that changes the flow is in the
middle we risk not calling the others.
Currently there is no protection against multiple callbacks wanting to
change the flow.
Maybe we need a new callback register that implies
QEMU_PLUGIN_CB_RW_REGS and we will always ensure is the last cb of the
chain?
> /**
> * qemu_plugin_read_memory_vaddr() - read from memory using a virtual address
> *
> diff --git a/plugins/api.c b/plugins/api.c
> index eac04cc1f6..0511b72ebb 100644
> --- a/plugins/api.c
> +++ b/plugins/api.c
> @@ -41,6 +41,7 @@
> #include "qemu/log.h"
> #include "system/memory.h"
> #include "tcg/tcg.h"
> +#include "exec/cpu-common.h"
> #include "exec/gdbstub.h"
> #include "exec/target_page.h"
> #include "exec/translation-block.h"
> @@ -457,6 +458,14 @@ int qemu_plugin_write_register(struct qemu_plugin_register *reg,
> return gdb_write_register(current_cpu, buf->data, GPOINTER_TO_INT(reg) - 1);
> }
>
> +void qemu_plugin_set_pc(uint64_t vaddr)
> +{
> + g_assert(current_cpu);
> +
> + cpu_set_pc(current_cpu, vaddr);
> + cpu_loop_exit(current_cpu);
> +}
> +
As you see the actual mechanics of restarting at the new PC is the easy
bit ;-)
> bool qemu_plugin_read_memory_vaddr(uint64_t addr, GByteArray *data, size_t len)
> {
> g_assert(current_cpu);
--
Alex Bennée
Virtualisation Tech Lead @ Linaro
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] plugins: Add PC diversion API function
2025-09-19 10:59 ` Alex Bennée
@ 2025-09-23 9:42 ` Florian Hofhammer
0 siblings, 0 replies; 3+ messages in thread
From: Florian Hofhammer @ 2025-09-23 9:42 UTC (permalink / raw)
To: Alex Bennée
Cc: qemu-devel, pierrick.bouvier, Richard Henderson, Laurent Vivier,
Warner Losh, Daniel P. Berrangé
On 19/09/2025 12:59, Alex Bennée wrote:
> Thanks for taking the time to roll a proper patch. I assume this is
> working for you OK?
It worked well with my initial tests (basically just a simple
hello-world). After I sent the patch, I started using the function
more extensively and it's triggering assertions in the
cpu_exec_longjmp_cleanup function (cpu != current_cpu). I am debugging
this and I'm trying to figure out what I'm missing in my (arguably
very minimal) patch that would cause the stack and with it the pointer
to the cpu struct to get corrupted.
> If we were to go forward with up-streaming we would want expand a test
> case to cover the new API. Maybe we could expand the syscall plugin with
> an option to emulate a system call.
I'll add test cases once I'm certain it works properly!
> The current potential foot guns I can see are:
>
> - are we called from a callback with QEMU_PLUGIN_CB_RW_REGS
> - could multiple hooks be wanting to set the PC?
>
> Not doing the first could potentially loose you register values which
> wouldn't be rectified until later in the block.
Good point, I'll add checks for that.
> Currently we maintain the list qemu_plugin_insn.insn_cbs in the order
> they are set. However if the callback that changes the flow is in the
> middle we risk not calling the others.
>
> Currently there is no protection against multiple callbacks wanting to
> change the flow.
As the function immediately changes control flow, the first call to
this API would win immediately, without any further callbacks being
processed. Do you think it makes more sense to set a flag and check
for the flag after all callbacks have been processed?
In that case, the question arises however how to handle multiple calls
to qemu_plugin_set_pc in different callbacks. Is it the first call
that should actually win, or the last one? Or should later calls
trigger asserts if the flag was already set so that we ensure the
function is only called once? Happy to get your thoughts on this!
> As you see the actual mechanics of restarting at the new PC is the easy
> bit ;-)
Well, if it works properly... ;-)
Best regards,
Florian
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2025-09-23 9:48 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-11 15:11 [PATCH] plugins: Add PC diversion API function Florian Hofhammer
2025-09-19 10:59 ` Alex Bennée
2025-09-23 9:42 ` Florian Hofhammer
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).