自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

從0學(xué)ARM-什么是位置無(wú)關(guān)碼?

存儲(chǔ) 存儲(chǔ)軟件
uboot搬移到DRAM中,然后跳轉(zhuǎn)到DRAM繼續(xù)運(yùn)行uboot剩下的代碼,那么在搬移之前的這段代碼必須是位置無(wú)關(guān),而且不能使用絕對(duì)尋址指令,否則尋址就會(huì)出錯(cuò)。

 [[375631]]

一、為什么需要位置無(wú)關(guān)碼?

首先我們需要了解一下ARM板子的啟動(dòng)流程。

1. exynos 4412啟動(dòng)流程

首先看一下 exynos 4412 memory map :

可知:iROM基地址是0x00000000 iRAM基地址是0x02020000

這兩塊內(nèi)存都在 SOC中。

查看exynos 4412 Booting Sequence:

位于第五章。

上圖是exynos4412上電復(fù)位時(shí)的啟動(dòng)流程,大致如下:

<1>執(zhí)行內(nèi)部只讀存儲(chǔ)器iROM中的一段代碼(廠家固化在里面的),這段代碼主要是初始化一些系統(tǒng)的基本配置,比如初步時(shí)鐘配置、堆棧、啟動(dòng)模式(對(duì)應(yīng)圖中的標(biāo)志①)。

<2>iROM中的代碼根據(jù)階段一獲取的啟動(dòng)模式(OM_STAT寄存器),從相應(yīng)的存儲(chǔ)介質(zhì)中拷貝BL1鏡像到內(nèi)部靜態(tài)隨機(jī)存儲(chǔ)器SRAM,BL1主要是完善系統(tǒng)時(shí)鐘的初始化工作、內(nèi)存控制器一些時(shí)序的配置。做完這些工作后把OS鏡像拷貝到內(nèi)存中(對(duì)應(yīng)圖中標(biāo)志②③)。

<3>跳轉(zhuǎn)到OS中執(zhí)行。

SRAM只有256KB,而uboot鏡像一般是超過(guò)這個(gè)大小的,也就是說(shuō)它不能把完整的uboot鏡像拷貝到SRAM中,因此,推測(cè)這里的拷貝方式應(yīng)該還是:「BL1拷貝的僅僅是uboot的一部分」,這一部分除了能設(shè)置好基本的硬件運(yùn)行環(huán)境外,「還能把其自身(uboot鏡像)完整的拷貝到內(nèi)存中」,然后uboot在內(nèi)存中運(yùn)行,完成OS鏡像的拷貝和引導(dǎo)

一般情況下兩者的地址并不相同,程序在DRAM中的地址重定位過(guò)程必須由程序員來(lái)完成。

這樣就有了「位置無(wú)關(guān)代碼」的概念,指代碼不在連接時(shí)指定的運(yùn)行地址空間,也可以執(zhí)行,它一段加載到任意地址空間都能執(zhí)行的特殊代碼。

uboot搬移到DRAM中,然后跳轉(zhuǎn)到DRAM繼續(xù)運(yùn)行uboot剩下的代碼,那么在搬移之前的這段代碼必須是位置無(wú)關(guān),而且不能使用絕對(duì)尋址指令,否則尋址就會(huì)出錯(cuò)。

二、怎么實(shí)現(xiàn)位置無(wú)關(guān)碼?

1. 什么是《編譯地址》?什么是《運(yùn)行地址》?「編譯地址:」

32位的處理器,它的每一條指令是4個(gè)字節(jié),以4個(gè)字節(jié)存儲(chǔ)順序,進(jìn)行順序執(zhí)行,CPU是順序執(zhí)行的,只要沒(méi)發(fā)生什么跳轉(zhuǎn),它會(huì)順序進(jìn)行執(zhí)行, 編譯器會(huì)對(duì)每一條指令分配一個(gè)編譯地址,這是編譯器分配的,在編譯過(guò)程中分配的地址,我們稱(chēng)之為編譯地址。

