北航操作系統課程lab1實驗報告


OS Lab1實驗報告

實驗思考題

Thinking 1.1

使用man objdump命令,可以看到,對於objdump -DS指令而言,-D參數表示反匯編所有部分的內容(disassemble the contents of all sections),-S參數表示顯示與反匯編匯合的源代碼 (Display source code intermixed with disassembly, if possible.)

使用命令

 /OSLAB/compiler/usr/bin/mips_4KC-gcc -c test1.c
 /OSLAB/compiler/usr/bin/mips_4KC-ld test1.o -o test1
 /OSLAB/compiler/usr/bin/mips_4KC-objdump -DS test1.o > o.txt
 /OSLAB/compiler/usr/bin/mips_4KC-objdump -DS test1 > out.txt

反編譯.o文件:

   2 test1.o:     file format elf32-tradbigmips
   3 
   4 Disassembly of section .text:
   5 
   6 00000000 <main>:
   7    0:   3c1c0000    lui gp,0x0                                           
   8    4:   279c0000    addiu   gp,gp,0
   9    8:   0399e021    addu    gp,gp,t9
  10    c:   27bdffe0    addiu   sp,sp,-32
  11   10:   afbe0018    sw  s8,24(sp)
  12   14:   03a0f021    move    s8,sp
  13   18:   24020003    li  v0,3
  14   1c:   afc20010    sw  v0,16(s8)
  15   20:   c7c00010    lwc1    $f0,16(s8)
  16   24:   468000a1    cvt.d.w $f2,$f0
  17   28:   8f820000    lw  v0,0(gp)
  18   2c:   d4400000    ldc1    $f0,0(v0)
  19   30:   46201082    mul.d   $f2,$f2,$f0
  20   34:   c7c00010    lwc1    $f0,16(s8)
  21   38:   46800021    cvt.d.w $f0,$f0
  22   3c:   46201002    mul.d   $f0,$f2,$f0
  23   40:   f7c00008    sdc1    $f0,8(s8)
  24   44:   00001021    move    v0,zero
  25   48:   03c0e821    move    sp,s8
  26   4c:   8fbe0018    lw  s8,24(sp)
  27   50:   27bd0020    addiu   sp,sp,32
  28   54:   03e00008    jr  ra
  29   58:   00000000    nop
  30   5c:   00000000    nop
  31 Disassembly of section .reginfo:
  32
  33 00000000 <.reginfo>:
  34    0:   f2000004    0xf2000004
  35    4:   00000000    nop
  36    8:   00000005    0x5
  37     ...
  38 Disassembly of section .pdr:
  39 
  40 00000000 <.pdr>:
  41    0:   00000000    nop
  42    4:   40000000    mfc0    zero,c0_index
  43    8:   fffffff8    sdc3    $31,-8(ra)
  44     ...
  45   14:   00000020    add zero,zero,zero
  46   18:   0000001e    0x1e
  47   1c:   0000001f    0x1f
  48 Disassembly of section .rodata:
  49 
  50 00000000 <.rodata>:
  51    0:   40091eb8    0x40091eb8
  52    4:   51eb851f    beql    t7,t3,fffe1484 <main+0xfffe1484>
  53    8:   00000000    nop
  54    c:   00000000    nop
  55     ...

