GeekOS 中的project1
来源:互联网 发布:js this.classname 编辑:程序博客网 时间:2024/05/18 12:42
project2要求解析一个elf文件并执行。
解析elf很简单,只要读取出elf文件中的程序头即Program Headers。
需要知道:
1. 总共有几个Program Headers。
2. 每个Program Headers在文件中和在内存中的起始地址和界限。
3. 程序入口地址。
project1的代码中已经给我们定义好了elf head 和program head 的数据结构了。
/* * ELF header at the beginning of the executable. */typedef struct { unsigned char ident[16]; unsigned short type; unsigned short machine; unsigned int version; unsigned int entry; unsigned int phoff; unsigned int sphoff; unsigned int flags; unsigned short ehsize; unsigned short phentsize; unsigned short phnum; unsigned short shentsize; unsigned short shnum; unsigned short shstrndx;} elfHeader;/* * An entry in the ELF program header table. * This describes a single segment of the executable. */typedef struct { unsigned int type; unsigned int offset; unsigned int vaddr; unsigned int paddr; unsigned int fileSize; unsigned int memSize; unsigned int flags; unsigned int alignment;} programHeader;
只要照着填充即可。
int Parse_ELF_Executable(char *exeFileData, ulong_t exeFileLength, struct Exe_Format *exeFormat){ int i = 0; Print("elf file length: %d \n", (int)exeFileLength); elfHeader *elf_head = (elfHeader *)exeFileData; exeFormat->numSegments = elf_head->phnum; exeFormat->entryAddr = elf_head->entry; programHeader *ph = (programHeader *)(exeFileData + elf_head->phoff); for(i=0;i<elf_head->phnum;i++) { exeFormat->segmentList[i].offsetInFile = ph[i].offset; exeFormat->segmentList[i].lengthInFile = ph[i].fileSize; exeFormat->segmentList[i].startAddress = ph[i].paddr; exeFormat->segmentList[i].sizeInMemory = ph[i].memSize; exeFormat->segmentList[i].protFlags = ph[i].flags; } return 0;}
使用readelf -a user/a.exe
来观察调试。
完成project2并不难,大家会发现project2运行的时候会出现一些问题。
比如并没有打印出第二个字符串"Hi,This is the second string"
又或者系统会死机,等等各种各样的问题。
在我的机器上,运行之后出现了这个问题。
看libc/entry.c
void _Entry(void){ /* Call main(); arguments won't be needed */ main(0, 0); /* make the inter-selector jump back */ __asm__ __volatile__ ("leave"); __asm__ __volatile__ ("lret"); //pop eip, pop cs.}
可以看到是这里调用了用户程序user/a.exe
问题就出在下面的这两句嵌入汇编上。
察看a.exe反汇编代码。
objdump -d user/a.exe
00001000 <_Entry>: 1000: 83 ec 1c sub $0x1c,%esp 1003: c7 44 24 04 00 00 00 movl $0x0,0x4(%esp) 100a: 00 100b: c7 04 24 00 00 00 00 movl $0x0,(%esp) 1012: e8 09 00 00 00 call 1020 <main> 1017: c9 leave 1018: cb lret 1019: 83 c4 1c add $0x1c,%esp 101c: c3 ret 101d: 90 nop 101e: 90 nop 101f: 90 nop00001020 <main>: 1020: 55 push %ebp 1021: 89 e5 mov %esp,%ebp 1023: 83 e4 f0 and $0xfffffff0,%esp 1026: 83 ec 10 sub $0x10,%esp 1029: c7 04 24 c0 20 00 00 movl $0x20c0,(%esp) 1030: e8 13 00 00 00 call 1048 <ELF_Print> 1035: c7 04 24 00 21 00 00 movl $0x2100,(%esp) 103c: e8 07 00 00 00 call 1048 <ELF_Print> 1041: b8 00 00 00 00 mov $0x0,%eax 1046: c9 leave 1047: c3 ret 00001048 <ELF_Print>: 1048: 8b 44 24 04 mov 0x4(%esp),%eax 104c: cd 90 int $0x90 104e: c3 ret
可以看到在_Entry入口处的汇编,首先
sub $0x1c,%esp
然而程序却抢先在
add $0x1c,%esp
之前使用leave和lret长跳转返回了,这样程序当然出错了。
我这里是将
__asm__ __volatile__ ("leave");
__asm__ __volatile__ ("lret");
__asm__ __volatile__ ("lret");
改成了
__asm__ __volatile__ ("add $0x1c, $esp");
__asm__ __volatile__ ("lret");
就能正常工作了。
呃,插入
__asm__ __volatile__ ("add $0x1c, $esp");
这句确实让人感觉挺不舒服的,暂时也没有更好的解决办法。
- GeekOS 中的project1
- GeekOS-Project1
- geekos项目project1代码
- GeekOS project1 -- 载入可执行文件
- GeekOS源代码学习(9)project1中Init_DMA与Init_Floppy
- GeekOS中的进程睡眠
- GeekOS中的线程切换
- GeekOS 中的文件系统
- GeekOS 中的进程同步方法
- Project1.vbp
- 基本思路 project1
- Project1.c
- Project1(Sorting)
- GeekOS-Project0
- GeekOS project0
- geekos project4
- GeekOS Project3
- GeekOS Project2
- hdu - 2602 - Bone Collector
- 使用dmsetup命令生成snapshot ( by quqi99 )
- Android使用JNI实现Java与C之间传递数据 .
- 照成“未处理的异常: 0xC0000005: 读取位置 0x00000000 时发生访问冲突”的三种可能性
- 【2013.1.31】好朋友就是你的东西是我的,我的东西还是我的——Flyweight(使用Vector)
- GeekOS 中的project1
- Android开发--AsyncTask异步任务(一)
- 在myeclipse中的console调用cmd
- c#中数据和方法的封装(第九课)
- 博客搬家声明
- 类的继承和实现(第十课)
- 汇编和机器码对照表
- Framework中接口的概念和实现(第十一课)
- windows下tomcat解压版自启动