2016-08-04 55 views
2

我看與gcc產生的臂組件的代碼,我注意到GCC編譯用下面的代碼的功能鏈接寄存器(拇指/臂)R11關係:R7和與ARM架構調用約定

0x00010504 <+0>: push {r7, lr} 
    0x00010506 <+2>: sub sp, #24 
    0x00010508 <+4>: add r7, sp, #0 
    0x0001050a <+6>: str r0, [r7, #4] 
=> 0x0001050c <+8>: mov r3, lr 
    0x0001050e <+10>: mov r1, r3 
    0x00010510 <+12>: movw r0, #1664 ; 0x680 
    0x00010514 <+16>: movt r0, #1 
    0x00010518 <+20>: blx 0x10378 <[email protected]> 
    0x0001051c <+24>: add.w r3, r7, #12 
    0x00010520 <+28>: mov r0, r3 
    0x00010522 <+30>: blx 0x10384 <[email protected]> 
    0x00010526 <+34>: mov r3, lr 
    0x00010528 <+36>: mov r1, r3 
    0x0001052a <+38>: movw r0, #1728 ; 0x6c0 
    0x0001052e <+42>: movt r0, #1 
    0x00010532 <+46>: blx 0x10378 <[email protected]> 
    0x00010536 <+50>: adds r7, #24 
    0x00010538 <+52>: mov sp, r7 
    0x0001053a <+54>: pop {r7, pc} 

對我來說有趣的事情是,我看到GCC使用R7將值彈出到PC而不是LR。我看到與R11類似的東西。編譯器將r11和LR推入堆棧,然後將R11彈出到PC。不應該LR作爲返回地址而不是R7或R11。爲什麼在這裏使用R7(這是Thumb模式下的幀指針)? 如果你看看蘋果ios調用約定,它甚至是不同的。它使用其他寄存器(例如r4到r7)到PC返回控制。它不應該使用LR嗎?

或者我在這裏失去了一些東西?

另一個問題是,它看起來像LR,R11或R7值永遠不會是返回地址的立即值。但是一個指向包含返回地址的堆棧的指針。是對的嗎?

另一個奇怪的事情是,編譯器不會爲函數爆出者做同樣的事情。例如,它可能不是使用彈出到PC使用bx LR,但爲什麼?

+1

如果編譯器必須將返回地址存儲在堆棧中,它可以通過直接將*返回地址*直接卸載到PC中來保存'bx lr'。爲此目的,「pop」和「ldm *」被記錄爲互通分支指令。 – EOF

+0

'pop {r7,pc}'不會把'r7'放到'pc'上,因爲你的問題暗示着。它彈出堆棧中的兩個寄存器('sp')。 –

+0

我不知道我是否正確理解你的問題,但是爲了清楚起見,'push {r7,lr}'相當於'stmdb sp !, {r7,lr}',這意味着它會減少'sp',將'r7'存儲爲'[sp]',再次減量'sp',並將'lr'存儲爲'[sp]'。另一方面,'pop {r7,pc}'等同於'stmia sp !, {r7,pc}',意思是它會從'[sp]'加載'pc',增加'sp',加載'r7 '從'[sp]'開始,然後再次增加'sp'。因爲'pc'被加載,它會有效地分支。函數體中使用'r7'(其中'r0-r3'存儲參數),所以需要堆疊以保存值。 – rjp

回答

3

首先,他們可能希望保持堆棧在64位邊界上對齊。

由於寄存器r8到r15在大多數指令中不被支持,所以R7比幀指針更好。我將不得不看看我會認爲有特殊的電腦和SP偏移加載/存儲說明,爲什麼R7會被燒燬呢?

不確定所有你問的問題,你可以推動lr,但彈出pc,我認爲這相當於bx lr,但你必須查看每個架構,因爲有些你不能用pop來切換模式。在這種情況下,它似乎假定並沒有用額外的指令燒錄一個流行的r3 bx r3類的東西。而實際上這樣做可能需要兩個額外的指令pop r7,pop r3,bx r3。

因此,可能會發生這樣的情況,即一個編譯器被告知正在使用哪種體系結構,並且可以假定pop pc在另一個不太確定的情況下是安全的。再次必須閱讀各種架構的arm架構文檔,以瞭解哪些指令可用於更改模式的變體以及哪些不能。也許如果你用gnu瀏覽各種架構類型,它可能會改變它的返回方式。

編輯

unsigned int morefun (unsigned int, unsigned int); 
unsigned int fun (unsigned int x, unsigned int y) 
{ 
    x+=1; 
    return(morefun(x,y+2)+3); 
} 
arm-none-eabi-gcc -O2 -mthumb -c so.c -o so.o 
arm-none-eabi-objdump -D so.o 
00000000 <fun>: 
    0: b510  push {r4, lr} 
    2: 3102  adds r1, #2 
    4: 3001  adds r0, #1 
    6: f7ff fffe bl 0 <morefun> 
    a: 3003  adds r0, #3 
    c: bc10  pop {r4} 
    e: bc02  pop {r1} 
    10: 4708  bx r1 
    12: 46c0  nop   ; (mov r8, r8) 

arm-none-eabi-gcc -O2 -mthumb -mcpu=cortex-m3 -march=armv7-m -c so.c -o so.o 
arm-none-eabi-objdump -D so.o 
00000000 <fun>: 
    0: b508  push {r3, lr} 
    2: 3102  adds r1, #2 
    4: 3001  adds r0, #1 
    6: f7ff fffe bl 0 <morefun> 
    a: 3003  adds r0, #3 
    c: bd08  pop {r3, pc} 
    e: bf00  nop 

只是用而不MCPU 3月份給出了相同的結果(犯規彈出LR對R1-BX)。

行軍=用於ARMv5改變它如預期那樣流行和BX的東西稍微

00000000 <fun>: 
    0: b510  push {r4, lr} 
    2: 3102  adds r1, #2 
    4: 3001  adds r0, #1 
    6: f7ff fffe bl 0 <morefun> 
    a: 3003  adds r0, #3 
    c: bd10  pop {r4, pc} 
    e: 46c0  nop   ; (mov r8, r8) 

ARMV4T。

armv6-m給出了armv5t給出的內容。

gcc版本6.1.0使用--target = arm-none-eabi創建,沒有任何其他的arm說明符。

OP很可能問我是否理解正確,他們可能會看到三個指令pop pop bx而不是單個彈出{rx,pc}。或者至少有一個編譯器與另一個相比有所不同蘋果IOS被提及,所以它可能默認爲比任何地方都適合的重型核心。他們像我的gcc默認工作到處(包括最初的ARMv4T),而不是在任何地方工作,除了原創。我假設你添加了一些命令行選項,你會看到gcc編譯器的行爲與我已經演示的不同。

注意在這些例子中r3和r4沒有使用,爲什麼他們保存它們呢?這很可能是我提到的在堆棧中保持64位對齊的第一件事。如果對於所有的拇指變體解決方案,如果在彈出窗口之間發生中斷,則中斷處理程序正在處理未對齊的棧。由於r4無論​​如何都是一擲千金,它們可能分別彈出r1和r2或r2和r3,然後分別彈出bx r2或bx r3,並且沒有那個未對齊並保存指令的時刻。哦...

+0

彈出到PC是在ARMv5T和以後的互操作,在這個時間點有效地意味着「任何不是ARM7TDMI」,因爲這是關於只有v5之前的東西仍然可以在現代套件中找到。 – Notlikethat

+0

可以理解,但它仍然取決於編譯器編譯的內容,通用作品在任何地方的拇指(包括armv4t)還是不適用於任何地方。 –

+0

看到編輯,當然取決於首先構建gcc時的選項,但通用-mthumb似乎在任何地方都可以工作,所有的拇指變體,解決方案,如果指定拱然後它知道它可以彈出到pc而不是必須對bx做兩三步。 –