随想录(虚拟机实现)
Posted 费晓行
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了随想录(虚拟机实现)相关的知识,希望对你有一定的参考价值。
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
要在一个cpu上实现对另外一个cpu的仿真,虚拟机是重要的方式之一。比如我们使用的cpu一般是x86或者amd64,这个时候如果需要学习arm、mips、powerpc、openrisc,或者是riscv等其他cpu,虚拟机就是重要的方式。当然,如果你野心更大一点,设计自己的cpu,这个时候也需要设计一个虚拟机。看上去设计cpu是一个硬件开发的活,但是大部分的工作其实都和软件有关,如果保证你设计的cpu硬件没有问题,这个时候虚拟机验证就是重要的一个环节。
一般完成一个虚拟机需要哪几个步骤,
1、读取bin文件,当然你也可以支持elf文件
2、支持指令的解析,包括特殊寄存器的访问
3、支持immu和dmmu,支持icache、dcache,此部分可选
5、支持memory空间的解析
6、支持外设io空间的解析
7、中断的处理
有了上面这几个部分,基本上就可以把其他cpu的程序运行起来了。甚至于说uboot、kernel、busybox编译好的系统,也是可以拿来运行的。大家要对这一点有信心。我们找一个开源的虚拟机实现来说明这一点,项目地址在这https://github.com/cassuto/nano-cpu32k-emulator,一起来看看它是怎么实现的。
直接看项目的makefile文件,
CC = gcc
CFLAGS = -std=gnu99 -g -O2 -Wall -Wno-unused-function $(INCS) $(DEFS)
LDFLAGS = -g
OBJS = main-loop.o \\
exec.o \\
memory-mmio.o \\
debug.o \\
msr.o \\
i-mmu.o \\
d-mmu.o \\
tsc.o \\
irqc.o \\
parse-symtable.o \\
trace-runtime-stack.o \\
device-tree.o \\
device-ata.o
DEPS := $(OBJS:.o=.d)
.PHONY:all clean distclean
all: emu
clean:
-rm $(OBJS)
distclean:
-rm $(OBJS) $(DEPS)
emu: $(OBJS)
$(CC) $(LDFLAGS) $^ -o $@
%.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
%.d: %.c
$(CC) $(CFLAGS) -MM -MF $@ $<
-@sed -i 's,\\($(notdir $*)\\)\\.o[ :]*,$(basename $@).o: ,g' '$@'
$(OBJS): $(DEPS)
-include $(DEPS)
makefile看上去也没有几行,我们一步一步来,首先找到文件入口,也就是main-loop.c,main函数就在里面。memory_init、memory_load_address_fp、devicetree_init、cpu_exec_init初始化之后,最重要的工作就是调用cpu_exec,它位于文件exec.c。
exec是一个循环函数,它的作用就是读指令,执行指令。当然,在这个过程中,有几个部分需要处理一下,一个是判断一下当前是否有时间中断和其他异常,一个是看一下指令地址是否需要翻译。等到这两个都处理好了之后,就可以处理指令了。
case INS32_OP_AND:
cpu_set_reg(rd, cpu_get_reg(rs1) & cpu_get_reg(rs2));
break;
case INS32_OP_AND_I:
cpu_set_reg(rd, cpu_get_reg(rs1) & uimm14);
break;
case INS32_OP_OR:
cpu_set_reg(rd, cpu_get_reg(rs1) | cpu_get_reg(rs2));
break;
case INS32_OP_OR_I:
cpu_set_reg(rd, cpu_get_reg(rs1) | uimm14);
break;
其他比较重要的部分i-mmu.c,处理指令地址翻译;d-mmu.c,处理数据地址翻译;irq.c,中断处理;device-tree.c & device-ata.c,设备空间地址访问;memory-mmio.c,内存空间地址访问;msr.c,特殊寄存器读写;tsc.c,是否生成时间中断。
坦率地说,支持地外设不算多,不过那是个体力活,就看自己适配多少功能了。有兴趣地同学可以看看这个代码,挺有意思的。有些软件的原理其实不复杂,不要神话linux kernel、gcc这些软件,都是可以为我们所用的,永远记住,他们都是我们使用的一个工具和手段而已。
/**
* @brief Raise an exception to handle for CPU.
* NOTE! you must goto handle_exception after raised an exception
* @param [in] vector Target exception vector.
* @param [in] lsa Target LSA virtual address.
* @param [in] syscall Indicates if it is a syscall.
*/
void
cpu_raise_exception(vm_addr_t vector, vm_addr_t lsa, char syscall)
{
msr.EPC = cpu_pc + (syscall ? INSN_LEN : 0);
msr.ELSA = lsa;
/* save old PSR */
msr.EPSR = msr.PSR;
/* set up new PSR for exception */
msr.PSR.RM = 1;
msr.PSR.IMME = 0;
msr.PSR.DMME = 0;
msr.PSR.IRE = 0;
/* transfer to exception handler */
/* -INSN_LEN will be eliminated by insn fetch in cpu_exec() */
cpu_pc = (vm_signed_addr_t)vector -INSN_LEN;
#ifdef TRACE_EXCEPTION
verbose_print_1("Raise Exception %X EPC=%x\\n", vector, msr.EPC);
getchar();
#endif
}
最后想强调一点异常的处理。结合计算机原理,我们看一下cpu遇到异常要实现哪些功能,现场保存哪些数据,跳转到哪些地址,pc怎么修改,这些其实都值得好好看看的。
以上是关于随想录(虚拟机实现)的主要内容,如果未能解决你的问题,请参考以下文章
Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | DexClassLoader 构造函数 | 参考 Dalvik 的 DexClassLoader 类加载流程 )(代码片段
Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | DexClassLoader 构造函数 | 参考 Dalvik 的 DexClassLoader 类加载流程 )(代码片段