Chinaunix首页 | 论坛 | 博客
  • 博客访问: 347958
  • 博文数量: 105
  • 博客积分: 2730
  • 博客等级: 少校
  • 技术积分: 1110
  • 用 户 组: 普通用户
  • 注册时间: 2007-04-20 12:09
文章分类

全部博文(105)

文章存档

2013年(3)

2012年(2)

2011年(36)

2010年(34)

2009年(6)

2008年(20)

2007年(4)

分类: C/C++

2010-09-30 20:02:02

上手来看看mipsel-linux-user的运行过程,此文分几次完成.


环境ubuntu10.04, eclipse-CDT, mipsel-linux-gcc


还是要提一下,编译交叉编译器的时候, 可以使用buildroot系统,它把gccuclibc都编译好了,放在相同的prefix路径下, 而且需要注意-with-sysroot参数是如何让编译好的gcc自动链接uclibc.

mipsel-linux-gcc 直接编译helloworld即可,生成了mipsELF文件.


eclipse CDT非常好用,我用它来调试qemu. disassembly窗口可以指定一个地址看反汇编,虽然对mips指令无意义,但是可以观看TCG的翻译结果.


Configure qemu:


./configure --target-list=mipsel-linux-user --enable-debug-tcg --enable-debug --disable-kvm --disable-xen –interp-prefix=/opt/a/cross-mipsel –prefix=opt/a/qemu-mipsel

其中interp-prefix指向了目标c库的root目录,对于cross工具一套来说,和其prefix是同一个路径.

编译完之后,就可以用eclipse进行调试了.


qemu的参数设置为helloworld elf文件,然后启动调试.


执行顺序:

main函数入口在linux-user/main.c

首先要做的事就是设置各种环境变量.linux app拥有和host相同的环境变量,然后开始解析各种命令行选项,这些省略不说.

第一个调用的关键函数是cpu_exec_init_all(), 初始化host这边的运行时环境.

  • cpu_gen_init()

    • tcg_context_init() // 这一部分初始化TCG, 为各数据结构分配内存,然后生成一组指令序列用于频繁的TCG进入与离开的环境切换,这也是最早被生成的TCG代码部分. 为什么要生成这部分?因为保存上下文的哪些寄存器是能因为ABI的不同而异, 而且使用一段编译时的代码段可能操作上不如TCG直观吧.

      • tcg_target_init()

      • tcg_target_qemu_prologue() //生成切换上下文代码

  • code_gen_alloc()

    • map_exec() //把刚才Gen出来的代码块所在的页变成RWX属性!(缓冲区:code_gen_prologue)

  • page_init() //扫描/proc/self/maps获得当前qemu进程的地址映射, 对每一个页,调用::

    • page_set_flags() //首先检查当前页是否可写,如果可写则标记”原来可写”,另外,还要调用::

      • page_find_alloc函数检查时候是一个被映射到target的内存的页,如果当前页被映射到target的目标内存中,那么这块内存就是target的内存了.如果target内存有该动,就需要检查是不是自修改代码了,后话以后再说.init阶段调用这个函数不是为了这个目的.因为现在target内存是完全空的.

  • Return;

然后执行cpu_init(), 初始化target的一些配置

  • 确定CPU model, 比如是24k还是34k之类的

  • 构造CPUMIPSState结构(env),它是target的根.

  • cpu_exec_init() 初始化一些数据结构

  • fpu_init() //浮点结构和寄存器等等

  • mvp_init() //MT-ASE, See Mips32 ISA vol.3 : 9.1 CP0

  • mips_tcg_init() //初始化TCG的所有寄存器映像,分配空间,调用tcg_register_helper函数定义很多指令的helper(都是宏,而且在编译时生成,获取比较麻烦).

  • Return;

cpu_init之后,得到了唯一的,也是重要的产物:env,它描述了全部CPU的运行状态.

/proc/sys/vm/mmap_min_addr获得mmap允许映射的最低地址空间,我这里得到了65536.

然后把余下的命令行参数都传给target,当然是为了给target一个完整的userspace的感觉.

然后初始化TaskState数据结构.TaskStatelinux-user/qemu.h里定义, 主要记录了一些与user process有关的内容,比如pid, 打开的文件映像, 环境变量, signal状态和队列, 以及一个栈. 这个数据结构保存的都独立于CPU和存储的,可以理解为硬件无关/进程相关的信息集合.

初始化结束后, envopaque指针就指向TaskState.

此时,硬件和软件的环境都初始化完成了.

下一步就是loader_exec() // 函数所在的linux-user/linuxload.c文件就是从kernel里移植来的.

  • prepare_binprm() // 检查elf文件,是否可以被load, 如检查x位权限

  • 检查elf文件头,并且load_elf_binary() //linux too


target_set_brk() //


下面的部分待续.

syscall_init();

这一段就是按照linux-user/syscall_types.h 的定义来初始化syscall

signal_init();






阅读(2278) | 评论(0) | 转发(0) |
0

上一篇:qemu target-list

下一篇:C如何应用异常机制

给主人留下些什么吧!~~