1. 程式人生 > 實用技巧 >程式記憶體空間分佈與位置無關碼

程式記憶體空間分佈與位置無關碼

學習程式碼重定位時,對程式碼段、資料段、BSS段等分別儲存的內容以及指令讀取時的記憶體操作感覺很模糊,先做個小測試做初步的探討:

一、測試在Linux系統下用arm-linux-gcc編譯test檔案

test測試程式碼:

Start.S:

.text
.global _start

_start:
    ldr sp, =0x34000000 
    bl main 

main.c:

在測試程式碼main.c檔案中:①全域性變數(已初始化和未初始化)

            ②區域性變數(已初始化和未初始化)

            ③向固定地址的寫操作

            ④初始化陣列

int
b1; 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尋找初始化資料以及使用全域性變數都是可以正常完成的。

  所以,這其中①、④操作不是位置無關的。