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行注释掉. 然后启动.

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

启动成功! 可以看到这个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文件的组织图.

GeekOS之旅-Project1 (Parse an ELF executable Files) 与 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; 别忘了放进去.
测试结果如下:
GeekOS之旅-Project1 (Parse an ELF executable Files) 与 ELF 文件格式的浅析

下面继续 Project 2.!



原文链接: http://blog.csdn.net/crazyjixiang/article/details/6850759

你可能感兴趣的:(GeekOS之旅-Project1 (Parse an ELF executable Files) 与 ELF 文件格式的浅析)