「運(yùn)行地址:」

是指程序指令真正運(yùn)行的地址,是由用戶(hù)指定的,用戶(hù)將運(yùn)行地址燒錄到哪里,哪里就是運(yùn)行的地址。比如有一個(gè)指令的編譯地址是0x40008000,實(shí)際運(yùn)行的地址是0x40008000,如果用戶(hù)將指令燒到0x60000000上,那么這條指令的運(yùn)行地址就是0x60000000。

當(dāng)編譯地址和運(yùn)行地址不同的時(shí)候會(huì)出現(xiàn)什么結(jié)果?結(jié)果是不能跳轉(zhuǎn),編譯后會(huì)產(chǎn)生跳轉(zhuǎn)地址,如果實(shí)際地址和編譯后產(chǎn)生的地址不相等,那么就不能跳轉(zhuǎn)。

「C語(yǔ)言編譯地址:」

都希望把編譯地址和實(shí)際運(yùn)行地址放在一起的,但是匯編代碼因?yàn)椴恍枰鯟語(yǔ)言到匯編的轉(zhuǎn)換,可以直接的去寫(xiě)地址,所以直接寫(xiě)的就是他的運(yùn)行地址,這就是為什么任何bootloader剛開(kāi)始會(huì)有一段匯編代碼,因?yàn)槠鹗即a編譯地址和實(shí)際地址不相等,這段代碼和匯編無(wú)關(guān),跳轉(zhuǎn)用的運(yùn)行地址。

2. 舉例

實(shí)現(xiàn)位置無(wú)關(guān)碼主要考慮以下兩個(gè)方面:

  1. 1. 位置無(wú)關(guān)的函數(shù)跳轉(zhuǎn) 
  2. 2. 位置無(wú)關(guān)的常量訪(fǎng)問(wèn) 

代碼

