0

我在QEMU源文件编译选项中添加了-fPCI,在最后的链接命令中添加了-shared,这样QEMU就变成了一个可以动态加载的共享库。从那时起,我开始尝试了解 QEMU。我使用 dlopen 动态加载 qemu 并使用 dlsym 搜索 qemu 中的函数。这是我的代码:

#include<iostream>
#include<dlfcn.h>
#include<stdint.h>

using namespace std;

int main(int argc,char* argv[],char* envp[])
{
    void* handle = dlopen("/home/jack/qemu/qemu-5.0.0/arm-softmmu/libqemu-system-arm.so",RTLD_NOW);
    if(handle == nullptr)
    {
        printf("%s\n",dlerror());
        return 0;
    }

    void    (* qemu_init             )(int,char**,char**);
    void    (* qemu_main_loop        )(void);
    void    (* qemu_cleanup          )(void);
    bool    (* main_loop_should_exit )(void);
    void    (* main_loop_wait        )(int);
    int64_t (* cpu_get_icount        )(void);
    int64_t (* cpu_get_icount_raw    )(void);
    int64_t (* cpu_icount_to_ns      )(int64_t);
    int64_t (* cpu_get_clock         )(void);
    int64_t (* cpu_get_ticks         )(void);

#define GET_SYMBOL_AND_CHECK(X) *((void**)(&X)) = dlsym(handle,#X);if(nullptr == X){printf("lost symbol: "#X"\n");return 0;}
    GET_SYMBOL_AND_CHECK(qemu_init);
    GET_SYMBOL_AND_CHECK(qemu_main_loop);
    GET_SYMBOL_AND_CHECK(qemu_cleanup);
    GET_SYMBOL_AND_CHECK(main_loop_should_exit);
    GET_SYMBOL_AND_CHECK(main_loop_wait);
    GET_SYMBOL_AND_CHECK(cpu_get_icount);
    GET_SYMBOL_AND_CHECK(cpu_get_icount_raw);
    GET_SYMBOL_AND_CHECK(cpu_icount_to_ns);
    GET_SYMBOL_AND_CHECK(cpu_get_clock);
    GET_SYMBOL_AND_CHECK(cpu_get_ticks);
#undef GET_SYMBOL_AND_CHECK

    char* _argv[]=
    {
        "qemu-system-arm",
        "-M",
        "vexpress-a9",
        "-nographic",
        "-kernel",
        "/home/jack/temp/u-boot-2015.01/u-boot",
        "-icount",
        "1",
        "-singlestep",
        "-S",
        "-s",
    };
    int _argc=sizeof(_argv) / sizeof(_argv[0]);

    qemu_init(_argc,_argv,envp);

    while(!main_loop_should_exit())
    {
        main_loop_wait(false);

        //my test code:
        int64_t icount_raw = cpu_get_icount_raw();
        int64_t icount = cpu_get_icount();
        int64_t ticks = cpu_get_ticks();
        int64_t clock = cpu_get_clock();

        printf("----------icount_raw: %jd\n",icount_raw);
        printf("----------icount: %jd\n",icount);
        printf("----------ticks: %jd\n",ticks);
        printf("----------clock: %jd\n",clock);
    }

    qemu_cleanup();
    dlclose(handle);
    return 0;
}

程序的输出如下:

----------icount_raw: 0
----------icount: 0
----------ticks: 0
----------clock: 27595
----------icount_raw: 0
----------icount: 0
----------ticks: 0
----------clock: 47394


U-Boot 2015.01 (May 25 2020 - 14:42:11)

DRAM:  128 MiB
WARNING: Caches not enabled
Flash: 128 MiB
MMC:   MMC: 0
*** Warning - bad CRC, using default environment

In:    serial
Out:   serial
Err:   serial
Net:   smc911x-0
Warning: smc911x-0 using MAC address from net device

Warning: Your board does not use generic board. Please read
doc/README.generic-board and take action. Boards not
upgraded by the late 2014 may break or be removed.
Hit any key to stop autoboot:  2 ----------icount_raw: 60040125
----------icount: 120271139
----------ticks: 120271139
----------clock: 1001128004
----------icount_raw: 119738560
----------icount: 239668009
----------ticks: 239668009
----------clock: 2002239949
----------icount_raw: 180295711
----------icount: 360782311
----------ticks: 360782311
----------clock: 3003347066
----------icount_raw: 240405702
----------icount: 481002293
----------ticks: 481002293
----------clock: 4004446427
----------icount_raw: 300858002
----------icount: 601906893
----------ticks: 601906893
----------clock: 5005552419
----------icount_raw: 361297422
----------icount: 722785733
----------ticks: 722785733
----------clock: 6006625721
----------icount_raw: 420679210
----------icount: 841549309
----------ticks: 841549309
----------clock: 7007717838
----------icount_raw: 424900860
----------icount: 849992609
----------ticks: 849992609
----------clock: 7082080834
----------icount_raw: 424900883
----------icount: 849992655
----------ticks: 849992655
----------clock: 7082105752
----------icount_raw: 424900906
----------icount: 849992701
----------ticks: 849992701
----------clock: 7082120318
QEMU: Terminated

我为 QEMU 的主循环建模并编写了这个 while 循环。我每次循环打印获取的数据,发现icount_raw可以表示CPU当前执行的指令数。关于其他数据,我仍然感到困惑。本程序运行时,uboot程序可以正常运行。我发现屏幕上打印数据的频率大约是每秒一次。每次 icount_raw 增加很多。当我使用gdb远程控制程序运行时,使用“si”命令时,icount_raw每次递增1,这就是我想要实现的:每次QEMU只执行一条指令,就可以返回主循环. 我想知道如何修改QEMU的代码,让QEMU每次执行一条指令时,都能返回到主循环,而不是使用gdb的“si”命令。以后想知道每次执行N条指令后如何控制QEMU返回主循环。这个N我可以自由设置。我了解QEMU的事件循环是基于Glib的,我想我的问题可能需要修改QEMU中调用Glib的代码。

4

1 回答 1

0

尝试获取 QEMU 的内部结构并将它们放入 DLL 是完全不受支持的,因此您必须自己尝试找出如何修复其中的错误,恐怕。

通常,您描述的行为是预期的:QEMU 将客户代码编译成对应于多个客户指令的“翻译块”,然后它还尝试在可能的情况下直接在这些翻译块之间创建跳转。这对性能很重要:我们不想比我们绝对必须更频繁地返回顶级执行循环。

其中一些优化是可控的:在上游 QEMU 中,-singlestep 命令行选项意味着“在每个 TB 中只放置一条客户指令”,而“-d nochain”选项意味着“不要进行将 TB 链接在一起的优化” . 大多数情况下,这样做对于调试目的很有用:行为更容易理解,调试日志更容易阅读。缺点是性能贯穿地板。

于 2020-05-27T15:33:34.760 回答