skyeye的动态二进制翻译_第1页
skyeye的动态二进制翻译_第2页
skyeye的动态二进制翻译_第3页
skyeye的动态二进制翻译_第4页
skyeye的动态二进制翻译_第5页
已阅读5页,还剩8页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、SKYEYE 指令动态翻译模拟 (DBCT) 实现介绍 v0.0teawater()转载请标明来自 修改记录: v0.02006-06-16 , v0.0 版本编写完成。2006-05-27 ,文档创建。目录1. 写在前面2. 概述3. 微指令3.1. 微指令的结构和初始化3.2. 微指令中的变量3.3. 微指令中的函数调用3.4. 微指令中的异常处理3.5. 微指令中的跳转3.6. 微指令分类介绍3.6.1. 概述3.6.2. arm2x86.c:op_init3.6.3. arm2x86_test.c:ar

2、m2x86_test_init3.6.4. arm2x86_shift.c:arm2x86_shift_init3.6.5. arm2x86_psr.c:arm2x86_psr_init3.6.6. arm2x86_movl.c:arm2x86_movl_init3.6.7. arm2x86_mul.c:arm2x86_mul_init3.6.8. arm2x86_mem.c:arm2x86_mem_init3.6.9. arm2x86_dp.c:arm2x86_dp_init3.6.10. arm2x86_coproc.c:arm2x86_coproc_init3.6.11. arm2x86

3、_other.c:arm2x86_other_init4. 翻译块 (TB)4.1. 概述4.2. tb.h:struct tb_s4.3. TB_TBT_SIZE 和 TB_TBP_SIZE4.4. tb.c:tb_memory_init4.5. tb.c:tb_insn_len_max_init5. 初始化函数 arm2x86.c:arm2x86_init6. 翻译执行过程6.1. armemu.c:ARMul_Emulate32_dbct6.2. tb.c:tb_find6.4. tb.c:tb_translate6.5. tb.c:translate_word1. 写在前面本文针对 s

4、kyeye-1.2-RC7-3 进行编写。关于 skyeye 本身结构的介绍可以参见 skyeye 学习笔记 (/forum /gshowthreaded.php?Cat=&Board=program&Number=522443&page=1& amp;view=collapsed&sb=5&o=all&fpart=) 。在下面为了方便介绍,都将 SKYEYE 指令动态翻译模拟简 称为 DBCT 。DBCT 的实现并不算是很好的, 个人推荐对动态翻译有兴趣的朋友们读一下 QEMU 的代码。2. 概述DBCT 是在指令模拟思想上参照了 QEMU(h

5、ttp:/fabrice.bellard.free.fr/qemu/) ,但在实现上也 有不同,后面进 行每个部分介绍的时候,我会依次介绍到。DBCT 就是将若干条连续的被模拟指令组成一组(称为一个翻译块 TB) ,将每条指令根据其行为直接翻译成若干条微指令 (这里跟 QEMU 不同, QEMU 在翻译过程中有中间码存在 ), 每条微指令代表一种操作行为并由若干条本地指令组成, 最后就得到一组跟 TB 对应的本地 指令, 再在结尾增加返回指令, 对这组指令开始的地址进行函数调用, 就达到了指令模拟的 目的。然后对指令进行解码分析, 根, SKYEYE 普通指令模拟方C 语言的函数等,然后编译其

6、他我所了解指令模拟方式还有最常见的 直接读入一条指令, 据需要进行其操作,然后再读入下一条指令,重复刚才的工作 式就是这种类型。还有一种模拟方式是将要模拟的硬件行为翻译成某种语言比如 执行来进行指令模拟。3. 微指令3.1. 微指令的结构和初始化在 arm2x86_init 函数中在 tb_insn_len_max_init 函数之前的被调用的函数都是 微指令初始化 函数。在 DBCT 代码中,每条微指令都被封装在定义在 arm2x86.h 的 op_table_t 结构中,这个结构 中的 op 是指向这个微指令的地址,而 len 则是这个微指令的长度 。每条微指令的的初始化 都是由一个名称为