編譯代碼使用的連接文件「map.lds」如下:

  1. OUTPUT_FORMAT("elf32-littlearm""elf32-littlearm""elf32-littlearm"
  2. /*OUTPUT_FORMAT("elf32-arm""elf32-arm""elf32-arm")*/ 
  3. OUTPUT_ARCH(arm) 
  4. ENTRY(_start) 
  5. SECTIONS 
  6.  . = 0x40008000; 
  7.  . = ALIGN(4); 
  8.  .text      : 
  9.  { 
  10.   gcd.o(.text) 
  11.   *(.text) 
  12.  } 
  13.  . = ALIGN(4); 
  14.     .rodata :  
  15.  { *(.rodata) } 
  16.     . = ALIGN(4); 
  17.     .data :  
  18.  { *(.data) } 
  19.     . = ALIGN(4); 
  20.     .bss : 
  21.      { *(.bss) } 

如文件map.lds所示:「0x40008000」就是鏈接地址,

其他源文件如下:「gcd.s」

  1. .text 
  2. .global _start 
  3. _start: 
  4.   ldr  sp,=0x70000000         /*get stack top pointer*/ 
  5.   bl func 
  6.   ldr pc,=func 
  7.   b  main 
  8. func: 
  9.  mv pc,lr 

「main.c」

  1. /* 
  2.  * main.c 
  3.  * 
  4.  *  Created on: 2020-12-12 
  5.  *      Author: 一口Linux 
  6.  */ 
  7. int aaaa=0;  
  8. int main(void) 
  9.  aaaa = 0x11; 
  10.  while(1); 
  11.     return 0; 

「Makefile」

  1. TARGET=gcd 
  2. TARGETC=main 
  3. all
  4.  arm-none-linux-gnueabi-gcc -O1 -g -c -o $(TARGETC).o  $(TARGETC).c 
  5.  arm-none-linux-gnueabi-gcc -O1 -g -c -o $(TARGET).o $(TARGET).s 
  6.  arm-none-linux-gnueabi-gcc -O1 -g -S -o $(TARGETC).s  $(TARGETC).c 
  7.  arm-none-linux-gnueabi-ld $(TARGETC).o $(TARGET).o -Tmap.lds  -o  $(TARGET).elf  
  8.  arm-none-linux-gnueabi-objcopy -O binary -S $(TARGET).elf $(TARGET).bin 
  9.  arm-none-linux-gnueabi-objdump -D $(TARGET).elf > $(TARGET).dis 
  10.  
  11. clean: 
  12.  rm -rf *.o *.elf *.dis *.bin 

反匯編文件「gcd.dis」

如上圖所示:

  1. _start對(duì)應(yīng)的鏈接地址是0x40008000
  2. 9行 bl func對(duì)應(yīng)的指令
  3. 10行 ldr pc,=pc對(duì)應(yīng)的指令
  4. func的鏈接地址0x40008010
  5. 全局變量aaaa對(duì)應(yīng)的內(nèi)存位于bss段0x4000802c
  6. 19行 aaaa = 0x11 賦值語(yǔ)句對(duì)應(yīng)的機(jī)器碼

如果我們將生成的bin文件拷貝到內(nèi)存0x40008000位置運(yùn)行必然沒(méi)有問(wèn)題,

bl func 和 ldr pc,=func 都能跳轉(zhuǎn)到func函數(shù),而19行代碼,也能訪(fǎng)問(wèn)到全局變量aaaa。

如果我們將該程序拷貝到其他地址是否能正常運(yùn)行呢?

假定我們拷貝到0地址運(yùn)行,那么程序的執(zhí)行地址需要從0開(kāi)始重新編排,即_start對(duì)應(yīng)0地址,main對(duì)應(yīng)0x18。

拷貝到0地址后內(nèi)存布局:

拷貝到0地址運(yùn)行后,**內(nèi)存中指令(機(jī)器碼)**的內(nèi)容還和以前一樣, pc的值會(huì)根據(jù)實(shí)際運(yùn)行地址重新修正。

1.首先看bl func

對(duì)應(yīng)的匯編代碼是 第9行;該指令的機(jī)器碼是0xeb000001, 我們?cè)凇?. 從0開(kāi)始學(xué)ARM-ARM指令,移位、數(shù)據(jù)處理、BL、機(jī)器碼》講過(guò)該機(jī)器碼格式 是從pc的位置向前偏移1條指令 因?yàn)槿?jí)流水線(xiàn),所以應(yīng)該往下偏移3條指令,即func的位置, 所以bl仍然可以正確找到func這個(gè)函數(shù)。

bl func

2.ldr pc,=func 對(duì)應(yīng)的匯編代碼是 第10行;

我們可以看到是從pc值+4位置取出對(duì)應(yīng)的內(nèi)存的值,pc值+4是14,該位置對(duì)應(yīng)15行, 即將40008010寫(xiě)入到pc,

而我們的bin文件只有44個(gè)字節(jié)大小,所以此時(shí)內(nèi)存40008010并沒(méi)有我們編寫(xiě)的任何代碼。所以ldr pc,=func 無(wú)法跳轉(zhuǎn)到func。

3.c訪(fǎng)問(wèn)全局變量aaaa

對(duì)應(yīng)的匯編代碼是 第19行;

c訪(fǎng)問(wèn)全局變量aaaa

我們可以看到是從pc值+4位置取出對(duì)應(yīng)的內(nèi)存的值,pc值+4是28,該位置對(duì)應(yīng)22行, 即將4000802c寫(xiě)入到r3,然后20行會(huì)將r2中值寫(xiě)入到0x4000802c這個(gè)地址, 而此時(shí)該地址并不是全局變量aaaa, 所以此指令是無(wú)法找到bss段的aaaa變量的內(nèi)存。

四、總結(jié)

