程式記憶體空間分佈與位置無關碼
學習程式碼重定位時,對程式碼段、資料段、BSS段等分別儲存的內容以及指令讀取時的記憶體操作感覺很模糊,先做個小測試做初步的探討:
一、測試在Linux系統下用arm-linux-gcc編譯test檔案
test測試程式碼:
Start.S:
.text .global _start _start: ldr sp, =0x34000000 bl main
main.c:
在測試程式碼main.c檔案中:①全域性變數(已初始化和未初始化)
②區域性變數(已初始化和未初始化)
③向固定地址的寫操作
④初始化陣列
intb1; int b2 = 100; void main (){ int a1; int a2 = 100; unsigned long *p = (unsigned long *)0x32000000; p[0] = 0x1111111; p[1] = 0x2222222; p[3] = 0x333333; unsigned int arr[] = { 0x444, 0x555, 0x666, }; }
makefile:
all: arm-linux-gcc -c -o main.o main.c arm-linux-gcc -c -o start.o start.S arm-linux-ld -T test.lds start.o main.o -o test.elf arm-linux-objcopy -O binary -S test.elf test.bin arm-linux-objdump -D test.elf > test.dis
連結指令碼:
SECTIONS { . = 0x30000000; __code_start = .; . = ALIGN(4); .text : { *(.text) } .= ALIGN(4); .rodata : { *(.rodata) } . = ALIGN(4); .data : { *(.data) } . = ALIGN(4); __bss_start = .; .bss : { *(.bss) *(.COMMON) } _end = .; }
檢視生成的.dis檔案:
test.elf: file format elf32-littlearm Disassembly of section .text: 30000000 <_start>: 30000000: e3a0d30d mov sp, #872415232 ; 0x34000000 30000004: ebffffff bl 30000008 <main> 30000008 <main>: 30000008: e1a0c00d mov ip, sp 3000000c: e92dd800 stmdb sp!, {fp, ip, lr, pc} 30000010: e24cb004 sub fp, ip, #4 ; 0x4 30000014: e24dd018 sub sp, sp, #24 ; 0x18 30000018: e3a03064 mov r3, #100 ; 0x64 3000001c: e50b3014 str r3, [fp, #-20] 30000020: e3a03432 mov r3, #838860800 ; 0x32000000 30000024: e50b3018 str r3, [fp, #-24] 30000028: e51b2018 ldr r2, [fp, #-24] 3000002c: e3a03c11 mov r3, #4352 ; 0x1100 30000030: e2833011 add r3, r3, #17 ; 0x11 30000034: e1833603 orr r3, r3, r3, lsl #12 30000038: e5823000 str r3, [r2] 3000003c: e3a02004 mov r2, #4 ; 0x4 30000040: e51b3018 ldr r3, [fp, #-24] 30000044: e0822003 add r2, r2, r3 30000048: e3a03c22 mov r3, #8704 ; 0x2200 3000004c: e2833022 add r3, r3, #34 ; 0x22 30000050: e1833603 orr r3, r3, r3, lsl #12 30000054: e5823000 str r3, [r2] 30000058: e3a0200c mov r2, #12 ; 0xc 3000005c: e51b3018 ldr r3, [fp, #-24] 30000060: e0822003 add r2, r2, r3 30000064: e3a03833 mov r3, #3342336 ; 0x330000 30000068: e2833c33 add r3, r3, #13056 ; 0x3300 3000006c: e2833033 add r3, r3, #51 ; 0x33 30000070: e5823000 str r3, [r2] 30000074: e59f3010 ldr r3, [pc, #16] ; 3000008c <.text+0x8c> 30000078: e24bc024 sub ip, fp, #36 ; 0x24 3000007c: e8930007 ldmia r3, {r0, r1, r2} 30000080: e88c0007 stmia ip, {r0, r1, r2} 30000084: e24bd00c sub sp, fp, #12 ; 0xc 30000088: e89da800 ldmia sp, {fp, sp, pc} 3000008c: 30000090 mulcc r0, r0, r0 Disassembly of section .rodata: 30000090 <.rodata>: 30000090: 00000444 andeq r0, r0, r4, asr #8 30000094: 00000555 andeq r0, r0, r5, asr r5 30000098: 00000666 andeq r0, r0, r6, ror #12 Disassembly of section .data: 3000009c <b2>: 3000009c: 00000064 andeq r0, r0, r4, rrx Disassembly of section .bss: 300000a0 <b1>: 300000a0: 00000000 andeq r0, r0, r0 Disassembly of section .comment: 00000000 <.comment>: 0: 43434700 cmpmi r3, #0 ; 0x0 4: 4728203a undefined 8: 2029554e eorcs r5, r9, lr, asr #10 c: 2e342e33 mrccs 14, 1, r2, cr4, cr3, {1} 10: Address 0x10 is out of bounds.
二、分析.dis檔案分析各段的對應內容①全域性變數(已初始化和未初始化):
未初始化的全域性變數被放在BSS段,已經初始化的會把初始化資料放在.data段。
②區域性變數(已初始化和未初始化):
區域性變數儲存在棧中,關於區域性變數的定義、賦值等操作都在.text段中,由CPU執行相應的指令將他們入棧和出棧。對應:
30000018: e3a03064 mov r3, #100 ; 0x64 3000001c: e50b3014 str r3, [fp, #-20]
③向固定地址的寫操作
跟區域性變數的操作方法類似,他們都屬於程式碼的執行部分,在.text段中。
④初始化陣列
首先觀察.dis檔案,初始化陣列時所用的初始值0x444,0x555,0x666都儲存在.rodata段,在.text段執行到初始化陣列這一部分時,命令會去.rodata段取得對應的值。
.text
......
30000074: e59f3010 ldr r3, [pc, #16] ; 3000008c <.text+0x8c> //找到.rodata段的指引指令位置 30000078: e24bc024 sub ip, fp, #36 ; 0x24 3000007c: e8930007 ldmia r3, {r0, r1, r2} 30000080: e88c0007 stmia ip, {r0, r1, r2} 30000084: e24bd00c sub sp, fp, #12 ; 0xc 30000088: e89da800 ldmia sp, {fp, sp, pc} 3000008c: 30000090 mulcc r0, r0, r0 //通過該指令找到.rodata段的位置 Disassembly of section .rodata: 30000090 <.rodata>: 30000090: 00000444 andeq r0, r0, r4, asr #8 30000094: 00000555 andeq r0, r0, r5, asr r5 30000098: 00000666 andeq r0, r0, r6, ror #12
三、分析位置無關性
位置無關碼是執行該程式碼段時依賴本身被儲存的實體地址,例如該程式碼,如果未按照連結指令碼要求,將其直接燒錄在0地址,其②、③可以正常完成,但因為.text中對.data和.rodata的引用依然是以0x30000000為基地址的,將無法找到對應的值,如果按照連結指令碼規定將其燒錄在0x30000000起始的位置,那麼當執行到初始化陣列時,去0x30000090尋找初始化資料以及使用全域性變數都是可以正常完成的。
所以,這其中①、④操作不是位置無關的。