QEMU的最大亮點就是動態翻譯技術,正是由於這個強勁的引擎,使QEMU可以在不使用任何加速技術的情況下也能達到良好的速度,並能夠橫跨多種平臺運行,藉助於特定版本的GCC編譯器,還能夠仿真多種架構的處理器。這裏我說的動態翻譯指的是QEMU早期版本使用的“dynamic translation”,因爲從0.10版本開始使用的是“TCG”,擺脫了對GCC版本的依賴,並且不再需要編譯中間工具。 簡單來說,動態翻譯的基本思想就是把每一條x86指令切分成爲若干條微操作,每條微操作由一段簡單的C代碼來實現(見'target-i386/op.c'),然後通過中間工具('dyngen')提取相應的目標文件('op.o')來生成一個動態代碼生成器,最後把這些微操作組合成一個函數(見'op.h:dyngen_code()')。 在一個真實的CPU裏,執行流程由取指、譯指、執行指令三部分組成。在QEMU仿真的處理器中同樣如此,取指和執行指令不需多說,關鍵的是譯指這道工序,由反彙編器、dyngen程序、動態代碼生成器三部分來共同完成。我的實驗環境是X86平臺+0.7.2版本源碼,這裏我以BIOS啓動代碼的第一條指令jmp f000:e05b來詳細說明,該指令的彙編代碼是:EA 5B E0 00 F0,反彙編器首先分析EA,知道這是一條16位的跳轉指令,因此接着取出後面的EIP和CS。具體過程在 translate.c:disas_insn() 可見,它被分解爲如下幾條微操作: gen_op_movl_T0_im(selector); // 把0xf000放到T0中 它們的實現函數分別如下: static inline void gen_op_movl_T0_im(long param1) static inline void gen_op_movl_T1_imu(long param1) static inline void gen_op_movl_seg_T0_vm(long param1) static inline void gen_op_movl_T0_T1(void) static inline void gen_op_jmp_T0(void) static inline void gen_op_movl_T0_0(void) static inline void gen_op_exit_tb(void) 可以看出,以上函數都非常簡單,其實就是在操作碼緩衝區中放一個索引號。真正調用的函數在op.c中,如下: void OPPROTO op_movl_T0_im(void) void OPPROTO op_movl_T1_imu(void) void OPPROTO op_movl_seg_T0_vm(void) void OPPROTO op_movl_T0_T1(void) void OPPROTO op_jmp_T0(void) void OPPROTO op_movl_T0_0(void) #define EXIT_TB() asm volatile ("ret") 在我的實驗環境中,T0和T1的定義如下: mov eax,dword ptr [env (1FD1F14h)] // -> gen_op_movl_T0_im(selector) 現在可以清楚看到了,這就是Target上一條JMP指令在Host上的對應代碼實現。 本來還應該再講講 rep、call 之類的指令,因爲這也是QEMU比其它仿真器(如Bochs之類)快的原因之一,包括翻譯後指令的重用、一次性執行多條Target指令、直接使用常量等特性,但是發現打字實在是很累,代碼太多了大家也看的眼花,所以就先說到這裏吧。 |
QEMU技術分析1 - 動態翻譯(dynamic translation)
QEMU技術分析1 - 動態翻譯(dynamic translation)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.