GeekOS之旅-Project1 (Parse an ELF executable Files) 与 ELF 文件格式的浅析

想知道这个Project的Assignment之前,我们首先需要把这个Project编译过并把bochs启动起来. 编译和前一个Project一样很快通过.但是bochs启动遇到一个错误

00000000000p[ ] >>PANIC<< .bochsrc:10: directive 'diskc' not understood

看了下 .boshrc 中有这样一句: 10 diskc: file=diskc.img, cyl=40, heads=8, spt=64

看了下刚才编译过的project1里面有个disk.img ,这个diskc.img是什么呢? 其实diskc.img是个硬盘映像,我们之所以要加载这个是因为我们下面肯定会用到. 所谓映像是原始设备的对应字节. 所以需要在.bochrc中配置 disk.img.

再.boshrc添加如下

ata0-master:type=disk, mode=flat, path=./diskc.img, cylinders=40, heads=8, spt=64这句话就是制定硬盘参数,type为(disk,cdrom) mode(flat<一个文件布局>, concat<多文件布局>, external, dll ....), path就是路径了, cylinders为柱面大小, heads为头部大小,spt为每磁道扇区数

这么一解释,也就清楚多了. 好加上去,并把 第10行注释掉. 然后启动.


启动成功! 可以看到这个Project的Assigment:Parse an ELF executable image.

仔细阅读附带的手册Project2 的 Required Reading 和Synopsis . 主要任务是实现 src/geekos/elf.c 的Parse_ELF_Executable() 这个函数. 函数的功能是读取ELF文件中的offset, length, user address for the executable's text and data segments . 然后fill in the Exe_Format中!

所以我们首先要进行了解ELF格式的文件. 分析的样本用 project1 user下提供的a.exe , 这个源码在 src/user下a.c .

可以从google中搜索 Elf format file来进行了解.这里 可以我们也可以探讨下ELF 文件:

-> ELF是Linux默认的可执行文件格式, ELF包含三种类型:可重定位的文件(比如.o 目标文件), 共享文件 , 可执行文件. ELF文件包含 ELF Header, Sections ,String Table , Symbol Table, Relocation(Relocation Types) 这几个重要的部分. 让我们大概来看下ELF文件的组织图.


上面第一个为连接视图, 第二个为执行视图, ELF header(ELF 头部) , Program header table(程序头表), Section header table(节头表)

我们从ELF头部的数据结构开始看起, 打开geekos/elf.c 看下面这个结构体

