Gdb/Armulator 源代码分析
要清楚Armulator的执行过程就要从它的启动说起,当你在Gdb中键入target sim 去激活Amulator后Gdb首先进行命令行解释,并将current_target指针指向sim变量,即将Armulator的调试函数集赋予 Gdb,随后的函数调用堆栈如下: --%26;#224;gdbsim_open (…) in remote-sim.c. --%26;#224;sim_open(…) in /sim/arm/wrapper.c /*这里Amulator对调用参数进行分析处理*/ --%26;#224;*current_target->to_fetch_registers(-1) /*此函数指针实指向sim_fetch_register(…) in /sim/arm/wrapper.c*/ --%26;#224;sim_fetch_register(-1) /*此函数指针是在将current_target指向sim时,通过注册target_ops 结构完成挂接的*/ sim_fetch_register (sd, rn, memory, length) { ARMword regval; init (); file://就在这,Amulator进行了初始化 … } 至此Armulator被装载完毕,其后Gdb就是通过target_ops(定义在target.h)结构中的各个函数指针来完成对它的调试工. B. Armulator 内部机制 a. 初始化 从上述可知整个模拟器的初始化入口是在wrapper.c中的init( )函数,那么它到底又做了些什么呢? (原始的Gdb5.0中的Armulator是模拟ARM7DTMI 的,而补丁代码修改了memory map 并添加了timer 和uart 的IO能力使其能够模拟AT91.因为后者是对前者的增强,所以我们的分析以后者为准) Once the armulator to reset ,the ARMul_NewState will be called.And its task is to malloc a ARMul_state stuct which saves the armulator’s states and initialize it .And the ARMul_MemoryInit() will malloc 4m ram for you. #1 static void #2 init () #3 #4 static int done; #5 if (!done) #6 { #7 ARMul_EmulateInit (); file://Call this routine once to set up the emulator"s tables. #8 state = ARMul_NewState (); #9 state->bigendSig = (big_endian ? HIGH : LOW); #10 RMul_MemoryInit (state, mem_size); file://原始代码中的内存初始,但现在无用 #11 ARMul_OSInit (state); file://预装系统初始化 #12 ARMul_CoProInit (state); file://协处理器初始 #13 state->verbose = verbosity; #14 done = 1; #15 file://the below is added for AT91 #16 ARMul_SelectProcessor(state, ARM600); #17 ARMul_SetCPSR(state, USER32MODE); #18 ARMul_Reset(state); #19 } #20 } 因为这是补丁代码,难免又冗余出现,实际10-11行的两处掉用是没有实际意义的,而12行是协处理器的初始化,因为并没又模拟协处理器所以此处只是以备扩展. 重点的初始化过程是在ARMul_NewState(…)中的.首先它给模拟器的核心状态结构ARMul_State分配了空间,这个结构里保存了 Armulator的所有方面的状态,包括arm寄存器,流水线状态等等. 并赋予初值,我们以后就用state表示之.然后调用ARMul_Reset(…)进行更近一步的设置.而后者又主要完成模拟器内存结构的分配和rom映象的加载--/sim/arm/armmem.c/mem_reset(…),IO设备的状态初始—/sim/arm/armio.c /io_reset(…),你也可在这添加你的初始代码.到这就完成了Armulator的装载. (大家注意到18行也调用了ARMul_Reset(…),这是一个BUG,使得模拟器进行了两次内存分配,而浪费了系统内存.此处可删去.) Memory map 是所有模拟器的关键.Armulator由AT91向其他MCU移植时Memory map又是首先要处理的.Armulator的各个内存区是由mem_bank_t结构来描述的: typedef struct mem_bank_t { ARMword (*read_word)(ARMul_State *state, ARMword addr); void (*write_word)(ARMul_State *state, ARMword addr, ARMword data); unsigned long addr, len; char *filename; } mem_bank_t; file://定义在armmem.h中 Armulator的整个内存则是又此结构的数组static mem_bank_t mem_banks[]管理的. AT91的memory map如下: static mem_bank_t mem_banks[] = { /* the yuk"s below are to work around a uClinux/mount options problem */ { real_read_word, real_write_word, 0x01000000, 0x00400000, }, /* 2.4 */ { real_read_word, _write_word, 0x01400000, 0x00400000, "boot.rom"}, { real_read_word, real_write_word, 0x02000000, 0x00400000, }, /* 2.0 */ { real_read_word, real_write_word, 0x02400000, 0x00001000, }, /* yuk!*/ { real_read_word, _write_word, 0x04000000, 0x00400000, "boot.rom"}, { real_read_word, real_write_word, 0x00000000, 0x00004000, }, { io_read_word, io_write_word, 0xf0000000, 0x10000000, }, { fail_read_word, fail_write_word, 0, 0 } }; 根据mem_banks,mem_reset( )将分配空间,加载boot.rom文件. (原来的内存是由ARMul_MemoryExit( )释放的,但补丁后的代码就没了释放功能,这也是需要纠正的地方) a. 指令流 Armulator 加载完成后,就开始等待Gdb的运行命令了.最终/sim/wrapper.c/sim_resume( )是启动arm指令执行的地方. Sim_resume( )根据Gdb的要求选择用/sim/arm/arminit.c/ARMul_DoInstr()还是用/sim/arm/arminit.c /ARMul_DoProg()来调用 流水线模拟函数/sim/arm/armemu.c/ARMul_Emulate32().ARMul_DoInstr()和 ARMul_DoProg()的区别就是一个单步执行,一个连续执行指令. ARMul_DoProg()又不停的判断state->Emulate是否为STOP,如果是,模拟器又将停下等待Gdb的调试. 而在arm/armemu.c, /arm/armvirt.c 和 /arm/armsupp.c中