skyeye学习笔记
skyeye学习笔记 v0.0[email protected]
1.对sim的初始化
因为sim是作为gdb的一个target(应该可以称为调试目标吧 呵呵)存在的,所以在gdb运行的时候,要先对这个target进行初始化。在remote-sim.c文件中的函数_initialize_remote_sim就是对这个target的初始化函数,而这个函数将在启动的时候被调用。在这个函数中有3个操作:
第1个操作,调用init_gdbsim_ops函数,这个函数用来初始化全局结构变量gdbsim_ops,其的结构类型是target_ops,每个gdb中的target都有这么一个结构,这个结构定义在target.h文件中。这个变量的作用就是当调试开始的时候,gdb可以通过结构找到相应的对target进行控制的函数。
第2个操作,调用add_target将前面初始化好的gdbsim_ops增加到target_structs中用来方便系统对这个结构的使用。
第3个操作,用add_com给gdb增加了一个命令sim,这个命令通过调用sim_do_command这个函数可以帮助实现向sim发送命令,这个功能将在后面进行一次实际的应用,在那再详细介绍。
还有一点比较重要,sim目录下包含了很多cpu相关的目录,其中就就有arm目录,这就是最基本的skyeye的代码,gdb对skyeye的控制就是通过target的接口函数调用skyeye的函数来完成的。
2.对linux.2.4.x的读入
因为这个读入的操作跟使用命令file进行读入是基本一样的,所以对file命令的过程进行分析。
在用户执行file命令后,将调用exec.c文件中的函数file_command。在这个函数调用的函数exec_file_command用来将可执行文件打开,在其中target_preopen函数会检查前面是否有文件打开执行等等,并向用户提问是否进行清除,exec_file_attach则是具体的打开文件的操作,在这里打开了一个可执行文件结构exec_bfd,这将在后面被用到。symbol_file_command函数用来将符号表等进行初始化。
而在启动对可执行文件的读入在main.c文件中的函数captured_main对exec_file_command和symbol_file_command进行了调用。
3.target sim
在这里主要完成对sim的初始化的工作,这些工作是通过调用remote-sim.c文件中的函数gdbsim_open函数来完成的。
在开始的时候初始化了一些参数,然后传递给了wrapper.c文件中的sim_open函数,在这里设置了skyeye的引点以及一些其他的东西。
而后调用函数push_target将gdbsim_ops根据其的to_stratum增加到target_stack列表上相应的层次上,如果这层原来有target,就先将其remove掉,接着通过这个列表生成current_target,这个结构就是当前的target结构,当调用调试命令的时候,current_target中的函数就将被调用。
最后用参数-1调用了gdbsim_fetch_register函数,当这个函数的参数为-1的时候,将以参数0到15调用这个函数,代表对arm的16个寄存器的初始化。
在这些调用的时候只做了简单的检查就调用了wrapper.c中的函数sim_fetch_register,在这个函数中第一步是调用函数init,这个函数中进行了大部分的skyeye初始化工作。
在init函数中可以看到定义了一个static变量done,用来保证初始化只进行一次。ARMul_EmulateInit函数对ARMul_ImmedTable和ARMul_BitList,不过我没能看出这2个东西的具体作用。
ARMul_NewState函数对保存着Armulator的所有方面状态的state进行了空间分配和其中元素中的初始化,具体每个元素的定义可以看armdefs.h对ARMul_State定义中的每个元素的注释,写的很明确。
接着是根据big_endian设置state->bigendSig,也就是来设置skyeye的引点。
下面调用的2个函数ARMul_MemoryInit和ARMul_OSInit都是没做什么工作,所以不做分析。
接着在后面设置done为1表明init操作已经做过了。
skyeye_option_init函数对skyeye_config结构参数基本初始化,这个结构将会保存设置好的设备方面的很多参数。
skyeye_read_config函数是对配置文件进行读取分析。
先调用了函数skyeye_net_init对skyeye_config的net进行了初始化(个人觉得从功能上讲这个函数在上一个函数中调用更合适)。接着将循环对每行用函数parse_line_unformatted进行分析,函数会先对行进行简单的分析,然后跟skyeye_config.h中的结构数组skyeye_options进行比较并调用这个数组中相应实际配置函数,这些函数最后都会根据参数对skyeye_config结构中相应条目进行设置。
如果skyeye_net_on为1,则调用nic_init对网卡进行初始化。
接着调用了mach_init,这是前面初始化好的函数指针,会根据配置文件中设置的mach来在arm_machines数组中选择。
这些初始化函数都先调用了ARMul_SelectProcessor函数。这个函数根据参数对state的模式进行了设置。
接着调用了函数ARMul_CoProInit。ARMul_CoProInit函数是对skyeye中的协处理器进行了初始化。协处理器在skyeye被处理成了ARMul_State结构中的几个函数指针CPInit、CPExit、(这2个在启动和停止的时候被调用)LDC、STC、MRC、MCR、CDP、(这几个代表了相应的协处理器指令)CPRead、CPWrite(这两个的作用还没看明白),2个数据指针CPData和CPRegWords(前面一个好象在具体代码中没用到,后面这个没搞清楚作用)组成,这样skyeye就可以很方便的拥有扩展性很好的16个协处理器。
在ARMul_CoProInit函数中,先是循环调用ARMul_CoProDetach函数对16个CP进行了简单的函数初始化,然后是根据芯片对几个协处理器进行单独的初始化,最后循环调用了每个cp的CPInit函数,返回。
后面主要是对skyeye_config.mach中函数指针的初始化。
函数ARMul_Reset,这里是对state的进一步的初始化。
在ARMul_Reset函数最开始的部分是根据state->prog32Sig也就是系统模式是否用32位模式,来设置Reg和Cpsr(在26位模式中cpsr在r15中),在其后调用的函数ARMul_CPSRAltered是在修改过Cpsr后对state中其他参数值进行更新的函数。
在后面调用的mmu_reset是对state->mmu中内容进行初始化的函数。
其后是mem_reset函数,其主要作用是对state->mem也就是skyeye中的内存进行初始化,其中还包括了将相应的文件系统内存镜象(也就是内个boot.rom)读入内存的过程,这些要做的工作都是根据前面根据配置文件初始化好的skyeye_config.mem的值来进行的。
接着被调用的函数是io_reset,在函数中调用了mach_init中初始化好的函数指针skyeye_config.mach->mach_io_reset,给不同的设备对自己的设备结构进行初始化。
后面的部分是gdbsim_fetch_register函数本来该做的工作。
4.load
此命令的作用是将要调试的机器代码装入skyeye的内存,注意前面装载的是文件系统。
首先被调用的是remote-sim.c中的函数gdbsim_load,接着就在这个函数中调用了wrapper.c中的函数sim_load。
在这个函数中第一个被调用的函数是sim-load.c中的sim_load_file,上面提到的从文件中读出机器代码然后装入skyeye的内存的工作都是在这个函数中执行的。这里进行的工作就是先将elf文件打开,然后分析,调用函数指针do_write也就是前面传递来的函数sim_write将读出的节的内容按照其地址和长度写入skyeye的内存。最后将打开elf文件的操作指针返回。
下面进行的是设置启动点也就是PC寄存器值的过程,因为刚才的情况,在这里启动点也就是PC还是被设置成了0。
然后函数就返回了,基本的load操作也就完成了。
5.run
在执行指令后被调用的是remote-sim.c中的函数gdbsim_create_inferior,在开始是对当前情况进行了一些检查和清除过去信息的工作。
接着做的是根据函数gdbsim_create_inferior的参数exec_file和args构造命令行启动参数argv,其中exec_file中是前面打开的kernel的完整路径名,而args中就是run后面跟着的执行参数,如果没有就是空字符串,整理后的多层指针中就依次在每个指针中存储了启动参数。
接着调用wrapper.c文件中的函数sim_create_inferior来对skyeye进行近一步的初始化。
一开始又是一次对skyeye中PC寄存器的初始化,这次因为是按照参数可执行文件句柄结构abfd也就是全局变量exec_bfd进行的。这个变量是在3.2部分介绍的过程初始化的,所以PC也是由这个结构决定的。最后判断是否skyeye_config.start_address为0,如果不为0则表明在配置文件中设置了启动点,则设置PC为skyeye_config.start_address的值。
然后是根据argv的值对state->CommandLine进行初始化,实际就是依次将其中参数用空格分隔拷贝进去。
最后是根据env对state->MemSize进行设置。函数返回。
设置记录模拟器下层进程pid的inferior_pid为42,这么做因为在当前的情况下并没有单独的进程进行模拟,所以只进行这个设置表明模拟已经开始。
调用函数insert_breakpoints将刚才清除掉的断点重新加入代码中。用函数clear_proceed_status清楚掉以前程序执行的信息。
调用infrun.c中的函数proceed开始skyeye的运行。这个函数的3个参数的意义分别是:
addr,运行开始的位置,如果是-1则从停止的地方也就是pc寄存器指定的位置开始执行。
siggnal,好象跟退出信号有关的一个参数,我还没搞清楚作用。
step,运行的方式,如果为非0则每执行一条指令都进入trap方便调试。
在这个函数中开始是一些初始化工作,主要是对infrun.c中函数wait_for_inferior的调用。在这个函数中对remote-sim.c文件中的wait_for_inferior进行了调用,在这个函数中调用了wrapper.c函数中的sim_resume。
sim_resume函数是Armulator启动arm指令执行的地方。在这个函数中会根据执行的参数来使用2个不同的执行函数ARMul_DoInstr()和ARMul_DoProg(),这2个函数的区别是一个单步执行而一个连续执行指令。这2个函数都是调用armemu.c中的函数ARMul_Emulate32。
ARMul_Emulate32函数就是skyeye的核心解码和执行函数。
6.设备
设备的初始化在前面已经提到过了。
在解码循环中,每次都会调用函数io_do_cycle,这个函数中调用了前面初始化好的skyeye_config.mach->mach_io_do_cycle,这个函数检查设备是否需要发生中断,如果要发生中断则设置中断变量(会根据mach的不同有很大的差异,中断处理程序读取IO地址中相应地址就可以将这个值读出,读出后就可以分析出中断号码),然后调用这个mach的mach_update_int,在这个函数中根据中断变量以及中断阻塞变量(通过设置这个变量可以阻塞某个特定中断的发生)来设置state->NirqSig和state->NfiqSig,这2个变量表明系统中有irq和fig发生。
在调用io_do_cycle函数以前的部分代码是模拟了中断发生后芯片对其的处理过程,那就表明中断会在完成当年指令的处理后,才会被处理,这么作应该是模仿了arm芯片对中断的处理方式。具体的过程是检查了state->NirqSig或者state->NfiqSig是否被设置,同时检查IFLAG或者FFLAG是否没被设置(这2个宏是对state->IFFlags进行分析判断,这个IFFlags是在设置CPSR的时候同时设置的,这个变量表明了标志位中I位和F位,还有NFlag、ZFlag、CFlag、VFlag也是同样的作用,在注释中介绍是为了提高速度),如果没被设置表明中断没被阻塞,则调用处理函数ARMul_Abort进行处理。 GOOD!欢迎对skyeye进行进一步的开发。 very good! teawater感谢你一直以来对skyeye的支持,你在gdb中调入了一个单步反编译功能我们已经移值到skyeye上去了。很快就可以根大家见面了。再次感谢你 道谢就有点见外了:mrgreen:
页:
[1]