QEMU 用戶模式(User Mode)流程及源碼分析 (一)
用戶模式加載流程
用戶模式(User Mode)下的QEMU可以看作是其它架構(gòu)指令代碼的即時編譯(JIT)執(zhí)行器,客端(guest)代碼通過系統(tǒng)調(diào)用(syscall)的方式使用主端(host)的內(nèi)核,并和HOST端使用相同的資源。和系統(tǒng)模式(System Mode)相比,該方式運行效率更高。目前用戶模式支持Linux和BSD系統(tǒng)。
注:以下文檔中用{GUEST_ARCH}表示GUEST端的CPU架構(gòu)名稱字符串,比如GUEST端為x86_64架構(gòu)時,qemu-{GUEST_ARCH}表示qemu-x86_64。
以Linux User Mode為例,加載部分的代碼在linux-user文件夾內(nèi)。編譯完成后,會生成qemu-{GUEST_ARCH}的可執(zhí)行文件。
打開linux-user目錄下的main.c文件,main函數(shù)為應(yīng)用入口。會進行一系列初始化操作。比如配置GUEST端應(yīng)用的ROOT目錄(可通過命令行參數(shù)配置-L guest_root)。并初始化全局變量、系統(tǒng)調(diào)用和寄存器存儲。
該函數(shù)內(nèi)有特別關(guān)注3個函數(shù):cpu_create,loader_exec,cpu_loop
cpu_create
cpu_create 是創(chuàng)建CPUState對象的操作, 該對象用于描述CPU架構(gòu)的相關(guān)數(shù)據(jù), 包括寄存器數(shù)據(jù)、CPU特性等。該對象的成員變量env_ptr表示GUEST端CPU架構(gòu)相關(guān)數(shù)據(jù),其類型為CPU{GUEST_ARCH}State的指針。在結(jié)構(gòu)體定義中可看到env_ptr類型是CPUArchState,其定義是:
typedef CPU{GUEST_ARCH}Stat
e CPUArchState
CPU{GUEST_ARCH}State是一個結(jié)構(gòu)體,主要存儲GUEST端的寄存器數(shù)據(jù)。GUEST端寄存器對應(yīng)為HOST端的內(nèi)存,換句話說,讀寫寄存器的操作會被翻譯為特定內(nèi)存區(qū)域的讀寫。
在執(zhí)行翻譯后的代碼時, 會將CPUState對象的成員變量env_ptr(類型CPU{GUEST_ARCH}State)存入棧幀寄存器,然后對GUEST端的寄存器操作就可翻譯為針對棧幀寄存器間接尋址操作。
例如GUEST端為x86_64, HOST端為ARM64, 此時x86_64的代碼
翻譯到ARM64位時為
Guest端為x86_64時, env_ptr的類型是CPUX86State指針, 打開target/i386/cpu.h文件,可以看到R_ECX的值為1,該值也是x86_64平臺上RCX(ECX)寄存器的指令I(lǐng)D。 查看CPUX86State結(jié)構(gòu)體,regs字段即為存儲寄存器值的位置。 RCX寄存器存儲在env_ptr->regs[1], 該位置相對CPUX86State的偏移是(size_t)&(env_ptr->regs[1]) - (size_t)env_ptr = 8字節(jié)。在ARM64平臺上,env_ptr存儲于棧幀寄存器x29內(nèi),因此針對rcx寄存器的操作可轉(zhuǎn)化為針對[x29, #8]的間接尋址操作。 上述x0寄存器在實際翻譯過程中可為任意空閑可變寄存器。
loader_exec
loader_exe 是加載可執(zhí)行文件的的函數(shù), 其實現(xiàn)位于linux-user/linuxload.c文件內(nèi), 可加載ELF格式和FLAT格式的可執(zhí)行文件。
以加載ELF格式為例, load_elf_binary實現(xiàn)位于linux-user/elfload.c文件內(nèi), 在首次加載可執(zhí)行文件時,需要使用QEMU的加載器。
該可執(zhí)行文件加載動態(tài)庫,則是使用/lib/ld.**.so的GUEST端代碼可執(zhí)行庫。 因此需要確保/lib/ld.**.so之類的文件在GUEST端運行的根目錄內(nèi)。
GUEST端運行的根目錄可通過命令行參數(shù)-L guest_root指定。該目錄內(nèi)存儲GUEST端運行的環(huán)境。比如/lib/ld.**.so所在位置就是guest_root/lib/ld.**.so
需要特別說明的是,在linux環(huán)境下,GUEST端所需的各種動態(tài)庫(GUEST架構(gòu))都存放于guest_root/lib/{GUEST_ARCH}-linux-gnu/
cpu_loop
cpu_loop函數(shù)負責(zé)動態(tài)翻譯執(zhí)行的過程,為QEMU的核心部分, 其函數(shù)原型為
參數(shù)即為CPUState的env_ptr, 其過程分析將在下章講解。