【问题标题】:Qemu plugin functions - how to access guest memory and registersQemu 插件功能 - 如何访问客户内存和寄存器
【发布时间】:2020-07-04 09:37:25
【问题描述】:

背景

Qemu version 4.2.0,于 19 年 12 月发布,包括一个名为 TCG Plugins 的新功能。他们在tests/plugins 目录中有一些示例,API 或多或少定义在qemu-plugin.h 中。

该文件定义了两个枚举类型qemu_plugin_cb_flagsqemu_plugin_mem_rw,它们被传递到注册回调的函数中。这些枚举似乎表明回调是读取还是写入 CPU 寄存器或内存。但是,所有示例插件都使用QEMU_PLUGIN_CB_NO_REGS,并且只有 2 个插件使用内存访问枚举。 hotpages.cmem.c 使用 QEMU_PLUGIN_MEM_RW 作为默认注册内存回调 (qemu_plugin_register_vcpu_mem_cb)。 mem.c 在插件加载时有一个参数来选择它是读还是写,但是,它似乎在回调函数中没有任何区别。

问题

我的问题是,如何从插件回调函数访问客户内存和寄存器? API 似乎表明这是可能的,因为回调注册要求您说明您是否会访问它们,以及它是 RW 还是只是读取。

是否有使用这部分 API 的示例?我意识到这是 Qemu 功能的一个非常新的部分。

代码

当您在指令上注册回调时,例如在insn.c 中,您可以获得指令的虚拟地址。

uint64_t insn_vaddr = qemu_plugin_insn_vaddr(insn);

我正在运行一个裸机 ARM 程序,这个虚拟地址似乎与 ELF 文件中指令的地址相关。

在内存回调函数内部,你可以调用qemu_plugin_get_hwaddr来获取内存访问的硬件地址,但我不确定那个结构到底代表什么。

相关

这个答案已有 7 年历史,建议使用 GDB 界面。我的问题与使用 TCG 插件功能特别相关。

【问题讨论】:

    标签: c arm qemu bare-metal


    【解决方案1】:

    我刚刚遇到了同样的问题。 qemu 团队似乎真的试图阻止我们使用插件中的 CPU 或内存。我尝试通过修改 Makefile 来包含我需要的头文件,但这些头文件不应该包含在插件等外部代码中。我无法让它编译。

    正如你所说,有迹象表明这是可能的。我的猜测是该功能尚未完全实现,也许很快就会实现。

    与此同时,在我们等待合适的方法来执行此操作时,我是这样破解它的:

    注册

    在我的例子中,CPU 是一个 ARM。我先展示代码再解释。

    void *qemu_get_cpu(int index);
    
    static uint32_t get_cpu_register(unsigned int cpu_index, unsigned int reg) {
        uint8_t* cpu = qemu_get_cpu(cpu_index);
        return *(uint32_t*)(cpu + 33488 + 5424 + reg * 4);
    }
    

    我首先声明qemu_get_cpu 函数,因为我们不能包含它的标头。该函数返回一个CPUState*。由于我的 CPU 是 ARM,所以我知道指针实际上是 ARMCPU*。由于继承是在 qemu 中实现的,因此从 CPUState*ARMCPU* 的强制转换是无操作的,因此无需执行任何操作。

    然后,查看target/arm/cpu.h,我们可以看到该结构:

    struct ARMCPU {
        /*< private >*/
        CPUState parent_obj;
        /*< public >*/
    
        CPUNegativeOffsetState neg;
        CPUARMState env;
        // ...
    

    我使用this compiler trick 来获取 CPUState 和 CPUNegativeOffsetState 的大小,在我的例子中分别是 33488 和 5424。这为我们提供了CPUARMState 的偏移量,其开头如下:

    typedef struct CPUARMState {
        /* Regs for current mode.  */
        uint32_t regs[16];
        // ...
    

    所以寄存器只是开始,这就是我使用reg * 4的原因。

    现在我们可以读取我们的寄存器,下一步是......

    从内存中读取

    这个比较简单,我是从qemu本身的gdbstub.c得到的:

    void cpu_physical_memory_rw(uint64_t addr, uint8_t *buf,
                                uint64_t len, int is_write);
    
    // and in my function:
        char name[9] = {0};
        cpu_physical_memory_rw(name_addr, name, 8, 0);
    

    我们只是声明我们需要的方法并调用它。似乎该方法永远不会失败,从未映射的内存中读取什么都不做。

    【讨论】:

    • 我很高兴你找到了一种让它工作的方法。我得出的结论是,至少使用 QEMU 插件界面提供的工具是不可能的。对于我的应用程序,除了修改缓存状态的指令外,我已经能够在没有这个的情况下度过难关。这将非常有帮助,所以谢谢。
    【解决方案2】:

    我刚刚发现,在用户模式下,您只需取消引用 vaddr 即可从内存中获取值。

    char *val = (char*) vaddr;
    

    另外,我在cpu.c找到了下面这个方法,在gdbstub不可用的时候也可以使用。

    int cpu_memory_rw_debug(void *cpu, uint64_t addr,
                            void *ptr, uint64_t len, bool is_write);
    

    尽管如此,这是一种解决方法,我希望 TCG 插件有更多功能。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多