7、 get_op_xxx 形式的函数来完成的,这个函数会将微指令的地址返回,用 来设置在 op 中,而其的参数指针是用来设置 len 的,这些函数都会在微指令初始化函数中 被调用。在这个函数中有 2 个定义在 arm2x86.h 中的宏 OP_BEGIN 和 OP_END ,这 2 个宏都是 X86 指令。在这 2 个宏之间的代码就是微指令的代码。#define OP_BEGIN (f)_asm_ _volatile_(jmp .f_teawater_op_endnt.f_teawater_op_begin:nt)#define OP_END (f)_asm_ _volatile_ (.f_te

8、awater_op_end:ntmovl$.f_teawater_op_begin,%0ntmovl $.f_teawater_op_end,%1nt:=g(begin), =g(end); OP_BEGIN 先是一条跳转指令, 跳转到声明在 OP_END 中的符号地址 .f_teawater_op_end , 这句的作用是跳过 OP_BEGIN 和 OP_END 中间的代码, 防止他们被执行, 我为什么没有直 接使用普通的跳转语句 goto 呢?因为如果使用了 goto 语句,编译器就会知道微指令的代码 没被执行, 就会将这部分代码优化掉, 而 使用跳转指令编译器无法判断汇编语句的行为 ,所

9、 以不会优化掉 2 个宏之间的微指令代码。 然后是一条声明符号的伪指令, 这个符号指向的地 址就是微指令开始的地址。OP_END 先是一条声明符号的伪指令,这个符号的地址就是指向微指令结束的地址,OP_BEGIN 中的跳转指令也是跳到了这个地址。然后跟着是两条赋值指令,将 微指令开始 和结束的地址存到初始化函数开始声明的begin 和 end 变量中, 这样在函数中就取得微指令的开始和结束地址。从上面对 2 个宏的的介绍就可以基本清楚微指令的生成过程, 在取得了微指令的开始和结束 地址后,就可以通过计算取得其的长度最后返回。在 QEMU 的微指令的生成过程跟 DBCT 不同, QEMU 采用的

10、方式是每个微指令都在一个函 数中,编译完成后,通过特定的程序将微指令的开始地址和长度取出。3.2. 微指令中的变量在微指令的代码中,都没有使用动态局部变量,也就是栈中的地址,而直接使用了寄存器, 当然在寄存器不够的情况下也使用了全局变量 。这个使用的方法是参考的 QEMU 中微指令 对变量的使用方法。个人认为这样做因为若干条微指令才能组成一条指令(尤其是在 ARM 这种单条指令实现若干功能的体系结构更是如此 ),这些微指令之间要相互传递计算等得到的值,如果通过栈传 递也可以,但是实现相对复杂,而且频繁的内存操作速度也会受到影响。对这些 寄存器的声明在 arm2x86_self.h 这个单独的头

11、文件中, 因为对寄存器的声明有时候会 影响代码编译,所以只在 DBCT 相关的文件中包含了这个头文件。ebp寄存器声明为 ARMul_State结构的指针st,其将在微指令的代码开始之前指向state也就是存储了 SKYEYE 模拟 CPU 所有信息的结构 ,这样微指令就可以方便的访问这个结构。ebx、esi和edi声明为uint32_t类型的变量T0,T1和T2,这几个变量在微指令中可以灵活使用。 注意因为这几个寄存器保存了栈指针等信息, 所以在运行微指令以前需要进行保存, 具体在 介绍 DBCT 运行的时候会进行介绍。其他eax等寄存器因为是在 GCC中是局部寄存器,不能作为全局变量声明,

12、所以不能作为 微指令中的变量使用。3.3. 微指令中的函数调用在微指令中有时候需要对函数进行调用, 因为微指令使用方法的原因, 这里不能进行普通的 函数调用,而需要特殊处理。下面就以 arm2x86.c:get_op_begin 中对 tea_begin 函数的调用为例子进行介绍。这里首先是对esp减Oxc也就是在栈中分配 Oxc的空间,然后将ebp也就是指向ARMul_State结构的指针push到栈中,这2条汇编指令是将 st作为参数传递给被调用函数tea_begin,前面在栈中分配 0xc 的空间是要保证传递参数的 0x10 对齐, 0xc 加 32位的长度就是 0x10。 没有存储使用

