1. 程式人生 > >深入編譯連結和執行

深入編譯連結和執行

我們先看下面這份程式碼:
#include<stdio.h>
int gdata1=10;
int gdata2=0;
int gdata3;

static int gdata4=11;
static int gdata5=0;
static int gdata6;

int main()
{
        int a=12;
	int b=0;
	int c;
	static int d=13;
	static int e=0;
	static int f;
	return 0;
}
額,程式碼中有全域性變數,靜態全域性變數,區域性變數,靜態區域性變數 gdata1,gdata2,gdata3,gdata4,gdata5,gdata6的作用域都是從程式開始到結束,a,b,c,d,e,f的作用域是在程式碼塊中,d,e,f一直佔用記憶體,直到程式結束。
我們知道我們用C/C++寫完一份程式碼後是要經過編譯和連結才能生成可執行程式(.exe),這個可執行程式檔案本身在磁碟中儲存的。那麼如果我們要執行它,就必須把它載入到記憶體中去執行。CPU訪問記憶體的速度要比訪問磁碟的速度快得多。 那麼需要把什麼東西載入到記憶體中去呢? 1、指令   2、資料 那麼我們必須知道我們編寫的程式中哪些是指令,哪些又是資料? 指令和資料又是怎麼儲存在記憶體中的呢? 計算機硬體CPU,記憶體,I/0都可以使用不同商家的硬體,作業系統為了遮蔽底層這些硬體的差異,使我們呼叫程式的時候能夠使用統一的介面,如圖: 為了遮蔽I/O的差異,使用VFS,遮蔽記憶體與I/O,使用虛擬地址空間,遮蔽CPU和I/0,使用了資源排程的最小單位,程序。
那麼我們的程式載入到記憶體其實是在虛擬地址空間裡,也就是虛擬記憶體裡,作業系統提供給我們實體記憶體的管理方法,虛擬記憶體。 虛擬記憶體有多大呢?跟CPU的位數有關,如果是32位的,虛擬記憶體就是2^32,4G的大小 那麼虛擬地址空間是怎麼存放資料的呢? .txt 放指令 .data 放資料 (初始化且不為0的資料) .bss 放資料 (未初始化和初始化為0的資料) 那麼我們上面寫的程式哪個是指令,哪個是資料? 額,指令和資料為什麼分開放?資料段為什麼要分成.data和.bss?這樣存放節省了誰的空間,虛擬地址空間還是檔案空間 程式的編譯過程是怎樣的?
二進位制可重定位目標檔案的格式是什麼呢?
我們在Linux下看一下:
.obj裡面有段   .text                            .data    16進位制下位元組數為C ,程式中gdata1,gdata4,d都屬於.data段,合起來12個位元組                        .bss     16進位制下位元組數為14,程式中按照我們之前說的,應該有6個數據,合起來是24個,可是.bss段只有20個位元組,那麼6箇中哪個不是儲存在.data塊呢?                        .comment                         .note.GUN-stack                  .

我們讀取.obj發現有一個檔案頭:裡面包含了在哪種機器上執行,入口地址(entry point address)為0x0,我們說過0地址是不可訪問的,那麼.obj檔案是不能執行的。
我麼再看一下.obj檔案的格式:
relocatable  是一個可重定位檔案,並不是可執行檔案。 我們再來看看:
size of this header:                 52(bytes)                                     檔案頭的大小是52位元組
我們在段資訊的那張圖裡知道:

VMA虛擬記憶體地址,LMA載入記憶體地址,都是0,那麼說明在編譯階段是不會給程式分配記憶體的。

再來看File off 偏移量:

那麼.data塊下面根本不是.bss段,而是.comment段,那麼.bss段是節省了檔案的空間。

檢視其他的段:


檢視段的內容:

.data段是不是有10,11,13,.obj檔案的格式就是這樣了。

Start of section  headers:     204 bytes    16進位制是CC


我們段表的第一條資訊,就是starting at offset 0xcc,說明.obj檔案先找檔案頭,在檔案頭裡找到段頭的偏移地址,就能找到.bss裡面的資料了。