反編譯.out文件:

   2 test1:     file format elf32-tradbigmips
   3
   4 Disassembly of section .reginfo:
   5
   6 00400094 <.reginfo>:
   7   400094:   f2000004    0xf2000004
   8   400098:   00000000    nop
   9   40009c:   00000005    0x5
  10     ...
  11   4000a8:   10007ff0    b   42006c <main+0x1ffbc>
  12 Disassembly of section .text:
  13
  14 004000b0 <main>:
  15   4000b0:   3c1c0fc0    lui gp,0xfc0
  16   4000b4:   279c7f40    addiu   gp,gp,32576
  17   4000b8:   0399e021    addu    gp,gp,t9
  18   4000bc:   27bdffe0    addiu   sp,sp,-32
  19   4000c0:   afbe0018    sw  s8,24(sp)
  20   4000c4:   03a0f021    move    s8,sp
  21   4000c8:   24020003    li  v0,3
  22   4000cc:   afc20010    sw  v0,16(s8)
  23   4000d0:   c7c00010    lwc1    $f0,16(s8)
  24   4000d4:   468000a1    cvt.d.w $f2,$f0
  25   4000d8:   8f828018    lw  v0,-32744(gp)
  26   4000dc:   d4400110    ldc1    $f0,272(v0)
  27   4000e0:   46201082    mul.d   $f2,$f2,$f0
  28   4000e4:   c7c00010    lwc1    $f0,16(s8)
  29   4000e8:   46800021    cvt.d.w $f0,$f0
  30   4000ec:   46201002    mul.d   $f0,$f2,$f0
  31   4000f0:   f7c00008    sdc1    $f0,8(s8)
  32   4000f4:   00001021    move    v0,zero
  33   4000f8:   03c0e821    move    sp,s8
  34   4000fc:   8fbe0018    lw  s8,24(sp)
  35   400100:   27bd0020    addiu   sp,sp,32
  36   400104:   03e00008    jr  ra
  37   400108:   00000000    nop
  38   40010c:   00000000    nop
  39 Disassembly of section .rodata:
  40
  41 00400110 <.rodata>:
  42   400110:   40091eb8    0x40091eb8
  43   400114:   51eb851f    beql    t7,t3,3e1594 <main-0x1eb1c>
  44   400118:   00000000    nop
  45   40011c:   00000000    nop
  46 Disassembly of section .got:
  47 
  48 10000000 <_GLOBAL_OFFSET_TABLE_>:
  49 10000000:   00000000    nop
  50 10000004:   80000000    lb  zero,0(zero)
  51 10000008:   00400000    0x400000
  52     ...

Thinking 1.2

使用readelf -h testELF解析testELF文件

 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:               0x8048490
   Start of program headers:          52 (bytes into file)
   Start of section headers:          4440 (bytes into file)
   Flags:                             0x0
   Size of this header:               52 (bytes)
   Size of program headers:           32 (bytes)
   Number of program headers:         9
   Size of section headers:           40 (bytes)
   Number of section headers:         30
   Section header string table index: 27

使用readelf -h vmlinux解析vmlinux文件

 ELF Header:
   Magic:   7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00 
   Class:                             ELF32
   Data:                              2's complement, big endian
   Version:                           1 (current)
   OS/ABI:                            UNIX - System V
   ABI Version:                       0
   Type:                              EXEC (Executable file)
   Machine:                           MIPS R3000
   Version:                           0x1
   Entry point address:               0x80010000
   Start of program headers:          52 (bytes into file)
   Start of section headers:          37164 (bytes into file)
   Flags:                             0x1001, noreorder, o32, mips1
   Size of this header:               52 (bytes)
   Size of program headers:           32 (bytes)
   Number of program headers:         2
   Size of section headers:           40 (bytes)
   Number of section headers:         14
   Section header string table index: 11

在Data段的信息中可以看到,我們生成的readelf文件適用於解析小端存儲的,而我們的內核vmlinux是大端存儲的文件,因此無法解析內核。

Thinking 1.3

實驗操作系統使用GXemul仿真器,支持直接加載ELF格式的內核。BootLoader中Stage1的功能已經由其提供,而事實上此時已經跳到了內核入口,跳到內核第一步就是實現在boot/start.o中的代碼初始化硬件設備,設置堆棧,跳轉到main函數入口。

Thinking 1.4

在加載程序時,避免發生共享頁面沖突頁面現象。首先,不同程序段的占用空間不能夠有重合,然后,盡量避免一個頁面同時被多個程序段所占用。即若前面的程序段末地址所占用的頁面地址為vi,則后續的程序段首地址應從下一頁面vi+1開始占用。

Thinking 1.5

內核入口在0x00000000處,main函數在0x80010000處。我們在start.S文件中編寫mips匯編代碼,設置堆棧后直接跳到main函數中。在跨文件調用函數時,每個函數會有一個固定的地址,調用過程為將需要存儲的值進行進棧等保護,再用jal跳轉到相應函數的地址。

Thinking 1.6

將宏定義的內容轉化為寄存器編號:

 mtc0 $0,$12
 #將SR寄存器清零
 mfc0 $t0,$16
 and $t0,~0x7 #要清零的位的反碼
 ori $t0,0x2 #要置1的位
 mtc0 $t0,$16
 #將Config寄存器0號位和2號位置0,將1號位置1

上電后,需要設置SR寄存器來使CPU進入工作狀態,而硬件通常都是復位后讓許多寄存器的位為未定義。

Config寄存器的后三位為可寫的,用來決定固定的kseg0區是否經過高速緩存和其確切的行為如何。

