程序人生-Hello’s P2P
計算機系統
大作業
題 ????目 ?程序人生-Hello’s P2P?
專?????? 業 ??計算機科學與技術??????
學 ?? 號 ??2021113044????????????
班 ?? 級 ??2103101???????????????
學?????? 生 ??方健??????????????
指 導 教 師 ??劉宏偉?????????????????
計算機科學與技術學院
2022年5月
摘? 要
本文深入的研究了hello的一生,深入研究它是如何從一個源程序hello.c轉化為一個可執行文件的,并在shell里面的運行步驟,詳細的分析了每一個過程的具體內容,如編譯、匯編、鏈接、重定位等等。
關鍵詞:匯編、重定位、ELF、鏈接???????????????????????????
(摘要0分,缺失-1分,根據內容精彩稱都酌情加分0-1分)
目? 錄
第1章 概述............................................................................................................. - 4 -
1.1 Hello簡介...................................................................................................... - 4 -
1.2 環境與工具..................................................................................................... - 4 -
1.3 中間結果......................................................................................................... - 4 -
1.4 本章小結......................................................................................................... - 4 -
第2章 預處理......................................................................................................... - 5 -
2.1 預處理的概念與作用..................................................................................... - 5 -
2.2在Ubuntu下預處理的命令.......................................................................... - 5 -
2.3 Hello的預處理結果解析.............................................................................. - 5 -
2.4 本章小結......................................................................................................... - 5 -
第3章 編譯............................................................................................................. - 6 -
3.1 編譯的概念與作用......................................................................................... - 6 -
3.2 在Ubuntu下編譯的命令............................................................................. - 6 -
3.3 Hello的編譯結果解析.................................................................................. - 6 -
3.4 本章小結......................................................................................................... - 6 -
第4章 匯編............................................................................................................. - 7 -
4.1 匯編的概念與作用......................................................................................... - 7 -
4.2 在Ubuntu下匯編的命令............................................................................. - 7 -
4.3 可重定位目標elf格式................................................................................. - 7 -
4.4 Hello.o的結果解析...................................................................................... - 7 -
4.5 本章小結......................................................................................................... - 7 -
第5章 鏈接............................................................................................................. - 8 -
5.1 鏈接的概念與作用......................................................................................... - 8 -
5.2 在Ubuntu下鏈接的命令............................................................................. - 8 -
5.3 可執行目標文件hello的格式.................................................................... - 8 -
5.4 hello的虛擬地址空間.................................................................................. - 8 -
5.5 鏈接的重定位過程分析................................................................................. - 8 -
5.6 hello的執行流程.......................................................................................... - 8 -
5.7 Hello的動態鏈接分析.................................................................................. - 8 -
5.8 本章小結......................................................................................................... - 9 -
第6章 hello進程管理................................................................................... - 10 -
6.1 進程的概念與作用....................................................................................... - 10 -
6.2 簡述殼Shell-bash的作用與處理流程..................................................... - 10 -
6.3 Hello的fork進程創建過程..................................................................... - 10 -
6.4 Hello的execve過程................................................................................. - 10 -
6.5 Hello的進程執行........................................................................................ - 10 -
6.6 hello的異常與信號處理............................................................................ - 10 -
6.7本章小結....................................................................................................... - 10 -
第7章 hello的存儲管理................................................................................ - 11 -
7.1 hello的存儲器地址空間............................................................................ - 11 -
7.2 Intel邏輯地址到線性地址的變換-段式管理............................................ - 11 -
7.3 Hello的線性地址到物理地址的變換-頁式管理....................................... - 11 -
7.4 TLB與四級頁表支持下的VA到PA的變換............................................. - 11 -
7.5 三級Cache支持下的物理內存訪問.......................................................... - 11 -
7.6 hello進程fork時的內存映射.................................................................. - 11 -
7.7 hello進程execve時的內存映射.............................................................. - 11 -
7.8 缺頁故障與缺頁中斷處理........................................................................... - 11 -
7.9動態存儲分配管理....................................................................................... - 11 -
7.10本章小結..................................................................................................... - 12 -
第8章 hello的IO管理................................................................................. - 13 -
8.1 Linux的IO設備管理方法.......................................................................... - 13 -
8.2 簡述Unix IO接口及其函數....................................................................... - 13 -
8.3 printf的實現分析........................................................................................ - 13 -
8.4 getchar的實現分析.................................................................................... - 13 -
8.5本章小結....................................................................................................... - 13 -
結論......................................................................................................................... - 14 -
附件......................................................................................................................... - 15 -
參考文獻................................................................................................................. - 16 -
第1章 概述
1.1 Hello簡介
P2P: From Program to Process
最開始我們將代碼一個個寫入hello.c中,它是hello程序的生命的起點,然后調用預處理器cpp將hello.c預處理為hello.i,接下來編譯器ccl會將hello.i翻譯為hello.s,然后通過匯編器將hello.s翻譯為機器指令保存在hello.o中,最后調用鏈接器形成可執行文件hello,在shell中輸入命令./hello,操作系統就會通過fork為其生成子進程,然后通過execve將其加載,不斷進行訪存、內存申請等操作。最后,在程序結束返回后,由父進程或祖先進程進行回收,程序結束。這樣就實現了完整的P2P過程。
020: From Zero-0 to Zero-0
指一個程序從無到擁有自己的內存,地址和時間周期等,然后直到該程序執行結束后,父進程負責將其回收的全部過程。shell在子進程中使用execve執行這個程序時,系統將會把這個程序加載到內存,等待進程結束時,它的父進程將會回收hello,而系統內核將會對內存進行清理,這個過程就叫做020。
1.2 環境與工具
1.2.1 硬件環境:
X64 CPU;2GHz;2G RAM;256GHD Disk
1.2.2 軟件環境:
Windows11 64位以上; Vmware 16;Ubuntu 20.04 ;LTS 64位/優麒麟 64位
1.2.3 開發工具:
Visual Studio 2021 64位;CodeBlocks 64位;vi/vim/gedit/gcc;edb
1.3 中間結果
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?圖1-3:中間過程文件
hello.c:源程序
hello.i:經過預處理過后的文件;
hello.s:經過編譯生成的匯編文件;
hello.o:匯編生成的可重定位的文件;
hello:通過鏈接產生的可執行目標文件二進制文件;
elf.txt: 可重定位目標文件hello.o導出的ELF文件;
hello_elf.txt:可執行文件hello導出的ELF文件;
objdump_hello.s:可重定位目標文件hello.o反匯編生成的文件
hello_objdump.s:可執行文件hello反匯編生成的文件
1.4 本章小結
本章簡單介紹hello程序的一生,介紹了P2P和020過程,然后又介紹我所使用的硬件環境、軟件環境和開發調試工具,最后則簡述了過程中間生成的中間文件的名字和作用。
(第1章0.5分)
第2章 預處理
2.1 預處理的概念與作用
預處理概念:
預處理指的是預處理器cpp根據以字符#開頭的命令,來修改原始的C程序,最后生成.i文本文件的過程。
預處理作用:
1.將源文件中用#include 形式聲明的文件復制到新的程序中;
2..用實際值替換用#define 定義的字符串;
3.根據#if后面的條件決定需要編譯的代碼;
4.預編譯程序可以識別一些特殊符號,預編譯程序對于在源程序中出現的這些特殊符號串會用合適值進行替換。
5.預處理還可以幫助程序員節省工作量,提高程序可讀性,便于維護。
2.2在Ubuntu下預處理的命令
命令:cpp hello > hello.i
??????????? 圖2-2:Ubuntu下預處理命令展示
2.3 Hello的預處理結果解析
? ? ? ?? ????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖2-3:hello.i內容展示
解析:可以看見程序被拓展成了3060行,原始hello.c程序出現在3046行之后。在這之前是頭文件stdio.h、unistd.h以及stdlib.h依次展開。預處理器將它們全部都展開直接插入程序文本中,所以最后的.i文件特別的長
2.4 本章小結
本章介紹了預處理的概念和作用,以及預處理的方法,然后展示了.i文件的部分內容,分析了里面內容的由來。
(第2章0.5分)
第3章 編譯
3.1 編譯的概念與作用
編譯的概念:
編譯程序是通過詞法分析和語法分析,在確認所有的指令都符合語法規則后,將其翻譯成等價的中間代碼或匯編代碼來表示。編譯器cc1將預處理過的文本文件hello.i 翻譯轉換成匯編文本文件hello.s。
編譯的作用:
1.語法分析:編譯程序的語法分析器以單詞符號作為輸入,分析單詞符號串是否形成了符合語法規則的語法單位,方法有自上而下分析法和自下而上分析法兩種;
2.中間代碼:源程序的一種內部表示,或稱之為中間語言。中間代碼的作用是使編譯程序的結構在邏輯上更為簡單明確,特別是使目標代碼的優化比較容易實現;
3.代碼優化:指對程序進行多種等價變換,使得從變換后的程序出發時能生成更有效的目標代碼;
4.目標代碼:生成目標代碼是編譯的最后階段。目標代碼生成器把語法分析或優化后生成的中間代碼轉換成目標代碼。此處目標代碼指匯編語言代碼,即不同種類的語言提供相同的形式,其指令與處理器的指令集類似,更貼近底層,便于匯編器將其轉換為可執行機器語言代碼供機器執行。
3.2 在Ubuntu下編譯的命令
命令:gcc -S hello.i -o hello.s
????????????????? 圖3-2:Ubuntu下編譯的命令展示
3.3 Hello的編譯結果解析
3.3.1結果展示:
????????????????????????????? 圖3-3-1:hello.s內容展示
3.3.2偽指令
打開我們的hello.s文件我們可以發現匯編代碼有一部分是以.作為開頭,這些以‘.’開頭的行都是指導匯編器和鏈接器工作的偽指令,通常我們可以忽略這些行,下面給出這些偽指令的含義:如下表
| 偽指令 | 含義 |
| ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?.file | 聲明源文件 |
| .text | 聲明代碼節 |
| ?? .section | 文件代碼段 |
| ? .rodata | 只讀文件 |
| ?.align | 數據指令地址對齊方式 |
| ?.string | 聲明字符串 |
| ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .globl | 聲明全局變量 |
| ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?.type | 聲明變量類型 |
?????????????????? 表3-3-2:偽指令的類型、含義
3.3.3數據
(1)字符串常量
??????????????? 圖3-3-3-1:hello.s匯編代碼關于字符串常量的部分
解析:.LC0和.LC1作為兩個字符串變量被聲明對于原程序printf里面的字符串“用法: Hello 學號 姓名 秒數!”和“Hello %s %s”。而在.LC0中出現的\347\224等是因為中文字符沒有對應的ASCII碼值無法直接顯式顯示,所以這樣的字符方式顯示。而且這兩個字符串都在.rodata下面,所以是只讀數據。
(2)全局變量
? ? ? ? ? ? ?圖3-3-3-2:hello.s匯編代碼關于全局變量main的部分
解析:這里.globl聲明了main是一個全局變量,.type main,@function看出main是一個函數類型
(3)變量
? ? ? ? ? ???????
??????? 圖3-3-3-3:hello.s匯編代碼關于函數參數argc、argv的部分?
解析:可以看出來%edi保存的main函數的第一個參數int argc,%rsi保存的是main函數的第二個參數char *argv[],然后在棧中-20(%rbp)的位置保存變量argc占4個字節,在-32(%rbp)的位置保存變量argv[],占八個字節,接下來程序就可以通過-20(%rbp)和-32(%rbp)直接找到函數的參數內容。
??????? ????????
???????? 圖3-3-3-3-1:hello.s匯編代碼關于局部變量i的部分
解析:可以看出-4(%rbp)保存的是變量i的值,初始為0,每次循環加一,循環的條件是i<=8。
3.3.4賦值
????????
圖3-3-3-4:hello.s匯編代碼關于變量i賦值操作的部分
解析:通過使用mov指令,將立即數0直接賦值給變量i。
3.3.5算術操作
????????
圖3-3-5-1:hello.s匯編代碼關于棧指針%rsp算術操作的部分
解析:通過sub將棧指針%rsp減去立即數32,代表棧申請32個字節的空間。
????????
圖3-3-5-2:hello.s匯編代碼關于棧指針%rsp算術操作的部分
解析:通過add可以將-4(%rbp)里面的值加是立即數1,即i=i+1。
3.3.6關系操作/控制轉移
????????
?????????????圖3-3-6-1 hello.s匯編代碼關于控制轉移if else的部分
解析:在-20(%rbp)保存的是第一個參數argc,通過cmpl操作將argc與4做比較,je指令可以根據上一步比較結果設置的條件碼狀態實現跳轉(關系操作!),若argc不等于4,則跳過25行的命令,從26行開始執行if里面的內容;若argc等于4,則跳轉到.L2處去執行else部分的內容,從而實現if else的控制轉移。
???????? ???????
? ????????
圖3-3-6-2 hello.s匯編代碼關于控制轉移for循環的部分
解析:首先在.L2里面初始化i=0,然后通過jmp無條件跳轉到.L3,在.L3里面進行for循環里面的條件判斷,通過cmp將i與8比較,然后通過jle實現條件跳轉(關系控制<=),若i<=8,則跳轉到.L4里面進行for循環里面的操作,在.L4里面有一個add的操作,使得每次循環i++;若最后i>8了,則在.L3中不會跳轉到.L4,結束循環,從而實現了一個完整的for循環的操作。
3.3.7數組/指針/結構操作
????????
圖3-3-7:hello.s匯編代碼關于數組/指針操作argv的部分
解析:在之前談過,在-32(%rbp)保存著一個數組指針argv,它指向一個字符串數組,其中argv[0]保存著命令行第一個參數,一般是可執行文件的名字這里是./hello,argv[1],argv[2]則保存著我們輸入的倆個字符串,這里是我們的學號和姓名,char *占8個字節,將-32(%rbp)加16就是argc[2],-32(%rbp)加8就是argv[1],從而實現利用指針對數組argv[]的調用。
3.3.8函數操作
????????
圖3-3-8-1:hello.s中關于函數printf的調用部分
解析:printf是庫里面的一個函數,在最后鏈接的時候內容會復制進去,參數傳遞:call puts時只輸入字符串參數首地址,for循環中call printf時輸入argv[1]和argc[2]的地址,函數調用:if判斷滿足條件后在for循環中被調用,調用之后會在屏幕是輸出我們想要的結果
????????
圖3-3-8-2:hello.s關于函數exit的調用
解析:參數傳遞:輸入參數為1后再執行退出命令,exit(1)表示正常退出,exit(0)表示非正常退出;函數調用:if判斷條件滿足后被調用;在調用之后程序就會結束進程。
????????
圖3-3-8-3:hello.s關于函數sleep的調用
解析:參數傳遞:輸入參數atoi(argv[3]),函數調用:在for循環中被調用,call sleep;調用之后程序延遲。其中其中函數atoi()是把字符串轉換成整型數的一個函數。
????????
圖3-3-8-4:hello.s關于函數getchar的調用
解析:函數調用:在main中被調用,沒有參數傳遞,getchar()。
????????
圖3-3-8-5:hello.s中關于main函數的調用。
解析:main是主函數,有倆參數argc和argv先前已經介紹過,在函數里面最后會return 0,來結束main函數。
3.4 本章小結
本章首先介紹了編譯的概念與作業,然后介紹了在Ubuntu下編譯的命令,展示了hello.s的內容,最后根據hello.s文件,詳細介紹了中編譯器是怎么處理C語言的各個數據類型以及各類操作的。
(第3章2分)
第4章 匯編
4.1 匯編的概念與作用
匯編的概念:
驅動程序運行匯編器as將匯編語言(hello.s)翻譯成機器語言(hello.o)的過程稱為匯編,同時這個機器語言文件是可重定位目標文件。匯編器接受.s文件作為輸入,以.o可重定位目標文件作為輸出。可重定位目標文件包含二進制代碼和數據,在編譯時與其他可重定位目標文件能夠合并起來,創建成一個可執行目標文件,從而被加載到內存中執行。
匯編的作用:
匯編是將高級語言轉化為機器可以直接識別并且執行的代碼文件的過程,匯編器將.s文件匯編程序翻譯成機器語言指令,并將這些指令打包成為可重定位目標程序的格式,最后將結果保留在.o目標文件中,.o文件是一個包含程序指令編碼的二進制文件。
4.2 在Ubuntu下匯編的命令
??? 命令:gcc -c hello.s -o hello.o
????????
????????????? 圖4-2:Ubuntu下匯編命令展示
4.3 可重定位目標elf格式
4.3.1導出ELF文件
由于hello.o文件不能直接打開,因此我們需要輸入readelf -a hello.o > ./elf.txt將其轉換成elf文件后查看,如下圖:
???????????????????????????? 圖4-3-1:Ubuntu下導出elf文件展示
????
??????????????????? 圖4-3-1-1:elf文件展示
4.3.2 ELF頭
? ? ? ? ? ? ? ? ????????????????????? 圖4-3-2:ELF頭信息
解析:可以看見ELF以16個字節開頭,其中前4個字節稱為ELF的魔數,分別與ASCll中的DEF控制符、字符E、字符L、字符F對應,這4個字節是用來確認文件類型的。接下來一個字節是用來表示ELF文件類型的,0x1表示32位,0x2表示64位。第六個字節用來表示字節序,0x1表示小段,0x2表示大端。第7個字節表示ELF的版本號,一般為1。剩下的字節在ELF標準中沒有定義用0填充。16個字節下面也給出了文件的類別,字節序,ELF文件類型,版本號,在第11行還給出了這個ELF的起始地址,在15行還給出了ELF頭的長度為64個字節,13行給出了節頭部表的起始位置是1240,19行說明了節頭部表里面有14個表項,18行說明了每一個表項的大小是64個字節。
4.3.3節頭部表:
???????? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ? ? ? 圖4-3-3:節頭部表信息
解析:上面分析過里面會有13個表項即13個節,頭節目表描述了.o文件中出現的各個節的類型、位置、所占空間大小等必要有效信息。如上圖所示,其屬性分別有名稱、類型、地址(此時暫時未被分配均為0)、偏移量(節相對于文件開始的偏移)、節大小、全體(Entry)大小、旗標(節屬性)、鏈接(與其他節的)、信息(附加節)、對齊(2的Align次方)。以第一個表項為例,它的名稱是.test,位置在0x40,大小是146個字節(0x92)。
4.3.4重定位節
? ????????????????? 圖4-3-4重定位節信息
解析:當匯編器生成一個目標模塊時,它并不知道數據和代碼最終將放在內存中的什么位置。它也不知道這個模塊引用的任何外部定義的函數或者全局變量的位置。所以,無論何時匯編器遇到最終未知的目標引用,它就會生成一個重定位條目,告訴鏈接器在將目標文件合并到可執行文件時如何修改新的引用。在重定位節中包含了在代碼中使用的一些外部變量等信息,在鏈接的時候需要根據重定位節的信息對這些變量符號進行修改。本程序需要重定位的信息有:.rodata中的模式串,puts,exit,printf,slepsecs,sleep,getchar這些符號需要與相應的地址進行重定位這些符號的相關信息偏移量如上圖。
4.3.5符號表
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ??圖4-3-5:符號表信息
解析:在符號表中存放了所有引用的函數和全局變量的信息。Num為某個符號的編號,value表示符號在對應節中的偏移量,size是所占字節數,type是符號的類型,Bind表示這個符號是本地的還是全局的,Vis在C語言中并未使用可以忽略,Ndx是section的索引值, Name是符號的名稱。這里我們發現main這個符號為例,它的Ndx為1,說明它的定義在./test節中(之前節頭部表可以看節的索引值),Bind是GLOBAL說明是一個全局變量,Type是FUNC說明main是一個函數,size是146,說明它在./test節中占146個字節,value為0,說明它偏移量為0,就在./test最上面。
4.4 Hello.o的結果解析
4.4.1生成反匯編文件
命令為:objdump -d -r hello.o > objdump_hello.s
???????? ? ? ? ? ? ????????????????? 圖4-4-1-0:Ubuntu下生成反匯編文件展示
????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖4-4-1-1:hello反匯編生成的objdump_hello.s
4.4.2對比與分析:
(1)操作數變化:
hello.s中的操作數一般是十進制表示的,而objdump_hello.s中的操作數一般是十六進制表示的,如下圖:
????????
??圖4-4-2-1:hello.s中立即數操作數展示
????????
???????? 圖4-4-2-2:objdump_hello.s中立即數操作數展示
(2)跳轉指令變化:
hello.s中地址使用段名稱如 je .L2,而objdump_hello.s中則使用相對偏移地址,如je? 2f <main+0x2f>,如下圖:
? ????????
??? 圖4-4-2-3:hello.s中跳轉指令展示
????????
??? 圖4-4-2-4:objdump_hello.s中跳轉指令展示
(3)函數調用:
在hello.s中,call的后面緊跟著函數名,而在objdump_hello.s中,call的后面緊跟著下一條指令的地址。這是因為該程序調用的函數是共享庫中的函數,最終需要通過動態鏈接器才能確定函數的運行時執行地址。對于這一類函數調用,call指令中的相對偏移量暫時編碼為全0,然后在.rela.text節添加重定位條目,等待鏈接時的進一步確定。如下圖所示:
????????? ? ? ? ? ? ? ? ?圖4-4-2-5:objdump_hello.s函數調用展示
???????? ? ? ? ? ? ? ? ?圖4-4-2-6:hello.s函數調用展示
(4)數據訪問變化:
在hello.s 文件中,使用段名稱+%rip訪問 rodata(printf 中的字符串),而在反匯編得到的hello.asm中,使用 0+%rip進行訪問。其原因與函數調用類似,rodata 中數據地址在運行時才能確定,故訪問時也需要重定位。在匯編成為機器語言時,將操作數設置為全 0 并添加相應的重定位條目,如下圖所示:
???????? ? ? ? ?? ? ? ? ? ? ? ? 圖4-4-2-7:hello.s中數據訪問的變化
??????????????????????? 圖4-4-2-8:objdump_hello.s中數據訪問的變化
4.4.3機器語言與匯編語言:
發現hello.o的反匯編文件objdump_hello.s與hello.s匯編代碼基本是一致的,但是在這反匯編文件的字里行間中,也混雜著一些我們相對陌生的面孔,也就是機器代碼。這些機器代碼是二進制機器指令的集合,每一條機器代碼都對應一條機器指令,到這才是機器真正能識別的語言。每一條匯編語言都可以用機器二進制數據來表示,匯編語言中的操作碼和操作數以一種相當于映射的方式和機器語言進行對應,從而讓機器能夠真正理解代碼的含義并且執行相應的功能。總之匯編語言能與機器碼建立一一對應的關系。
4.5 本章小結
本章首先介紹了匯編的概念和作用,然后給出了在Ubuntu下啟動匯編的指令,以及如何導出ELF文件。然后詳細的分析了ELF文件里面的內容,對ELF頭、頭部表、重定向節、符號表的內容做了詳細的解釋,然后利用objdump反匯編hello.o將反匯編文件objdump_hello.s與原匯編文件hello.s做比較,分析了倆者的異同點,最后分析了機器語言與匯編語言的關系。
(第4章1分)
第5章 鏈接
5.1 鏈接的概念與作用
鏈接的概念:
鏈接是指將多種不同重定位目標文件或靜態/動態庫的代碼和數據部分收集整合起來,修改符號引用,組合并且輸出成一個單一可執行目標文件的過程。?
鏈接的作用:
鏈接能夠節省源程序空間,同時對于未編入的常用函數文件能夠進行合并從而生成能夠正常工作的可執行目標文件。除此之外鏈接也可以表述為使得一個復雜程序被分解成諸多簡明模塊來進行編寫,并且最終被合并為一個可執行程序。這些作用使得分離編譯成為了一種可能。
5.2 在Ubuntu下鏈接的命令
命令:ld -o hello -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o hello.o /usr/lib/x86_64-linux-gnu/libc.so /usr/lib/x86_64-linux-gnu/crtn.o
???????? ? ? ? ? ? ? ? ??????????????? 圖5-2:Ubuntu下匯編命令展示
5.3 可執行目標文件hello的格式
5.3.1導出hello的ELF文件
命令: readelf -a hello > ./hello_elf.txt
???????? ? ? ? ? ? ? ?????????????? 圖5-3-1:導出hello的ELF文件
5.3.2 ELF頭
????????
??????????????? 圖5-3-2:hello的ELF頭內容
解析:與之前分析hello.o的過程類似,不過注意到第8行的類型變為了EXEC即可執行文件,同時發現ELF頭的地址變了,變為了0x4010f0,這是因為它們需要重定位到最終運行時的內存地址,section也變多了變為了27個,還多了一個program headers程序頭部表。
5.3.3節頭
?????????????????????????? 圖5-3:節頭內容展示
解析:節頭的內容分析和之前一樣,唯一的區別一個就是節的數量多了,由原來的14個變成了現在的27個,多出來的節如下:
| 節 | 主要作用 |
| .interp: | 包含了動態鏈接器在文件系統中的路徑; |
| .note???? .ABI-tag: | ELF規范中記錄的注釋部分,包含一些版本信息; |
| .gnu???? .hash: | 符號的哈希表,用于加速查找符號; |
| .dynamic??? .dynsym??? .dynstr: | 與動態鏈接符號相關; |
| .gnu.version?? .gnu.version_r: | 與版本有關的信息; |
| .init_array?? .fini_array: | 存放函數指針,其中的函數分別在main函數 之前之后調用,用于初始化和收尾; |
| .init??? .fini: | 存放上述初始化和收尾的代碼 |
| .eh_frame_hdr | 與異常處理相關。 |
?????????????????? 表5-3-3-1:多出的節主要功能展示
5.3.4程序頭
??????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖5-3-4程序頭展示
解析:?elf可執行文件易加載到內存,可執行文件連續的片被映射到連續的內存段,程序頭部表描述了這一映射關系。程序頭部表包括各程序頭的偏移量、內存地址、對其要求、目標文件與內存中的段大小及運行時訪問權限等信息。
5.3.5段節
???????????????????????????? 圖5-3-5:段節展示
解析:如圖所示的節段映射,說明了在鏈接過程中,將多個代碼段與數據段分別合并成一個單獨的代碼段和數據段,并根據段的大小以及偏移量重新設置各個符號的地址。
5.3.6重定位節
????????????????????????? 圖5-3-6:重定位節展示
解析:分析過程與之前類似,由于對于可執行目標文件來說,仍然會存在重定位信息,因為有些需要動態鏈接的塊還沒有被鏈接,重定位節中就給出了這些符號的相關信息。
5.3.7符號表
?? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖5-3-7:符號表內容
解析:對于可執行目標文件來說,包含兩個符號表,一個符號表的名稱為.dynsym, 從名稱和符號表中的內容來看應該是還沒有動態鏈接的一些未知符號。另一張符 號表就是熟知的.symtab,里面保存了程序中定義和引用的函數以及全局變量等的 信息,其余分析與之前類似。
5.3.8版本信息
??????????????????????????????? 圖5-3-8:關于版本信息的節
解析:這部分不重要,只是介紹版本信息而已。
5.4 hello的虛擬地址空間
???????? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? 圖5-4-1:Date Dump窗口
解析:打開edb,加載程序后,打開edb的Date Dump窗口,查看本進程的虛擬地址空間各段信息,并與5.3對照分析說明。通過查看edb可以發現虛擬地址開始于0x400000,如上圖所示。
??????? ? ? ? ? ? ? ? ? ???????????????????????? 圖5-4-2:Date Dump窗口
解析:根據查看的信息我們發現虛擬地址結束于0x400ff0
然后根據節頭目表,我們可以通過edb查看各個節的信息,比如.text節,虛擬地址起始于0x4010f0,大小為0x145,如下圖所示:
???????? ? ? ? ? ? ??????????????????? 圖5-4-3:Data Dump尋找./test節部分
同理我們還可以同樣找到其他節在虛擬地址空間例的信息
5.5 鏈接的重定位過程分析
5.5.1生成hello的反匯編文件
命令:objdump -d -r hello > hello_objdump.s ??如圖:
??????????????????????????? 圖5-5-1:生成hello的反匯編文件
5.5.2 hello與hello.o反匯編文件的差異:
(1)代碼增多
???????????????
??圖5-5-2-1:hello.o反匯編內容 ???????????圖5-5-2-2:hello反匯編內容
解析:可以看出來hello反匯編多了很多代碼,接下來我會慢慢介紹為什么會多出這些代碼
(2)增加了外部的共享庫函數
???????????????????? 圖5-5-2-3:hello反匯編代碼關于共享庫的部分
解析:在鏈接之后,鏈接器會把共享庫里面被hello.o里面調用的函數復制進去,所以代碼增加了不少。
(3)函數調用地址不同
???????????????????? 圖5-5-2-4:hello.o反匯編文件函數調用部分
????????? ? ? ? ? ? ?圖5-5-2-5:hello反匯編文件函數調用部分
分析:hello中沒有hello.o中的重定位條目,并且跳轉和函數調用的地址在hello中都變成了虛擬內存空間地址。對于hello.o的反匯編代碼,函數只有在鏈接之后才能確定運行執行的地址,所以在.rela.text節中為其添加了重定位條目。所以在hello.o文件中,函數的調用時直接用當前的下一個地址占位,而在hello文件中是跳轉到一個【函數名@plt】的函數的地址處。
(4).text段的內容增多了
? ??????? ???????
? 圖5-5-2-6:hello.o反匯編./test部分 ???????圖5-5-2-7:hello反匯編./test部分
分析:發現左方的.text段只有main函數,右方的.text段不僅包含.main,更包含main函數的很多入口函數,如_start之類,所以./test變長了很多。
5.5.3 鏈接的過程:
通過分析hello和hello.o的不同之處,得到鏈接的主要過程就是鏈接就是鏈接器ld將各個目標文件(即各種.o文件)整合組裝在一起時其各個目標文件中的各個庫函數段會按照一定的規則累積在一起,需要進行符號解析、重定位及計算符號引用地址三個步驟。
5.5.4重定位:
重定位將合并輸入模塊。并為每個符號分配運行地址。重定位由兩個步驟組成:重定位節與符號定義、重定位節中的符號引用。
1.定位節與符號定義,鏈接器將相同類型的節合并為同一類型的新的聚合節,此后鏈接器將運行時內存地址賦值新的聚合節、輸入模塊定義的每個節,還有輸入模塊定義的每個符號。
2.重定位節中的符號引用,鏈接器修改代碼節與數據節中對每個符號的引用,使他們指向正確的運行地址,這一步依賴hello.o中的重定位條目。
5.6 hello的執行流程
??????????????????????? 圖5-6:Ubuntu下edb執行
通過edb的調試記錄下每列函數調用call命令進入的函數如下所示:
這其中的子函數名和地址(后6位)如下所示:
<_init>????? 401000
<.plt>?????? 401020
<puts@plt>? 401090
<printf@plt> 4010a0
<getchar@plt>4010b0
<atoi@plt>? 4010c0
<exit@plt>? 4010d0
<sleep@plt> 4010e0
<_start>???? 4010f0
<_dl_relocate_static_pie> 401120
<main>?????????????? 401125
<__libc_csu_init>?????? 4011c0
<__libc_csu_fini>?????? 401230
<_fini>??????????????? 401238
5.7 Hello的動態鏈接分析
???????? ?? ? ? ? ? ? ? ? ?圖5-7-1:hello的節頭部表
分析:在elf文件中我們可以找到got pit開頭地址為0x404000,在edb中找到它
????????????????????????? 圖5-7-2:dl_init之前窗口
dl_init之后
???????? ? ? ? ? ? ? ? ? ? 圖5-7-3:dl_init之后窗口
分析:找到的地址已經由0變成了相應的偏移量。
5.8 本章小結
在本章中先介紹了鏈接的概念和作用,還介紹了然后在Ubuntu下利用ld命令實現鏈接,然后導出hello的ELF文件,分析該ELF文件里面的具體內容,并與hello.o的ELF做對比,分析異同,然后分析了hello的虛擬地址空間、重定位過程、執行流程、以及動態鏈接過程。
(第5章1分)
第6章 hello進程管理
6.1 進程的概念與作用
進程的概念:
經典定義是一個執行中程序的實例,提供給我們一種錯覺:我們的程序好像是系統中當前運行的唯一程序,我們的程序獨占使用處理器和內存,處理器好像是無間斷的執行我們程序中的指令,我們程序的代碼和數據好像是系統中內存唯一的對象。
進程的作用:
進程提供給了應用程序兩個關鍵抽象
一個獨立的邏輯控制流,它提供一個假象,好像我們的程序獨占地使用處理器。
一個私有的地址空間,它提供一個假象,好像我們的程序獨占地使用內存系統。
6.2 簡述殼Shell-bash的作用與處理流程
作用:Shell執行一系列的讀取解析步驟,然后終止。讀取步驟讀取來自用戶的一個命令行。解析步驟將命令行解析,并運行程序。
處理流程:
(1)終端進程讀取用戶由鍵盤輸入的命令行。
(2)分析命令行字符串,獲取命令行參數,并構造傳遞給execve的argv向量
(3)檢查第一個(首個、第0個)命令行參數是否是一個內置的shell命令
(4)如果不是內部命令,調用fork( )創建新進程/子進程
(5)在子進程中,用步驟2獲取的參數,調用execve( )執行指定程序。
(6)如果用戶沒要求后臺運行(命令末尾沒有&號)否則shell使用waitpid(或wait等待作業終止后返回。
(7)如果用戶要求后臺運行(如果命令末尾有&號),則shell返回;
6.3 Hello的fork進程創建過程
根據 shell 的處理流程,可以推斷,輸入命令執行 hello 后,父進程如果判斷不是內部指令,即會通過fork函數創建子進程。子進程與父進程近似,并得到一份與父進程用戶級虛擬空間相同且獨立的副本——包括數據段、代碼、共享庫、堆和用戶棧。父進程打開的文件,子進程也可讀寫。二者之間最大的不同或許在于 PID 的不同。Fork函數只會被調用一次,但會返回兩次,在父進程中,fork返回子進程的 PID,在子進程中,fork返回0。
6.4 Hello的execve過程
當Hello的進程被創建之后,他會調用execve函數加載并調用程序。exevce函數在被調用時會在當前進程的上下文中加載并運行一個新程序。它被調用一次從不返回,執行過程如下:
1.刪除已存在的用戶區域
2.映射私有區:為 hello 的代碼、數據、.bss 和棧區域創建新的區域結構,所有這些區域都是私有的、寫時才復制的
3.映射共享區:比如 hello 程序與共享庫 libc.so 鏈接
4.設置 PC:exceve() 做的最后一件事就是設置當前進程的上下文中的程序計數器,使之指向代碼區域的入口點
5.execve() 在調用成功的情況下不會返回,只有當出現錯誤時,例如找不到需要執行的程序時,execve() 才會返回到調用程序
6.5 Hello的進程執行
6.5.1邏輯控制流:
一系列程序計數器PC的值的序列叫做邏輯控制流,這些值唯一地對應于包含在程序的可執行目標文件中的指令,或是包含在運行時動態鏈接到程序的共享對象中的指令。各個進程將輪流使用處理器,在同一個處理器核心中,每個進程執行它的流的一部分后被暫時掛起,然后執行其他進程。
6.5.2用戶模式與內核模式:
處理器通過某個控制寄存器中的一個模式位來提供限制一個應用可以執行的指令以及它可以訪問的地址空間范圍的功能。該寄存器描述了當前進程運行的權限。當設置了模式位時,進程就運行在內核模式中。沒有設置模式位時,進程就運行在用戶模式中。一個運行在內核模式的進程可以執行指令集中的任何指令,并且可以訪問系統中的任何內存;而用戶模式的進程不允許和執行特權指令、也不允許用戶模式中的進程直接引用地址空間中內核區內的代碼和數據。
6.5.3上下文切換:
內核為每個進程維持一個上下文。上下文就是內核重新啟動的一個進程所需的狀態。這個狀態包括通用目的寄存器、浮點寄存器、程序計數器、用戶棧、狀態寄存器、內核棧和各種內核數據結構。
6.5.4進程時間片:
一個進程執行它的控制流的一部分的每一時間段叫做時間片。
6.5.5調度的過程:
在進程執行的某些時刻,內核可以決定搶占當前進程,并且可以重新開始一個先前被搶占了的進程,這種決策便稱為調度。調度是由內核中的調度器代碼處理的。當內核選擇一個新進程運行時,即內核調度了這個進程。在內核調度一個新的進程運行之后,它就可以稱之為搶占了當前進程,并使用上下文切換機制來將控制轉移到新的進程。例如執行sleep()函數,sleep()函數請求調用休眠進程,sleep()將內核搶占,進入倒計時,當倒計時結束后,hello程序重新搶占內核,繼續執行。
6.5.6? 用戶模式與核心模式的相互轉換:
為了能讓處理器安全運行,從而不至于損壞操作系統,必須提前已知應用程序可執行指令所能訪問的地址空間范圍。因此就存在用戶模式與核心模式的劃分:核心模式可以戲稱為“創世模式”,因為它擁有最高訪問權限,此時處理器有以一個寄存器當做模式位來描述當前進程的特權。進程只有故障、中斷或陷入系統調用時才會得到內核訪問權限,其他情況下始終處于用戶權限之中,從而保證了系統的安全性。
6.6 hello的異常與信號處理
6.6.1異常的種類:
| 類別 | 原因 | 異步/同步 | 返回行為 |
| 中斷 | 來自I/O設備的信號 | 異步 | 總是返回到下一條指令 |
| 陷阱 | 有意的異常 | 同步 | 總是返回到下一條指令 |
| 故障 | 潛在可恢復的錯誤 | 同步 | 可能返回到當前指令 |
| 終止 | 不可恢復的錯誤 | 同步 | 不會返回 |
?????????????????????? 表6-6-1:異常的種類
6.6.2產生信號:
信號可以被理解為一條小消息,他通知進程系統中發生了一個某種類型的事件。每種信號類型都對應于某種系統事件,它提供了一種機制,通知用戶進程發生了這些異常。發生異常后,系統會根據異常的種類發出信號,每個信號都有對應的序號。如下圖:
???????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???????????????? 圖6-6-2 Linux信號
6.2.3執行過程中操作實例:
正常執行:
??????????????????????????????? 圖6-2-3-1:正常執行
在運行過程中按下Ctrl-Z:
??????????????????????????????? 圖6-2-3-2:按下Ctrl-Z
按下Ctrl-Z后接其他指令:
??????????????????????? 圖6-2-3-3:按下Ctrl-Z+ps
分析:分析進程仍然處于后臺。
再按下jobs:
????????
? ? ? ? ? ? ? 圖6-2-3-4:按下jobs
結果:進程停止。
按下fg:
?
? ? ? ? ? ? ? ? ? ? 圖6-2-3-5:按下fg
結果:使該進程轉為前臺,繼續工作
按下kill:
????????
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖6-2-3-6:按下kill
結果:殺死進程。
按下pstree:
???????? ? ? ? ? ? ? ? ? ???? 圖6-2-3-7:按下pstree
6.2.4異常與信號處理說明:
當按下鍵盤的時候, 引發了中斷異常.如圖:
????????
???????????????????????? 圖6-2-4-1中斷異常實例
而當程序在進行顯示屏輸出的時候:
引發的是陷阱:
????????
???????????????????? 圖6-2-4-1中斷異常實例
通過陷阱,調用系統函數.將字符串打印到屏幕上面
信號處理:
對于kill -9 12350:直接將SIGKILL發送到12350進程,使其直接終止.
對于CTRL Z:將SIGINT/SIGSTP由內核發送到12350號進程.SIGINT直接終止12350.而SIGSTP將12350號進程進行停止與掛起.
6.7本章小結
本章介紹了進程的相關概念和作用,了解了shell的處理過程,以及如何利用fork創建子進程,介紹了進程調度和內核用戶模式切換,好介紹了4種異常和各種信號,最后根據實際操作進行分析。
(第6章1分)
第7章 hello的存儲管理
7.1 hello的存儲器地址空間
結合hello說明邏輯地址、線性地址、虛擬地址、物理地址的概念。
7.2 Intel邏輯地址到線性地址的變換-段式管理
7.3 Hello的線性地址到物理地址的變換-頁式管理
7.4 TLB與四級頁表支持下的VA到PA的變換
7.5 三級Cache支持下的物理內存訪問
7.6 hello進程fork時的內存映射
7.7 hello進程execve時的內存映射
7.8 缺頁故障與缺頁中斷處理
7.9動態存儲分配管理
Printf會調用malloc,請簡述動態內存管理的基本方法與策略。
7.10本章小結
第8章 hello的IO管理
8.1 Linux的IO設備管理方法
設備的模型化:文件
設備管理:unix io接口
8.2 簡述Unix IO接口及其函數
8.3 printf的實現分析
[轉]printf 函數實現的深入剖析 - Pianistx - 博客園
從vsprintf生成顯示信息,到write系統函數,到陷阱-系統調用 int 0x80或syscall等.
字符顯示驅動子程序:從ASCII到字模庫到顯示vram(存儲每一個點的RGB顏色信息)。
顯示芯片按照刷新頻率逐行讀取vram,并通過信號線向液晶顯示器傳輸每一個點(RGB分量)。
8.4 getchar的實現分析
異步異常-鍵盤中斷的處理:鍵盤中斷處理子程序。接受按鍵掃描碼轉成ascii碼,保存到系統的鍵盤緩沖區。
getchar等調用read系統函數,通過系統調用讀取按鍵ascii碼,直到接受到回車鍵才返回。
8.5本章小結
結論
Hello程序自被編寫出代碼起,經歷了以下幾個階段:
首先經過預處理解析宏定義、文件包含、條件編譯等,生成hello.i文件,然后編譯器將代碼轉化為匯編代碼,生成hello.s文件,然后將匯編語言轉換為機器語言,生成hello.o文件,然后鏈接器進行重定位動態解析等一系列操作,最終生成hello的可執行文件。然后父進程shell-bash進程調用fork函數,為hello創建進程,調用execve函數在當前進程的上下文加載并運行hello程序,該進程映射它的虛擬空間到文件,運行的過程當中伴隨著虛擬地址到物理地址的轉換,調用的函數與I/O設備緊密結合。運行結束后,子進程終止,最終被父進程回收。至此,hello程序結束了自己的一次運行。
一學期的計算機系統學習,雖然很累,不過收獲真的很大,感謝一學期老師和助教的辛苦付出!
(結論0分,缺失 -1分,根據內容酌情加分)
附件
列出所有的中間產物的文件名,并予以說明起作用。
hello.c:源程序
hello.i:經過預處理過后的文件;
hello.s:經過編譯生成的匯編文件;
hello.o:匯編生成的可重定位的文件;
hello:通過鏈接產生的可執行目標文件二進制文件;
elf.txt: 可重定位目標文件hello.o導出的ELF文件;
hello_elf.txt:可執行文件hello導出的ELF文件;
objdump_hello.s:可重定位目標文件hello.o反匯編生成的文件
hello_objdump.s:可執行文件hello反匯編生成的文件
(附件0分,缺失 -1分)
參考文獻
為完成本次大作業你翻閱的書籍與網站等
[1]?Randal E.Bryant, David O'Hallaron. 深入理解計算機系統[M]. 機械工業出版社.2019.6
[2] shell解析命令行的過程以及eval命令https://www.cnblogs.com/f-ck-need-u/p/7426371.html
[3]?printf?函數實現的深入剖析?https://www.cnblogs.com/pianist/p/3315801.html
[4]老師上課講的PPT
(參考文獻0分,缺失 -1分)
總結
以上是生活随笔為你收集整理的程序人生-Hello’s P2P的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: cad 打开硬件加速卡_CAD:“你的图
- 下一篇: ubuntu异常关机,断电重启后进入紧急