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 <[email protected]> Signed-off-by: Florian Hofhammer <[email protected]> --- include/plugins/qemu-plugin.h | 13 +++++++++++++ plugins/api.c | 11 +++++++++++ scripts/qemu-plugin-symbols.py | 9 +++++++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/include/plugins/qemu-plugin.h b/include/plugins/qemu-plugin.h index a6ec8e275d..7b9cd6a971 100644 --- a/include/plugins/qemu-plugin.h +++ b/include/plugins/qemu-plugin.h @@ -76,6 +76,7 @@ typedef uint64_t qemu_plugin_id_t; * * version 6: * - changed return value of qemu_plugin_{read,write}_register from int to bool + * - added qemu_plugin_set_pc */ extern QEMU_PLUGIN_EXPORT int qemu_plugin_version; @@ -1042,6 +1043,18 @@ QEMU_PLUGIN_API bool 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 +__attribute__((__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 32eb086300..23c291f644 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" @@ -467,6 +468,16 @@ bool qemu_plugin_write_register(struct qemu_plugin_register *reg, return (gdb_write_register(current_cpu, buf->data, GPOINTER_TO_INT(reg) - 1) > 0); } +void qemu_plugin_set_pc(uint64_t vaddr) +{ + g_assert(current_cpu); + + g_assert(qemu_plugin_get_cb_flags() == QEMU_PLUGIN_CB_RW_REGS_PC); + + 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); diff --git a/scripts/qemu-plugin-symbols.py b/scripts/qemu-plugin-symbols.py index 69644979c1..ce99796ce2 100644 --- a/scripts/qemu-plugin-symbols.py +++ b/scripts/qemu-plugin-symbols.py @@ -20,9 +20,14 @@ def extract_symbols(plugin_header): # Remove QEMU_PLUGIN_API macro definition. content = content.replace('#define QEMU_PLUGIN_API', '') expected = content.count('QEMU_PLUGIN_API') - # Find last word between QEMU_PLUGIN_API and (, matching on several lines. + # Find last word between QEMU_PLUGIN_API and ( to get the function name, + # matching on several lines. Discard attributes, if any. # We use *? non-greedy quantifier. - syms = re.findall(r'QEMU_PLUGIN_API.*?(\w+)\s*\(', content, re.DOTALL) + syms = re.findall( + r'QEMU_PLUGIN_API\s+(?:__attribute__\(\(\S+\)\))?.*?(\w+)\s*\(', + content, + re.DOTALL, + ) syms.sort() # Ensure we found as many symbols as API markers. assert len(syms) == expected -- 2.53.0
