您的位置:首页 > 其它

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

2012-03-26 00:00 1691 查看
想知道这个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.!

$(document).ready(function(){dp.SyntaxHighlighter.HighlightAll('code');});

原文链接:
http://blog.csdn.net/crazyjixiang/article/details/6850759
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: