想知道這個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 0x1000There are 3 program headers, starting at offset 52Program 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.!