13、的ebp、ebx、esi和edi,因为这几个全局寄存器的值是由被调用函数进行保存 的,如果调用函数修改了这几个寄存器的值就进行保存, 在函数返回时恢复, 如果没有就使 用就不进行保存和恢复。然后是将tea_begin的地址赋值给T2,然后再对其进行调用,这样做的目的是进行直接地址 调用。因为一般的函数调用都是直接地址调用, 所以调用跟当前这个调用指令的地址是相关 的,但是微指令会被拷贝到 TB 上的某地址执行,调用指令的地址发生了变化,所以如果进 行相对地址调用, 肯定要产生错误, 所以这里都使用直接地址调用。 在某些函数中为了适应 CYGWIN 使用函数指针进行调用也是出于同样的目的。最后是

14、取得返回值,一般一个32位数的返回值都是放在寄存器eax中,如果有需要就将其存入 T0 等寄存器变量然后使用。3.4. 微指令中的异常处理模拟指令一般来说都模拟异常处理, DBCT 也不例外。DBCT 的做法是当有异常处理的时候,设置 st-trap 或者 state-trap 为异常的类型 (定义在 arm2x86.h,TRAP_XXX 的都是),然后根据情况调用 X86汇编指令ret返回到正常模式,然 后在非 DBCT 运行模式中进行实际的处理。因为这种处理减少了微指令中实现的难度,所以类似 TRAP_SETS_R15 、 TRAP_SET_CPSR 以及 TRAP_SET_R15 等几个

15、非异常处理 也采用了同一种处理方式。3.5. 微指令中的跳转这里的跳转不是指模拟的被模拟指令的跳转, 而是指微指令根据需要跳转指定的长度。 比如 某条被模拟指令有 condition判断,其中就会需要这种跳转,当condition和PSR中的值不符合的时候, 就需要跳过当前指令被翻译成的微指令代码, 这个时候就需要跳转过指令的长 度。在 DBCT 中的做法是先写一条类似_asm_ _volatile_ (jmp 0xffffffff) ;的指令, 一般来说后 4 个字节就是跳转长度,在翻译指令的时候将要跳转的长度写入就可以。在后面介绍 指令翻译过程 的时候,还会对微指令跳转的使用再作详细介绍。

16、3.6. 微指令分类介绍3.6.1. 概述因为规划设计的问题, 微指令的分类有点乱, 所以就以初始化函数为分类基础进行分类介绍。3.6.2. arm2x86.c op_init这里初始化的最重要的 2 个微指令是 op_begin 和 op_begin_test_T0 ,这 2 个指令都是在翻译ARM 指令的时候放在每条指令最开始的部分的。op_begin 是被翻译的 ARM 指令的 condition 是 AL 或者 NV 也就是不进行条件判断时候使用的微指令。这里先调用了函数arm2x86.c:tea_begin ,而这个函数调用了arm2x86.c: tea_check_out,这个函数

17、类似普通指令执行模式中指令开始执行时候作的操作一 样, 检查是否需要单步返回,检查是否有硬件中断发生进行异常处理,检查当前 TB 是否已 经标记为脏 (如果当前 TB 中执行的微指令写了当前 TB 相关的内存,就会有这样的情况发 生,这时返回到普通模式重新执行,就会自动对这个 TB 进行重新翻译 ),最后一步执行 armio.c:io_do_cycle 调用全部虚拟设备执行。 tea_begin 函数返回以后会判断返回值,如果为 真就返回。op_begin_test_T0 是被翻译的 ARM 指令需要条件判断时候使用的微指令 。在这条指令运行 以前, 被翻译指令的 condition 已经被存

18、到 T0 中 ,这里首先以 st 和 T0 为参数调用 arm2x86.c:tea_begin_test,这个函数也是调用 tea_check_out检查是否有异常等需要从 DBCT 模式返回普通执行模式, 如果没有则调用 arm2x86_psr.h:gen_op_condition 判断当前被翻译指 令是否可以执行。tea_begin_test返回后,先判断是否有异常需要返回普通执行模式;然后判断是否这条指令是否因为 condition 而不被执行,如果是则就执行一条 jmp 指令,也就是 前面介绍过的微指令跳转。其他几个微指令比较简单不作详细介绍。3.6.3. arm2x86_test.c

