位置有關碼和位置無關碼詳細解釋

1、總體介紹

代碼的運行分爲三個階段:取指,譯碼,執行,而取指令肯定要知道代碼地址,執行過程又包含對變量的讀寫,對變量的讀寫也得知道變量的地址吧,至於代碼的地址和變量的地址怎麼確定呢?

答:由編譯器確定,編譯器確定地址分爲兩種形式,1、根據當前PC+偏移地址確定地址。2、直接用鏈接地址

對於第一種方式,地址是動態的,會根據PC的值變化而變化,對於第二種方式就是固定爲我們指定的鏈接地址

 

問題:上面的兩種方式和位置有關碼與位置無關碼有什麼聯繫呢?

答:第一種方式對應的就是位置無關碼,第二種方式對應的就是位置有關碼

總結:

顧名思義,位置無關碼肯定是和位置無關呀,這種代碼放在什麼位置都能正常運行,所以地址肯定是動態的不能固定的啊。而位置有關碼肯定是和某個具體位置有關的代碼啊,而這個具體位置就是我們的鏈接地址

 

2、位置有關碼和位置無關碼具體分析

鏈接腳本如下:

鏈接地址爲0x33f80000

SECTIONS {
    . = 0x33f80000;
    .text : { *(.text) }
    
    . = ALIGN(4);
    .rodata : {*(.rodata*)} 
    
    . = ALIGN(4);
    .data : { *(.data) }
    
    . = ALIGN(4);
    __bss_start = .;
    .bss : { *(.bss)  *(COMMON) }
    __bss_end = .;
}

彙編代碼如下:


.text
.global _start
_start:
 
	bl close_watch_dog		/* 相對跳轉,位置無關 */
	bl _start
	adr r0, close_watch_dog	/* 獲取標號地址,位置無關 */
	
	ldr r0, SMRDATA			/* 獲取標號處的值,位置無關 */
 
	ldr r0, =0x12345678
	ldr r0, =SMRDATA        /* 獲取標號地址,位置有關 */
	ldr r0, =main			/* 獲取函數名的地址,位置有關 */
	ldr r0 ,=__bss_start	/* 獲取鏈接腳本里標號的地址,位置有關 */
 
	
close_watch_dog:
	mov r1, #0
	str r1, [r0]
	mov pc, lr
	
SMRDATA:
    .word  0x22111120

c語言main如下:

int a;
void abc(){
	a = 2;
}
int main(){
	int b;
	a =1 ;
	b =1 ;
	abc();
	return 0;
}

 

編譯後的反彙編代碼如下,0x33f80000代表鏈接地址

如果我們把代碼加載到0x33f80000處運行,此時運行的地址和鏈接地址完全一樣,也就是下面的反彙編圖

此時,鏈接地址 = 運行地址