實驗難點展示

本次實驗要做的很簡單很基礎,當然了,這是基於我做完了之后得出來的結論。在那么多文件夾內找到彼此間的調用關系實屬不易。我認為實驗中較難的地方在於對ELF文件的解析和補充printf函數的部分。

Exercise 1.2 解析ELF文件

 typedef struct {
         unsigned char   e_ident[EI_NIDENT];     /* Magic number and other info */
         Elf32_Half      e_type;                 /* Object file type */
         Elf32_Half      e_machine;              /* Architecture */
         Elf32_Word      e_version;              /* Object file version */
         Elf32_Addr      e_entry;                /* Entry point virtual address */
         Elf32_Off       e_phoff;                /* Program header table file offset */
         Elf32_Off       e_shoff;                /* Section header table file offset */
         Elf32_Word      e_flags;                /* Processor-specific flags */
         Elf32_Half      e_ehsize;               /* ELF header size in bytes */
         Elf32_Half      e_phentsize;            /* Program header table entry size */
         Elf32_Half      e_phnum;                /* Program header table entry count */
         Elf32_Half      e_shentsize;            /* Section header table entry size */
         Elf32_Half      e_shnum;                /* Section header table entry count */
         Elf32_Half      e_shstrndx;             /* Section header string table index */
 } Elf32_Ehdr;
 typedef struct{
         Elf32_Word sh_name;                 /* Section name */
         Elf32_Word sh_type;                 /* Section type */
         Elf32_Word sh_flags;                /* Section flags */
         Elf32_Addr sh_addr;                 /* Section addr */
         Elf32_Off  sh_offset;               /* Section offset */
         Elf32_Word sh_size;                 /* Section size */
         Elf32_Word sh_link;                 /* Section link */
         Elf32_Word sh_info;                 /* Section extra info */
         Elf32_Word sh_addralign;            /* Section alignment */
         Elf32_Word sh_entsize;              /* Section entry size */
 }Elf32_Shdr;
 typedef struct {  
     // segment type
     Elf32_Word p_type;  
     // offset from elf file head of this entry  
     Elf32_Off p_offset;  
     // virtual addr of this segment  
     Elf32_Addr p_vaddr;  
     // physical addr, in linux, this value is meanless and has same value of p_vaddr 
     Elf32_Addr p_paddr;  
     // file size of this segment  
     Elf32_Word p_filesz;  
     // memory size of this segment  
     Elf32_Word p_memsz;  
     // segment flag  
     Elf32_Word p_flags;  
     // alignment  
     Elf32_Word p_align; 
 }Elf32_Phdr;

ELF文件結構圖如上圖所示,而這三個結構體分別代表了ELF程序頭、Section節頭、Segment段頭,實際上這個問題困擾了我很久。可以看到 ,這些結構體占用的空間都不大,所以這里面肯定不包含具體的內容。經過多次試驗,我也終於想明白了

Elf32_Ehdr對應圖標的ELF header,其中e_entry指明了程序入口的虛擬地址。e_phoff指明程序頭表的偏移量。e_shoff 指明節頭表的偏移量。e_phentsize 表示程序頭表每一個表項的大小(即對應的Elf32_Phdr結構體大小)。e_phnum 表示程序頭表項數目。e_shentsize 表示節頭表每一個表項的大小(即對應的Elf32_Shdr結構體大小)。e_shnum 表示節頭表項數目。

多個節頭表項,可看做Elf32_Shdr[e_shnum]數組組成了節頭表(Section Header Table)。通過對每個結構體的sh_addr屬性的訪問,可以輕松知道每個Section的具體位置。

多個程序頭表項,可看做Elf32_Phdr[e_phnum]數組組成了程序頭表(Programmer Header Table)。FileSiz代表該段的數據在文件中的長度。MemSiz代表該段的數據在內存中所應當占的大小,MemSiz永遠大於等於FileSizC語言中未初始化的全局變量可以導致這個現象。

於是理清結構后,這個問題就十分簡單了。程序入口地址是binary,節頭起點和程序頭起點分別為binary+e_shoffbinary+e_phoff。要想得到所有Section的地址,只需要聲明一個Elf32_Shdr*類型的指針變量shdr,地址為shdr -> sh_addr,下一個節頭的訪問只需要shdr++(指針增加一,地址增加一個節頭的大小)。當然了,知道每個節頭的大小,也可以每次都加e_shentsize的偏移量來表示下一個節頭的地址。