19、:arm2x86_test_init这里初始化了几个简单的测试情况然后进行一些处理的微指令。3.6.4. arm2x86_shift.c:arm2x86_shift_init这里初始化的是各种 移位微指令 。 这里移位的长度如果是变量的处理起来比较简单,前面在别的微指令中存到某个寄存器变 量,然后在这条微指令中直接操作就可以。如果移位的长度是立即数,则处理办法有点类似前面的微指令跳转。先用一条类似“T1 = T1<< 31; 的语”句,这样其最后一个值是一个 8位的移位长度 ,然后在实际翻译的过程中 替换成实际翻译的立即数就可以了。3.6.5. arm2x86_psr.c:arm2

20、x86_psr_init这里初始化的是跟 ARM的状态寄存器PSR(包括CPSR和SPSR)相关的微指令。3.6.6. arm2x86_movl.c:arm2x86_movl_init这里 初始化是用来对某模拟寄存器或者某寄存器变量等赋值的微指令。其中的立即数赋值也比较类似前面的微指令跳转 ,先用类似 “T2 = ULONG_MAX”; 的语句, 这样其最后一个值就是 32 位长度的立即数 ULONG_MAX ,然后在实际翻译的过程中替换成实际翻译的立即数就可以了。3.6.7. arm2x86_mul.c:arm2x86_mul_init这里 初始化是用来对乘法指令进行模拟的微指令 。3.6.

21、8. arm2x86_mem.c:arm2x86_mem_init这里 初始化是用来对内存操作进行模拟的微指令 。这里对内存的操作采用的办法是调用 SKYEYE 原有的内存操作函数,直接取得返回值,然 后进行各种操作。这里这么做而不是直接访问相应的内存地址,因为有 MMU 的时候,需 要先通过 MMU 中的 TLB 和页表等转换地址, 而且还有地址是 IO 地址, 所以直接调用内存 操作函数是比较简单的实现方法。3.6.9. arm2x86_dp.c:arm2x86_dp_init这里初始化的是对 ARM 中 DP 指令 进行模拟的微指令。3.6.10. arm2x86_coproc.c:ar

22、m2x86_coproc_init这里初始化的是对 ARM 中 协处理器指令 进行模拟的微指令。3.6.11. arm2x86_other.c:arm2x86_other_init这里对所有 其他微指令 进行初始化。4. 翻译块(TB)4.1.概述在 DBCT 中,将 tb.h:TB_LEN 长度的被模拟指令翻译成一系列微指令 (这一系列 微指令的长 度最大值为 tb.h:TB_INSN_LEN_MAX ,由 tb.c:tb_insn_len_max_init 取得 ) ,这一系列微指令(存储这些微指令的内存称为 TBP)以及地址信息等其他信息封装在一起称为一个TB。在DBCT 初始化的时候,

23、 会根据配置文件以及实际情况对 TB 进行初始化, 介绍 tb_memory_init 的时候会详细进行介绍。4.2. tb.h:struct tb_s这个结构是 TB 的核心结构,每个 TB 块都将对应一个 tb_s 结构。 下面对其的每个成员变量进行介绍:struct list_headlist;当使用第二种方法使用 TB 的时候,这个 list 将所有使用过的 TB 全部用 tb.c:tbp_dynamic_list 以及这个 list 结构组成的链表连接起来,在每次使用某个 TB 的时候,都将其先从链表中删 除,然后连接到链表的最后。当对某地址进行执行 ,而所有 TB 都不是这个地址相

24、关的,并 且没有未使用过的 TB ,需要从现有 TB 中选择一个 TB 使用的时候,就会 使用链表第一个 TB 。这样做的好处是,最不常用的已翻译过的 TB 肯定是在链表第一个,选择其影响会最 小,提高了效率。在后面介绍翻译执行的时候会进行更具体的介绍。int ted;这个变量为 0 表明这个 TB 中数据是没有翻译过的,为 1 表明其中数据是翻译过的。在需要 标记某个TB为脏的时候,就可以通过设置ted为0来实现。uint8_t*insn_addrTB_LEN / sizeof(uint8_t *);在翻译过程中, 将把 每条指令对应的微指令地址 都存储到这个数组中, 这样在执行已经翻译 过