33f80000 <_start>:
33f80000:	eb000006 	bl	33f80020 <close_watch_dog>
33f80004:	ebfffffd 	bl	33f80000 <_start>
33f80008:	e28f0010 	add	r0, pc, #16
33f8000c:	e59f0018 	ldr	r0, [pc, #24]	; 33f8002c <SMRDATA>
33f80010:	e59f0018 	ldr	r0, [pc, #24]	; 33f80030 <SMRDATA+0x4>
33f80014:	e59f0018 	ldr	r0, [pc, #24]	; 33f80034 <SMRDATA+0x8>
33f80018:	e59f0018 	ldr	r0, [pc, #24]	; 33f80038 <SMRDATA+0xc>
33f8001c:	e59f0018 	ldr	r0, [pc, #24]	; 33f8003c <SMRDATA+0x10>
 
33f80020 <close_watch_dog>:
33f80020:	e3a01000 	mov	r1, #0
33f80024:	e5801000 	str	r1, [r0]
33f80028:	e1a0f00e 	mov	pc, lr
 
33f8002c <SMRDATA>:
33f8002c:	22111120 	andscs	r1, r1, #8
33f80030:	12345678 	eorsne	r5, r4, #125829120	; 0x7800000
33f80034:	33f8002c 	mvnscc	r0, #44	; 0x2c
33f80038:	33f80064 	mvnscc	r0, #100	; 0x64
33f8003c:	33f800a0 	mvnscc	r0, #160	; 0xa0
 
33f80040 <abc>:
33f80040:	e52db004 	push	{fp}		; (str fp, [sp, #-4]!)
33f80044:	e28db000 	add	fp, sp, #0
33f80048:	e59f3010 	ldr	r3, [pc, #16]	; 33f80060 <abc+0x20>
33f8004c:	e3a02002 	mov	r2, #2
33f80050:	e5832000 	str	r2, [r3]
33f80054:	e28bd000 	add	sp, fp, #0
33f80058:	e8bd0800 	pop	{fp}
33f8005c:	e12fff1e 	bx	lr
33f80060:	33f800a0 	mvnscc	r0, #160	; 0xa0
 
33f80064 <main>:
33f80064:	e92d4800 	push	{fp, lr}
33f80068:	e28db004 	add	fp, sp, #4
33f8006c:	e24dd008 	sub	sp, sp, #8
33f80070:	e59f3024 	ldr	r3, [pc, #36]	; 33f8009c <main+0x38>
33f80074:	e3a02001 	mov	r2, #1
33f80078:	e5832000 	str	r2, [r3]
33f8007c:	e3a03001 	mov	r3, #1
33f80080:	e50b3008 	str	r3, [fp, #-8]
33f80084:	ebffffed 	bl	33f80040 <abc>
33f80088:	e3a03000 	mov	r3, #0
33f8008c:	e1a00003 	mov	r0, r3
33f80090:	e24bd004 	sub	sp, fp, #4
33f80094:	e8bd4800 	pop	{fp, lr}
33f80098:	e12fff1e 	bx	lr
33f8009c:	33f800a0 	mvnscc	r0, #160	; 0xa0
 
Disassembly of section .bss:
 
33f800a0 <a>:
33f800a0:	00000000 	andeq	r0, r0, r0

 

 

如果我們把代碼加載到0x00000000處運行,此時實際運行的情況應該是下面這樣的:此時 鏈接地址≠運行地址

注意:下面的不是反彙編的到的,反彙編得到的還是上面那樣的,下面的只是我們根據實際運行情況描繪的

0x00000000代表的是實際運行地址

00000000 <_start>:
00000000:	eb000006 	bl	33f80020 <close_watch_dog>
00000004:	ebfffffd 	bl	33f80000 <_start>
00000008:	e28f0010 	add	r0, pc, #16
0000000c:	e59f0018 	ldr	r0, [pc, #24]	; 
00000010:	e59f0018 	ldr	r0, [pc, #24]	; 
00000014:	e59f0018 	ldr	r0, [pc, #24]	; 
00000018:	e59f0018 	ldr	r0, [pc, #24]	; 
0000001c:	e59f0018 	ldr	r0, [pc, #24]	; 
 
00000020 <close_watch_dog>:
00000020:	e3a01000 	mov	r1, #0
00000024:	e5801000 	str	r1, [r0]
00000028:	e1a0f00e 	mov	pc, lr
 
0000002c <SMRDATA>:
0000002c:	22111120 	andscs	r1, r1, #8
00000030:	12345678 	eorsne	r5, r4, #125829120	; 0x7800000
00000034:	33f8002c 	mvnscc	r0, #44	; 0x2c
00000038:	33f80064 	mvnscc	r0, #100	; 0x64
0000003c:	33f800a0 	mvnscc	r0, #160	; 0xa0
 
00000040 <abc>:
00000040:	e52db004 	push	{fp}		; (str fp, [sp, #-4]!)
00000044:	e28db000 	add	fp, sp, #0
00000048:	e59f3010 	ldr	r3, [pc, #16]	; 33f80060 <abc+0x20>
0000004c:	e3a02002 	mov	r2, #2
00000050:	e5832000 	str	r2, [r3]
00000054:	e28bd000 	add	sp, fp, #0
00000058:	e8bd0800 	pop	{fp}
0000005c:	e12fff1e 	bx	lr
00000060:	33f800a0 	mvnscc	r0, #160	; 0xa0
 
00000064 <main>:
00000064:	e92d4800 	push	{fp, lr}
00000068:	e28db004 	add	fp, sp, #4
0000006c:	e24dd008 	sub	sp, sp, #8
00000070:	e59f3024 	ldr	r3, [pc, #36]	; 33f8009c <main+0x38>
00000074:	e3a02001 	mov	r2, #1
00000078:	e5832000 	str	r2, [r3]
0000007c:	e3a03001 	mov	r3, #1
00000080:	e50b3008 	str	r3, [fp, #-8]
00000084:	ebffffed 	bl	33f80040 <abc>
00000088:	e3a03000 	mov	r3, #0
0000008c:	e1a00003 	mov	r0, r3
00000090:	e24bd004 	sub	sp, fp, #4
00000094:	e8bd4800 	pop	{fp, lr}
00000098:	e12fff1e 	bx	lr
0000009c:	33f800a0 	mvnscc	r0, #160	; 0xa0

 

分析如下:

一、B BL指令

bl  close_watch_dog   //位置無關碼

33f80000 : eb000006  bl 33f80020

    b 是相對跳轉:跳轉地址 = PC (PC=當前地址+8)+ 偏移值

    偏移值:機器碼 0xeb000006 低 24位 0x000006 按符號爲擴展爲 32 位 0x00000006 正數,向後跳轉 0x6 * 4(32位位4字節)=(24)字節 也就是 0x18

    1、起始運行地址爲0x0000000時:    0x00000000(當前運行地址) + 8 + 0x18(偏移地址) = 0x00000020 跳轉到正確位置

    2、起始運行地址爲0x3ff80000時:    0x3ff80000(當前運行地址)   + 8 + 0x18(偏移地址) = 0x33f80020  跳轉到正確位置

二、ADR

    adr r0, close_watch_dog     /* 獲取標號處的地址,位置無關碼,僞指令 */

   33f80008:  e28f0010  add  r0,  pc,  #16  

    跳轉地址 = PC (PC=當前地址+8)+ 偏移值

    1、運行地址爲0:                         0 + 8 + 16 = 0x20                             正確

    2、運行地址爲0x3ff80000:           0x3ff80008 + 8 + 16 = 0x33f80020   正確

    adr 獲取的是標號處的“實際”地址,標號在哪就是哪個地址,跟位置無關,總能獲得想要的值。

三、LDR

    ldr r0, SMRDATA       /* 獲取標號處的值,位置無關 僞指令和下面的中間加上=號的不同*/

    33f8000c:e59f0018  ldr   r0, [pc, #24]; 

    1、起始運行地址地址0:         r0 = c + 8 + 24 = 0x2c                            處   22111120                      

    2、起始運行地址0x3ff80000:   r0 = 0x3ff8000c + 8 + 24 = 0x33f8002c  處  22111120      和上面得到的一樣,所以和位置無關                 

    ldr r0, SMRDATA            /* 獲取標號地址,位置有關 這個中間加了= 不是僞指令,和上面的不同 */

    33f80014: e59f0018ldrr0,  [pc, #24];  33f80034 <SMRDATA+0x8>


    1、起始運行地址0:                       r0 = 0x14 + 8 + 24 = 0x34 處的值 33f8002c 與實際運行地址0x0000002c不同 ,所以不正確

  假設我要用這個地址去取值,或者用這個地址去跳轉函數,而此時實際的變量或者函數根本不在鏈接地址處,這樣肯定就出錯了

    2、起始運行地址0x3ff80000:          r0 = 0x3ff80014 + 8 + 24 = 0x33f80034 處的值 33f8002c和實際運行地址33f8002c相同 正確

    ldr r0, =main/* 獲取函數名的地址,位置有關 */
    ldr r0 ,=__bss_start /* 獲取鏈接腳本里標號的地址,位置有關 */

    這倆和 ldr r0, =SMRDATA 一致,位置有關,在0地址處運行不正確。
 

四、C函數

 

1、全局變量

int a;
void abc(){    //c語言函數abc()
	a = 2;
}

//對c語言函數反彙編

33f80040 <abc>:
33f80040:	e52db004 	push	{fp}		; (str fp, [sp, #-4]!)
33f80044:	e28db000 	add	fp, sp, #0
33f80048:	e59f3010 	ldr	r3, [pc, #16]	; 33f80060 <abc+0x20> //取出全局變量a
33f8004c:	e3a02002 	mov	r2, #2
33f80050:	e5832000 	str	r2, [r3]   //對全局變量賦值2
33f80054:	e28bd000 	add	sp, fp, #0
33f80058:	e8bd0800 	pop	{fp}
33f8005c:	e12fff1e 	bx	lr
33f80060:	33f800a0 	mvnscc	r0, #160	; 0xa0



33f800a0 <a>:   //全局變量a
33f800a0:	00000000 	andeq	r0, r0, r0

r3 爲全局變量 a 的地址,無論a 是存放在 0起始的地址還是0x33f80000起始的地址,它都認爲 a 的地址是 0x33f800a0 。因此,C函數中調用全局變量是位置有關碼。

 

 2、函數調用

    33f80084: ebffffed bl  33f80040 <abc>

    由於 main 函數和 abc 函數捱得比較近,在32M範圍之內,因此被翻譯成了一條 bl 指令那麼與位置無關。

    如果,調用的函數比較遠,大於32M的話,我認爲是與位置有關係的,這個不再驗證了。

3、局部變量

    局部變量在函數剛開始的地方被壓入棧,賦值語句被翻譯成:

    33f8007c: e3a03001movr3, #1
    33f80080: e50b3008 str r3, [fp, #-8]

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章