@samitouri / QOSamiQemu / commits / 4f55cd7801

plugins: add PC diversion API function

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 Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Signed-off-by: Florian Hofhammer <florian.hofhammer@epfl.ch> Link: https://lore.kernel.org/qemu-devel/20260305-setpc-v5-v7-4-4c3adba52403@epfl.ch Signed-off-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>

Florian Hofhammer committed Mar 5, 2026 at 11:06 UTC 4f55cd78015be1d994d4ec1586ceda05f2034dc5
3 files changed +31 -2
include/plugins/qemu-plugin.h
+13
@@ -76,6 +76,7 @@ typedef uint64_t qemu_plugin_id_t;
76 *
77 * version 6:
78 * - changed return value of qemu_plugin_{read,write}_register from int to bool
79 + * - added qemu_plugin_set_pc
80 */
81
82 extern QEMU_PLUGIN_EXPORT int qemu_plugin_version;
@@ -1042,6 +1043,18 @@ QEMU_PLUGIN_API
1043 bool qemu_plugin_write_register(struct qemu_plugin_register *handle,
1044 GByteArray *buf);
1045
1046 +/**
1047 + * qemu_plugin_set_pc() - set the program counter for the current vCPU
1048 + *
1049 + * @vaddr: the new virtual (guest) address for the program counter
1050 + *
1051 + * This function sets the program counter for the current vCPU to @vaddr and
1052 + * resumes execution at that address. This function does not return.
1053 + */
1054 +QEMU_PLUGIN_API
1055 +__attribute__((__noreturn__))
1056 +void qemu_plugin_set_pc(uint64_t vaddr);
1057 +
1058 /**
1059 * qemu_plugin_read_memory_vaddr() - read from memory using a virtual address
1060 *
plugins/api.c
+11
@@ -41,6 +41,7 @@
41 #include "qemu/log.h"
42 #include "system/memory.h"
43 #include "tcg/tcg.h"
44 +#include "exec/cpu-common.h"
45 #include "exec/gdbstub.h"
46 #include "exec/target_page.h"
47 #include "exec/translation-block.h"
@@ -467,6 +468,16 @@ bool qemu_plugin_write_register(struct qemu_plugin_register *reg,
468 return (gdb_write_register(current_cpu, buf->data, GPOINTER_TO_INT(reg) - 1) > 0);
469 }
470
471 +void qemu_plugin_set_pc(uint64_t vaddr)
472 +{
473 + g_assert(current_cpu);
474 +
475 + g_assert(qemu_plugin_get_cb_flags() == QEMU_PLUGIN_CB_RW_REGS_PC);
476 +
477 + cpu_set_pc(current_cpu, vaddr);
478 + cpu_loop_exit(current_cpu);
479 +}
480 +
481 bool qemu_plugin_read_memory_vaddr(uint64_t addr, GByteArray *data, size_t len)
482 {
483 g_assert(current_cpu);
scripts/qemu-plugin-symbols.py
+7 -2
@@ -20,9 +20,14 @@ def extract_symbols(plugin_header):
20 # Remove QEMU_PLUGIN_API macro definition.
21 content = content.replace('#define QEMU_PLUGIN_API', '')
22 expected = content.count('QEMU_PLUGIN_API')
23 - # Find last word between QEMU_PLUGIN_API and (, matching on several lines.
23 + # Find last word between QEMU_PLUGIN_API and ( to get the function name,
24 + # matching on several lines. Discard attributes, if any.
25 # We use *? non-greedy quantifier.
25 - syms = re.findall(r'QEMU_PLUGIN_API.*?(\w+)\s*\(', content, re.DOTALL)
26 + syms = re.findall(
27 + r'QEMU_PLUGIN_API\s+(?:__attribute__\(\(\S+\)\))?.*?(\w+)\s*\(',
28 + content,
29 + re.DOTALL,
30 + )
31 syms.sort()
32 # Ensure we found as many symbols as API markers.
33 assert len(syms) == expected