Exercise 1.5 補全print.c函數

兩個難點,第一個是針對變長參數列表的分析,第二個是對local help functions功能的分析。

<stdarg.h> 中定義了幾個重要的宏:

 typedef char* va_list;
 //在調用參數表之前,定義一個va_list 類型的變量
 void va_start ( va_list ap, prev_param );
 //然后應該對ap 進行初始化,讓它指向可變參數表里面的第一個參數。第一個參數是 ap 本身,第二個參數是在變參表前面緊挨着的一個變量,即“...”之前的那個參數;
 type va_arg ( va_list ap, type );
 //然后是獲取參數,調用va_arg,它的第一個參數是ap,第二個參數是要獲取的參數的指定類型,然后返回這個指定類型的值,並且把 ap 的位置指向變參表的下一個變量位置;
 void va_end ( va_list ap );
 //獲取所有的參數之后,我們有必要將這個 ap 指針關掉,以免發生危險,方法是調用 va_end

在print.c函數中,*fmt指向格式化字符串現在分析到的一個字符,而va_arg則一直往后獲取新的一個參數。對*fmt的分析是直接讀字符串,碰到‘%’就進入下一步操作,碰到‘\0'就退出循環。獲取格式的操作十分容易。而坑點主要在第二處代碼補寫的地方,由於%d是有符號十進制數,和其他的不一樣,而在函數聲明

 int PrintNum(char * buf, unsigned long u, int base, int negFlag, 
      int length, int ladjust, char padc, int upcase)

可以注意到第二個參數類型是unsigned long,這表明如果va_arg獲取到了一個負數的話,則需要將其轉化為正數,並將negFlag置1。

體會與感想

啟動過程理解

在學習lab1的過程中,初步看了下整體的文件夾和代碼。這些文件夾的名字取的都十分的恰當。以下是我的薄弱理解:boot用於在vmlinux啟動時做出第一手操作;drivers實際上是和硬件強關聯的,因為printf函數到了底層就靠他往某個地址寫入字符;gxemul是我們的用於仿真的軟件平台,make產生的vmlinux就在這里面;include是一些庫函數吧,有着一部分的c函數庫,還有對寄存器別名的宏定義等;init是內核啟動后經由boot操作結束后的進一步初始化;lib中我們實現了printf函數,可以理解成一個我們自己實現的一個庫文件夾;readelf主要是提供一個工具供我們解析ELF文件,貌似並不是啟動所需的必須成分;tools提供一個鏈接器,讓代碼段和數據段存在於應該在的位置上。

由於GXemul仿真器的作用,我們相當於直接走到啟動了內核這一步。在控制權轉向內核后,start.o文件開始初始化硬件,設置堆棧,並將當前PC跳轉到main函數入口。main函數的地址是在構建vmlinux的過程中,用tools/scse0_3.lds鏈接器鏈接到0x80010000地址上的,在init/main函數中,調用了mips_init函數,而printf函數所使用的庫就在我們寫的lib里面,這樣在啟動vmlinux時,顯示屏就會打出三行語句。這也是為什么如果沒實現1.5的內容是無法看到有效輸出的,或許也是因為如此才無法通過1.3和1.4的評測吧。

總結

從lab1過來人的角度來講,只要懂了,就很簡單,可在成為懂王之前,簡直就是裂開,譬如Exercise 1.1,要是沒有看到教程的提示,該怎么思考去哪個地方找路徑啊?lab1首先還是需要我們對工具的一個熟練使用。閱讀Makefile的能力對理解整個架構尤為重要,它勾勒了整個流程的先后順序,而且也真的好用,一個make就可以將改變后的代碼應用起來。重拾了mips匯編,在寫mips的時候要注意跳轉指令最多能跳轉多大的空間,此外不知道會不會涉及到延遲槽的利用,這次在jal main后面不管加沒加nop也都可以過評測。Linker Script的編寫是一個新東西,但現在應該掌握一點基礎就可以了,也算是見識到了.S文件和.lds文件的樣子,感覺學的好多好雜啊。整體文件的基本邏輯其實還是欠缺一點理解的,ELF文件的解析也是較難理解的,一下子多了如此多的代碼是很考驗我的接受能力的,這可只是lab1誒,好在同為重課的OO進入到了博客周,才給了可憐的我一絲喘息之機。當然,過了一遍重要的代碼還是有好處的,起碼以后能夠有一些些全局意識。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM