重定位引入和鏈結指令碼

2022-03-17 22:02:52 字數 3136 閱讀 5384

1、乙個事實:大部分指令是位置有關編碼

位置無關編碼(pic,position independent code):彙編原始檔被編碼成二進位制可執行程式時編碼方式與位置(記憶體位址)無關。

位置有關編碼:彙編原始碼編碼成二進位制可執行程式後和記憶體位址是有關的。

我們在設計乙個程式時,會給這個程式指定乙個執行位址(鏈結位址)。就是說我們在編譯程式時其實心裡是知道我們程式將來被執行時的位址(執行位址)的,而且必須給編譯器鏈結器指定這個位址(鏈結位址)才行。最後得到的二進位制程式理論上是和你指定的執行位址有關的,將來這個程式被執行時必須放在當時編譯鏈結時給定的那個位址(鏈結位址)下才行,否則不能執行(就叫位置有關**)。但是有個別特別的指令他可以跟指定的位址(鏈結位址)沒有關係,也就是說這些**實際執行時不管放在**都能正常執行。

3、再解s5pv210的啟動過程:三星推薦和uboot的實現是不同的

三星推薦的啟動方式中:bootloader必須小於96kb並大於16kb,假定bootloader為80kb,啟動過程是這樣子:先開機上電後bl0執行,bl0會載入外部啟動裝置中的bootloader的前16kb(bl1)到sram中去執行,bl1執行時會載入bl2(bootloader中80-16=64kb)到sram中(從sram的16kb處開始用)去執行;bl2執行時會初始化ddr並且將os搬運到ddr去執行os,啟動完成。

uboot實際使用的方式:uboot大小隨意,假定為200kb。啟動過程是這樣子:先開機上電後bl0執行,bl0會載入外部啟動裝置中的uboot的前16kb(bl1)到sram中去執行,bl1執行時會初始化ddr,然後將整個uboot搬運到ddr中,然後用一句長跳轉(從sram跳轉到ddr)指令從sram中直接跳轉到ddr中繼續執行uboot直到uboot完全啟動。uboot啟動後在uboot命令列中去啟動os。

4、為什麼要重定位?

原因:鏈結位址和執行位址有時候必須不相同,而且還不能全部用位置無關碼,這時候只能重定位。

擴充套件:分散載入:把uboot分成2部分(bl1和整個uboot),兩部分分別指定不同的鏈結位址。啟動時將兩部分載入到不同的位址(bl1載入到sram,整個uboot載入到ddr),這時候不用重定位也能啟動。

評價:分散載入其實相當於手工重定位。重定位是用**來進行重定位,分散載入是手工操作重定位的。

5、執行時位址由什麼決定?鏈結位址由什麼決定?

執行時的位址是由執行時決定的(編譯鏈結時是無法絕對確定執行時位址的)

鏈結位址是由程式設計師在編譯鏈結的過程中,通過makefile中-ttext

舉例:strip:   strip是把可執行程式中的符號資訊給拿掉,以節省空間。(debug版本和release版本)

objcopy:由可執行程式生成可燒錄的映象bin檔案。

7、程式段的概念:**段、資料段、bss段(zi段)、自定義段

段就是程式的一部分,我們把整個程式的所有東西分成了乙個乙個的段,給每個段起個名字,然後在鏈結時就可以用這個名字來指示這些段。也就是說給段命名        就是為了在鏈結指令碼中用段名來讓段站在核實的位置。

段名分為2種:一種是編譯器鏈結器內部定好的,先天性的名字;一種是程式設計師自己指定的、自定義的段名。

先天性段名:

**段:  (.text),又叫文字段,**段其實就是函式編譯後生成的東西

資料段: (.data),資料段就是c語言中有顯式初始化為非0的全域性變數

bss段:  (.bss),又叫zi(zero initial)段,就是零初始化段,對應c語言中初始化為0的全域性變數。

後天性段名:段名由程式設計師自己定義,段的屬性和特徵也由程式設計師自己定義。

分析一些問題,跟這裡結合,然後試圖明白一些本質:

1、c語言中全域性變數如果未顯式初始化,值是0。本質就是c語言把這類全域性變數放在了bss段,從而保證了為0

2、c執行時環境如何保證顯式初始化為非0的全域性變數的值在main之前就被賦值了?就是因為它把這類變數放在了.data段中,而.data段會在main執行之前被處理(初始化)。

8、鏈結指令碼究竟要做什麼?

鏈結指令碼其實是個規則檔案,他是程式設計師用來指揮鏈結器工作的。鏈結器會參考鏈結指令碼,並且使用其中規定的規則來處理.o檔案中那些段,將其鏈結成乙個可       執行程式。

鏈結指令碼的關鍵內容有2部分:段名 + 位址(作為鏈結位址的記憶體位址)

鏈結指令碼的理解:

sections {} 這個是整個鏈結指令碼

. 點號在鏈結指令碼中代表當前位置。

= 等號代表賦值

9、adr與ldr偽指令的區別

ldr和adr都是偽指令,區別是ldr是長載入、adr是短載入。

重點:adr指令載入符號位址,載入的是執行時位址;ldr指令在載入符號位址時,載入的是鏈結位址。

深入分析:只要知道adr和ldr分別用於載入執行位址和鏈結位址,從而可以判斷是否需要重定位即可;根本不需知道為什麼adr和ldr是這樣子,但是我們還是給大家                擴充套件講下為什麼adr和ldr可以載入不同的位址。

10、重定位(**拷貝)

重定位就是彙編**中的copy_loop函式,**的作用是使用迴圈結構來逐句複製**到鏈結位址。

複製的源位址是sram的0xd0020010,複製目標位址是sram的0xd0024000,複製長度是bss_start減去_start

所以複製的長度就是整個重定位需要重定位的長度,也就是整個程式中**段+資料段的長度。

bss段(bss段中就是0初始化的全域性變數)不需要重定位。

11、清bss段

清除bss段是為了滿足c語言的執行時要求(c語言要求顯式初始化為0的全域性變數,或者未顯式初始化的全域性變數的值為0,實際上c語言編譯器就是通過清bss       段來實現c語言的這個特性的)。一般情況下我們的程式是不需要負責清零bss段的(c語言編譯器和鏈結器會幫我們的程式自動新增一段頭程式,這段程式會在       我們的main函式之前執行,這段**就負責清除bss)。但是在我們**重定位了之後,因為編譯器幫我們附加的**只是幫我們清除了執行位址那乙份**中       的bss,而未清除重定位位址處開頭的那乙份**的bss,所以重定位之後需要自己去清除bss。

12、長跳轉

清理完bss段後重定位就結束了。然後當前的狀況是:

1、當前執行位址還在0xd0020010開頭的(重定位前的)那乙份**中執行著。

2、此時sram中已經有了2份**,1份在d0020010開頭,另乙份在d0024000開頭的位置。

然後就要長跳轉了。

重定位引入和鏈結指令碼1

1 乙個事實 大部分指令是位置有關碼 位置無關碼 pic position independent code 彙編原始檔被編碼成二進位制可執行程式時編碼方式 生成的可執行程式 與位置 記憶體位址 無關。位置有關編碼 彙編原始檔被編碼成二進位制可執行程式後和記憶體位址是有關的。我們在設計乙個程式時,會...

1 5 5 重定位引入和鏈結指令碼1

朱老師物聯網大講堂 學習筆記 重定位引入和鏈結指令碼 位置無關編碼 位置有關編碼 我們在設計程式的時候會給程式指定乙個執行位址,比如0xd0020010 這裡是通過鏈結器中指定來設定的,它的另乙個說法也叫鏈結位址,鏈結位址可以通過上面的 ttext或者鏈結指令碼來指定。程式的執行位址和我們的設定有關...

重定位和鏈結指令碼

重定位實際就是在執行位址處執行一段位置無關碼pic,讓這段pic 也就是重定位 從執行位址處把整個程式映象拷貝乙份到鏈結位址處,完了之後使用一句長跳轉指令從執行位址處直接跳轉到鏈結位址處去執行同乙個函式,這樣就實現了重定位之後的無縫連線。鏈結位址 鏈結時指定的位址 指定方式為 makefile中用 ...