25、的 TB 的时候,可以直接取得对应地址的微指令地址开始执行。原来的 DBCT 中也采用过不存储微指令地址,在执行的时候再重新翻译取得地址的方法, 最多是将取得后的地址存储起来, 后来考虑即使将所有地址都存起来也不 会使用很多内存, 所以就用了所有翻译过的地址都存起来的方法。uint8_t*tbp;这个成员变量指向 当前 TB 存储微指令的内存 ,显然这里指向内存块的大小为 TB_LEN / sizeof (ARMword) * TB_INSN_LEN_MAX 。ARMwordaddr;这个成员变量是 当前 TB 对应的被模拟指令的地址 。ARMwordtran_addr;在 TB 翻译过程中,

26、并不是一次将整个 TB 范围内的被模拟指令都翻译成微指令,而是每次 翻译到一个 必定发生返回的指令 ,并且实际要取得的微指令地址(注意翻 译是从 TB 开始的地址开始的 ) 也已经取得, 就不再继续进行翻译, 等下次请求一个地址比翻译到的地址大的 时候,就继续对 TB 进行翻译。这个成员变量 tran_addr 记录的就是翻译到的指令地址的下一个指令的地址,也就是如果继 续翻译的地址。uint8_t *tbp_now;该成员变量指向当前可以写入微指令的地址, 在 tbt-ted 为 0 也就是这个 TB 从 tbt-addr 开 始翻译的时候初始化 为 tbt-tbp ,每增加一个微指令都顺序

27、增加,并且在向上面 tran_addr 提到的那种继续翻译的时候,可以继续使用。ARMword last_addr;uint8_t *last_tbp;这2个成员在指令翻译的时候使用。last_addr存储这个TB上次被使用时候的地址,而last_tbp 就是对应的微指令地址。这样如果下次还使用这个TB的这个地址last_addr,就可以快速取得微指令地址last_tbp,提高执行速度。ARMwordret_addr;介绍 tran_addr 的时候,已经提到了只翻译到必定发生返回的指令就不再继续翻译,但是这 里还有一种情况需要考虑到,就是 DBCT 在翻译当前 TB 范围内的 被模拟指令跳转

28、的时候 , 都是将这个跳转指令翻译为微指令跳转指令 ,而不是通常的设置 PC 寄存器然后返回的 方 式,这样的跳转如果是向后跳转,并且超过了翻译结束的地址肯定是不行的。如何防止提前翻译结束?在翻译开始的时候设置ret_addr为0,一旦有TB内跳转出现,并且这个地址的值比ret_addr大,就设置ret_addr为这个地址。在翻译完一条指令并确定翻译 也许可以结束的时候,对ret_addr进行检查,只有在 ret_addr小于下一条将翻译的指令地址的时候,翻译才结束。4.3. TB_TBT_SIZE 和 TB_TBP_SIZE在 tb.c 中有 TB_TBT_SIZE 和 TB_TBP_SIZ

29、E , TB 的初始化就要根据其的值来进行,他们的 定义为:#define TB_TBT_SIZEskyeye_config.tb_tbt_size#define TB_TBP_SIZEskyeye_config.tb_tbp_sizeTB_TBT_SIZE是DBCT中所有TB的条目也就是tb_t结构所占空间。如果设置为0则就在 使用的时候分配在被模拟内存结构 armmem.h:mem_state_t-tbt 上,也就是只要运行某块内 存,就分配其的 tb_t 结构。 如果设置为非 0 就使用 tb.c:tbt_table 和 tb.c:tbt_table_size 的内存 进行动态分配。TB

30、_TBP_SIZE 是 TB 中实际储存微指令的内存所占空间 。如果设置为 0则就在使用的时候 分配在被模拟内存结构 armmem.h:mem_state_t-tbp 上,也就是只要运行某块内存,就分配 其的tbp。如果设置为非 0会同时标记tbp_dynamic为1表明是TBP动态分配,并且 TBP 所用的内存使用 tb.c:tbp_begin、tb.c:tbp_now 和 tbp_now_size 进行动态分配。skyeye_config.tb_tbt_size 和 skyeye_config.tb_tbp_size 是从配置文件中读出的值, 他们的初始 化也就是 设置默认值在 skyey

31、e_options.c:skyeye_option_init 函数中。在这里我们可以看到 config-tb_tbt_size 也就是 TB_TBT_SIZE 初始化为 0,因为 tb_t 结构占用空间不 大; config-tb_tbp_size 也就是 TB_TBP_SIZE 初始化为 TB_TBP_DEFAULT(1024 * 1024 * 64),这里没有也初始化因为每条被模拟的 ARM 指令都包含若干条微指令,这样跟 一个 TB 存储 的指令对应的微指令存储空间会比较大 ,甚至有超过 32 位寻址 空间大小的情况, 所以这里 一般不设置为 0。注意 TB_TBT_SIZE 和 TB_

32、TBP_SIZE 并不是从配置文件中读出后直接使用,而是在 tb_memory_init 进行过初始化后才使用,和其 相关的几个变量也是在 tb_memory_init 中进行的初始化。4.4. tb.c:tb_memory_init这个函数用来对 DBCT 中的 TB 进行初始化。下面介绍执行过程: 第一步,先判断 TB_TBT_SIZE 是否为 0,如果不为 0,就会执行一部分针对 TB_TBT_SIZE 的代码。注意这里我犯了一个比较大的错误,其中的结构应该使用tb_t,而我这里全部错误的使用了 tb_cache_t,而这个tb_cache_t也是一个不再需要的东西,早应该从代码中去掉,

33、下面的 介绍全都假定成tb_cache_t已经被换成了 tb_t。先对TB_TBT_SIZE进行基本的处理 和检查,然后取得所有被模拟内存一共需要 tb_t 所 占的空间,其跟 TB_TBT_SIZE 进行比 较。如果TB_TBT_SIZE大于等于这个值,则表明 DBCT不需要动态分配tb_t来节省空间, 就设置 TB_TBT_SIZE 为 0,使用固定分配的形势。如果 TB_TBT_SIZE 小于这个值,则初 始化存储 tb_t 的空间 tbt_table 和 tb_t 的数 量 tbt_table_size 。这样 TB_TBT_SIZE 就初始化完成,同时还对 tbt_table 和 t

34、bt_table_size 进行了设置。第二步,如果 TB_TBP_SIZE 为非 0,则对其进行基本的处理和检查。第三步,再次判断 TB_TBT_SIZE 是否为 0,然后对 TB_TBP_SIZE 进行处理。如果TB_TBT_SIZE不为0,首先取得这个长度的 tb_t结构组需要的tbp的长度tmp_u64, 跟 TB_TBP_SIZE 进行比较。如果 TB_TBP_SIZE 大于 tmp_u64 或者 TB_TBP_SIZE 为 0, 则 TB_TBP_SIZE 设置为这个值,这么作因为在 TB_TBT_SIZE 动态分配后, TB 无法对大 于其管理范围的微指令内存 TBP 进行管理,

35、所以进行这个设置。如果 TB_TBP_SIZE 小于 tmp_u64,则设置 tb.c:tbp_dynamic为1,也就是设置 DBCT中TBP为动态分配。如果 TB_TBT_SIZE 为 0,将判断 TB_TBP_SIZE 是否为 0。如果为 0 很显然不需要再作任何 初始化工作, tbp_dynamic 使用默认值 0,全部在 DBCT 运行时根据需要分配在 mem_state_t-tbt 和 mem_state_t-tbp 上就可以。如 果不为 0 则先取得全部被模拟内存需要 的TBP的长度tmp_u64,然后跟 TB_TBP_SIZE进行比较。如果 TB_TBP_SIZE大于等于 tm

36、p_u64,则表明TBP已经不需要动态分配,就设置 TB_TBP_SIZE为0。如果TB_TBP_SIZE 小于tmp_u64,则表明需要动态分配,设置tbp_dynamic为1。这样 TB_TBP_SIZE 就初始化完成,同时也根据需要对 tbp_dynamic 进行了设置。第四步,这时 TB_TBP_SIZE 的值已经得到了确定,这里就是给 tbp_begin 分配空间,注意 这里用 mmap 分配内存的时候设置了权限为可运行 PROT_EXEC 。然后对 tbp_now_size 和 tbp_now 也进行了初始化。这样用来进行 TBP 动态分配的 tbp_begin、tbp_now_s

37、ize 和 tbp_now 进行了初始化。总结一下, DBCT 中用来维护 TBT 和 TBP 动态分配的几个变量用的有点繁琐了。4.5. tb.c:tb_insn_len_max_init 这个函数用来对 tb.c:tb_insn_len_max 也就是 TB_INSN_LEN_MAX 进行了初始化。 做法是将所有被翻译指令被翻译成的微指令长度都取得,然后进行比较,将最长的设置为 tb_insn_len_max 。5. 初始化函数 arm2x86.c:arm2x86_init这个函数是 DBCT 的初始化函数,其在函数 arminit.c:ARMul_Reset 中被调用。 这个函数先会调用

38、前面介绍过的几个微指令初始化函数,然后是函数 tb_insn_len_max_init , 最后是函数 tb_memory_init 。6. 翻译执行过程6.1.armemu.c:ARMul_Emulate32_dbct这是 整个 DBCT 翻译执行的核心函数 ,类似普通指令执行方式的 ARMul_Emulate32 函数, 也是在 arminit.c:ARMul_DoProg 和 arminit.c:ARMul_DoInstr 被调用。下面介绍执行过程: 第一步,给 R15 寄存器也就是 PC 寄存器的值增加一个指令长度 INSN_SIZE ,这是因为 ARM 的多级流水线PC寄存器对应用是

39、非透明的,而在这个函数外面的函数都将R15当作当前PC值,所以在开始执行前先对R15寄存器进行设置。第二步,设置 state-trap 为 0。第三步,调用函数tb.c:tb_find,在这个函数中 根据参数提供的PC寄存器值,进行全部的分 配TB以及指令翻译的工作,最后将跟PC对应的微指令 地址返回。如果返回NULL则表示执行失败,设置 state-trap为TRAP_INSN_ABORT 也就是取指异常,跳转到后面对 state-trap 进行处理的部分。第四步, 对将在微指令中作为变量的寄存器进行保存 ,保存的原因前面介绍过, 因为这几个 寄存器的值都是被调用函数来保存,所以在这里进行保

40、存。调用取得的指向微指令内存的指针gen_func。返回后恢复几个寄存器的值。第五步,在介绍微指令的时候, 介绍过异常等特殊情况, 都是先设置state-trap然后就返回, 而这里就是实际对 异常等进行处理 的地 方。这部分代码比较清晰,就是根据 state-trap 进 行不同的处理,不作详细介绍。第六步,判断是否还继续执行 ,或者函数返回。如果继续执行就返回到第二步。第七步,state-Reg15减INSN_SIZE,恢复PC指向当前程序执行的地址,然后返回。6.2. tb.c:tb_find在这个函数中 根据参数提供的 PC 寄存器值,进行全部的分配 TB 以及指令翻译的工作 , 最

41、后将跟 PC 对应的微指令地址返回 。下面介绍执行过程:第一步,调用 armmmu.c:mmu_v2p_dbct 函数通过 SKYEYE 的 MMU 功能 取得跟执行地址 ADDR对应的被模拟物理地址addr,如果失败则函数出错返回。然后通过TB_ALIGN取得跟TB_LEN长度对齐的地址 align_addr,这个地址就是 addr对应TB的地址。第二步, 检查 align_addr 是否和 静态局部变量 save_align_addr 相同, 如果相同表明前面已经 对这个物理地址的 TB 进行过请求,已经取 得了翻译前需要的各种指针,都存在静态局部 变量中, 所以跳过分配 TB 的代码直接

42、执行指令翻译的代码。 注意 save_align_addr 的初始值 为 0x1 是为了保证不跟任何地址一样。第三步,这里开始的就是对 TB 进行分配的代码, 首先判断 tbt_table_size 是否为 0 来确定 tb_t 是否是动态分配的。第四步,如果是动态分配,就会以哈希计算的方法从 tbt_table 中取出跟 align_addr 对应地址 的 tb_t。比较tbt-addr和align_addr,如果tbt-addr跟align_addr不同表明其先前是其他地址的TB,就会进行一些清除过去记录的工作,设置 tbt-ted 为 0,设置 tbt-addr 为 align_addr