1. 位置無(wú)關(guān)碼:CPU取指時(shí)用相對(duì)地址取指令(比如pc +4),只要其相對(duì)地址沒(méi)有變,都能夠取指并運(yùn)行。即該段代碼無(wú)論放在內(nèi)存的哪個(gè)地址,都能正確運(yùn)行。究其原因,是因?yàn)榇a里沒(méi)有使用絕對(duì)地址,都是相對(duì)地址。

2. 位置相關(guān)碼:利用絕對(duì)地址取指并運(yùn)行,這就需要你存放程序(鏈接過(guò)程中)需要按照連接腳本的要求那樣執(zhí)行(Makefile里面有 -Ttext xxx指定或連接腳本)。即它的地址與代碼處于的位置相關(guān),是絕對(duì)地址,如:mov PC ,#0xff;ldr pc,=0xffff等。

3. 位置無(wú)關(guān)碼的應(yīng)用:1). 程序在運(yùn)行期間動(dòng)態(tài)加載到內(nèi)存;

2). 程序在不同場(chǎng)合與不同程序組合后加載到內(nèi)存(共享的動(dòng)態(tài)鏈接庫(kù));

3). 在運(yùn)行期間不同地址相互之間的映射(如bootloader)

4. 結(jié)論

  • 使用「mov pc ,xxx ; ldr pc ,xxx」等就是位置相關(guān)碼。這些使用絕對(duì)指令尋址。
  • 而使用「bl ,b ,adr,ldr」一般為位置無(wú)關(guān)碼。
  • 在使用「b, bl」調(diào)用C語(yǔ)言中的函數(shù)里「不要使用全局變量」,因?yàn)镃中全局變量的地址「也是根據(jù)鏈接地址生成」的。
  • 使用=和不使用=號(hào)是有很大區(qū)別的。「無(wú)=號(hào):取該標(biāo)號(hào)處的值,位置無(wú)關(guān) 有=號(hào):取該標(biāo)號(hào)的地址,位置相關(guān)」

【考一考】 考一考大家為什么uboot的異常向量表的reset異常,指令是b reset,而其他異常卻是我們本文所說(shuō)的位置相關(guān)碼,ldr pc,XXXXXX?

arm對(duì)應(yīng)的uboot異常向量表如下:

  1. arch/arm/cpu/armv7/start.S 

本文轉(zhuǎn)載自微信公眾號(hào)「 一口Linux」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系 一口Linux公眾號(hào)。

 

責(zé)任編輯:武曉燕 來(lái)源: 一口Linux
相關(guān)推薦

2020-12-11 09:05:04

ARMMDKGNU

2020-12-10 08:13:15

ARM架構(gòu) 嵌入式

2021-05-25 11:50:32

ARMuboot網(wǎng)絡(luò)協(xié)議棧

2021-01-08 12:06:59

WDT定時(shí)裝置

2021-01-16 11:40:28

ARM嵌入式開(kāi)發(fā)ADC應(yīng)用

2022-10-31 07:33:05

Javafor循環(huán)

2022-10-30 10:14:43

Java循環(huán)語(yǔ)句

2022-09-30 07:32:48

循環(huán)while循環(huán)體

2022-11-26 00:34:57

數(shù)組Java程序

2022-09-22 07:31:14

Java變量計(jì)算

2022-09-30 07:32:39

架構(gòu)

2022-09-16 07:32:15

編程計(jì)算機(jī)命令

2022-10-28 07:38:06

Javawhile循環(huán)

2019-01-29 14:29:03

微服務(wù)路由

2020-08-14 10:42:37

For循環(huán)Python代碼

2021-07-09 06:48:29

數(shù)組存儲(chǔ)內(nèi)存

2022-06-27 15:42:23

區(qū)塊鏈

2025-03-20 14:50:24

2020-06-12 14:17:26

開(kāi)源協(xié)議ARM

2017-05-18 13:23:06

機(jī)器學(xué)習(xí)強(qiáng)化學(xué)習(xí)分類(lèi)問(wèn)題
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)