14 /* 15 * ELF header at the beginning of the executable. 16 */ 17 typedef struct { 18 unsigned char| ident[16]; //信息 19 unsigned short|type; // 文件类型 20 unsigned short|machine; //硬件体系 21 unsigned int| version; 22 unsigned int| entry; //程序入口点 23 unsigned int| phoff; //程序头部偏移量 24 unsigned int| sphoff; //节头部偏移量 25 unsigned int| flags; //处理器特定标志 26 unsigned short|ehsize; //ELF头部长度 27 unsigned short|phentsize; //程序头部一段的长度 28 unsigned short|phnum; //程序头部段的个数 29 unsigned short|shentsize; //section 头部中一段的长度 30 unsigned short|shnum; //section 段个数 31 unsigned short|shstrndx; //section 头部字符表索引 32 } elfHeader;里面的相关信息 我已经注释上去了. 我们接着用readelf -h工具查看下a.exe文件 来对照着看下

ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Intel 80386 Version: 0x1 Entry point address: 0x1000 Start of program headers: 52 (bytes into file) Start of section headers: 4420 (bytes into file) Flags: 0x0 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 3 Size of section headers: 40 (bytes) Number of section headers: 7 Section header string table index: 4 可以对照的看下, 下面再研究 程序头表,继续看下它的结构体:

34 /* 35 * An entry in the ELF program header table. 36 * This describes a single segment of the executable. 37 */ 38 typedef struct { 39 unsigned int type; //段类型 40 unsigned int offset; //段位置相对于文件起始的offset 41 unsigned int vaddr; //段在内存中的地址 42 unsigned int paddr; //段的物理地址 43 unsigned int fileSize; //段在文件中的长度 44 unsigned int memSize; //段在内存中的长度 45 unsigned int flags; //标记 46 unsigned int alignment; //对齐标记 47 } programHeader;再用readelf -l 来看下a.exe的信息.

Elf file type is EXEC (Executable file) Entry point 0x1000 There are 3 program headers, starting at offset 52 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x001000 0x00001000 0x00001000 0x000a2 0x000a2 R E 0x1000 LOAD 0x0010c0 0x000020c0 0x000020c0 0x00028 0x00028 RW 0x1000 GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4 Section to Segment mapping: Segment Sections... 00 .text 01 .data 02

ELF深入可以看文档 看资料. 等提到ELF问题时 我们再来深入一下.

来做第一步 研究下Parse_ELF_Executable的原型.

int Parse_ELF_Executable(char *exeFileData, ulong_t exeFileLength, struct Exe_Format *exeFormat); exeFileData: 这个buffer包含了可执行的文件 也就是ELF文件.

exeFileLength: 这个是执行文件的长度

exeFormat: 这个结构体包含了文件的段和入口地址

返回值为int: ret返回0为成功

解析了elfHeader 和 ProgramHeader这两个结构体 剩下的就很轻松了, 分别用这两个结构体解析exeFileData的内容

exeFileData头部的指向肯定是从elfHeader开始的 因为之前我们已经分析过了,然后自带poff的属性 这是程序头部的偏移量,首地址+poff就可以得出programHeader的首地址了.

33 elfHeader* header = exeFileData; 34 programHeader* pHeader = (exeFileData+header->phoff);再继续看Exe_Format这个结构体.

77 /* 78 * A struct concisely representing all information needed to 79 * load an execute an executable. 80 */ 81 struct Exe_Format { 82 struct Exe_Segment segmentList[EXE_MAX_SEGMENTS]; /* Definition of segments */ 83 int numSegments;| | /* Number of segments contained in the executable */ 84 ulong_t entryAddr;| | /* Code entry point address */ 85 };
segmentList是个段的数组.

numSegments是段的数目

entryAddr是入口地址.

其中numSegments就是 elfHeader中的phnum, entryAddr是 elfHeader 的entry

segmentList因为是个数组,但是里面的每个元素是个结构体 我们先搞清这个结构体的内容:

57 /* 58 * A segment of an executable. 59 * It specifies a region of the executable file to be loaded 60 * into memory. 61 */ 62 struct Exe_Segment { 63 ulong_t offsetInFile;| /* Offset of segment in executable file */ 64 ulong_t lengthInFile;| /* Length of segment data in executable file */ 65 ulong_t startAddress;| /* Start address of segment in user memory */ 66 ulong_t sizeInMemory;| /* Size of segment in memory */ 67 int protFlags;| | /* VM protection flags; combination of VM_READ,VM_WRITE,VM_EXEC */ 68 };
稍微思考下, 这些不就是programHeader的offset, fileSize, vaddr, memSize, flags 嘛

ok一切准备就绪,就剩下苦力把程序写上去了.

30 int Parse_ELF_Executable(char *exeFileData, ulong_t exeFileLength, 31 struct Exe_Format *exeFormat) 32 { 33 elfHeader* header = exeFileData; 34 programHeader* pHeader = (exeFileData+header->phoff); 35 exeFormat->numSegments = header->phnum; 36 exeFormat->entryAddr = header->entry; 37 int i = 0; 38 for (; i< header->phnum; i++) { 39 exeFormat->segmentList[i].offsetInFile = pHeader->offset; 40 exeFormat->segmentList[i].lengthInFile = pHeader->fileSize; 41 exeFormat->segmentList[i].startAddress = pHeader->vaddr; 42 exeFormat->segmentList[i].sizeInMemory = pHeader->memSize; 43 exeFormat->segmentList[i].protFlags = pHeader->flags; 44 pHeader++; 45 } 46 47 return 0; //!! 48 49 //TODO("Parse an ELF executable image"); 50 }
整个程序如上,需要注意的是 return 0; 别忘了放进去.
测试结果如下:


下面继续 Project 2.!


  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值