43、。然后就是取得tbt-tbp也就是TBP。如果tbt-tbp为NULL,则表明这个 TB中的TBP没有 分配或者已经被 其他 TB 使用,这时候需要调用 tb.c:tb_get_tbp 进行 TBP 的分配。如果 tbt-tbp 不为 NULL ,则 TBP 已经分配过, 则按照前面在介绍 tbt-list 那样,先将其从 tbp_dynamic_list 链表中删除掉。第五步, 如果不是动态分配, 首先通过函数 tb.c:tb_get_mbp 取得 align_addr 对应模拟内存的 mem_bank_t 结构指针 mbp。检查结构中的 state-mem.tbtbank_num 是否为空

44、,如果为空表明 tbt 和 tbp 未分配相应的空 间,如果 tbp_dynamic为0表明是静态分配 TBP,则将先给 state-mem.tbpbank_num分配 空间,然后给 state-mem.tbtbank_num 分配空间。分配好空间后设置 TB 结构。在取得 TB 结构后检查 tbt-tbp 也就是 TBP 是否为空。如果为空就根据 tbp_dynamic 对其进 行设置,动态分配跟前面一样使用 tb.c:tb_get_tbp 函数,静态从 state-mem.tbpbank_num 中取得。如果不为空也跟前面一样判断 tbp_dynamic 根据情况将 TB 结构从列表中删除

45、。现在, TB 结构和其中的 TBP 都已经取得。第六步,用取得的 TB 进行一些设置。设置 state-tb_now 为刚取得的 TB 结构,其的作用是微指令在运行的时候可以访问当前运行 的TB,比如在标记 TB为脏之后,微指令可以马上判断出来然后退出。设置为 save_align_addr为 align_addr,目 的在第二步介绍过。如果 tbp_dynamic 为真表明是动态 TBP 分配,将 TB 结构增加到 tbp_dynamic_list 链表的最后面,这么作的目的在介绍 tbt-list 已经介绍过。第七步,现在开始的就是对被模拟指令进行翻译的代码。 先判断 tbt-ted 的

46、值来确定这个 TB 结构是否被翻译过。第八步,如果这个 TB 结构 已经翻译过 。先检查 tbt-last_addr 是否跟 addr 相同,如果相同就返回tbt-last_tbp 。这里在前面介绍tbt-last_addr 和 tbt-last_tbp 的已经介绍过了。判断需要翻译的物理地址 addr 是否大于等于 tbt-tran_addr ,这个 tbt-tran_addr 在前面也介 绍 过。如果 addr 小于 tbt-tran_addr 则表明 TB 中现有微指令代码已经可以满足 addr 的需要,直接 从 tbt-insn_addr 取出跟 addr 对应的 TBP 地址作为返回

47、值设置到 ret 就可以。如果 addr 大于等于 tbt-tran_addr 则表明需要继续翻译, 首先取得跟 tbt-tran_addr 地址对应 的 在被模拟内存块中指针real_begin_addr ,以及和 addr 对应的在被模拟内存块中指针real_addr。然后就调用tb.c:tb_translate从给定的real_begin_addr开始的内存进行翻译。最后 取得跟 addr 对应的微指令地址设置到 ret。第九步,如果这个 TB 结构 还没有翻译过 ,就需要重新翻译。也是首先取得跟tbt-tran_addr地址对应的在被模拟内存块中指针real_begin_addr,以及

48、和addr对应的在被模拟内存块中指针 real_addr。然后初始化tbt-tran_addr为align_addr,初始化tbt-tbp_now为tbp,这两个成员变量在前面介绍过,这里就不再介绍。调用tb.c:tb_translate从给定的real_begin_addr开始的内存进行翻译。最后取得跟addr对应的微指令地址设置到ret。并且设置tbt-ted为1表明 这个TB已经被翻译过。现在返回值ret,也就是跟ADDR对应的微指令地址已经取得。第十步,将 addr 和 ret 都设置到 tbt-last_addr 和 tbt-last_tbp 上,将 ret 返回。6.3. tb.c: tb_get_tbp这个函数用来对 TBP 进行动态分配。下面介绍执行过程:第一步,判断 tbp_now

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论