目標地址需要bx(和blx)指令的lsbit a 1,當它進入pc時,1被剝離。 b指令是pc相對的,arm文檔中顯示的數學表明它是平坦的。
一般而言,如果您讓這些工具完成工作,您無需隨時爲此擔心。
thumb.s
.thumb
.globl _start
_start:
b reset
nop
nop
.thumb_func
reset:
nop
nop
nop
nop
ldr r0,=reset
bx r0
然後
arm-none-eabi-as thumb.s -o thumb.o
arm-none-eabi-ld -Ttext=0x1000 thumb.o -o thumb.elf
arm-none-eabi-objdump -D thumb.elf
這給
thumb.elf: file format elf32-littlearm
Disassembly of section .text:
00001000 <_start>:
1000: e001 b.n 1006 <reset>
1002: 46c0 nop ; (mov r8, r8)
1004: 46c0 nop ; (mov r8, r8)
00001006 <reset>:
1006: 46c0 nop ; (mov r8, r8)
1008: 46c0 nop ; (mov r8, r8)
100a: 46c0 nop ; (mov r8, r8)
100c: 46c0 nop ; (mov r8, r8)
100e: 4801 ldr r0, [pc, #4] ; (1014 <reset+0xe>)
1010: 4700 bx r0
1012: 10070000 andne r0, r7, r0
...
分支需要照顧本身
1000: e001 b.n 1006 <reset>
...
00001006 <reset>:
分支中的編碼以16位量爲單位,而不是以字節爲單位,然後它們乘以2(移位它)以獲得總是偶數的字節地址。電腦從來不會奇怪,這是你喂bx或blx奇怪的值。
現在,因爲我在復位之前使用了.thumb_func,它告訴彙編器,這是一個拇指標籤,而不是臂標籤。所以當我說的時候,請將reset的地址加載到r0中,然後彙編器爲0x00001007分配一些數據,這在反彙編中顯得很奇怪,但它在那裏。他們已經爲我們樹立
00001006 <reset>:
...
100e: 4801 ldr r0, [pc, #4] ; (1014 <reset+0xe>)
1010: 4700 bx r0
1012: 10070000 andne r0, r7, r0
,所以LSb現在,如果你要刪除.thumb_func
100c: 46c0 nop ; (mov r8, r8)
100e: 4801 ldr r0, [pc, #4] ; (1014 <reset+0xe>)
1010: 4700 bx r0
1012: 10060000 andne r0, r6, r0
彙編認爲它是手臂地址,不設置和,所以LSb該代碼會崩潰。現在,如果你關心它,你總是可以添加額外的orr r0,#1,但這真的只是一個黑客。瞭解您正在使用的彙編程序如何將標籤聲明爲拇指標籤而不是手臂。是的,gnu彙編程序知道這段代碼段是拇指,這似乎很愚蠢,因爲我們告訴它,但它無法弄清楚拇指代碼中的標籤是...拇指標籤。非常愚蠢的工具。
而且我會假設還有其他更詳細的gnu彙編程序指令,這些指令也允許您聲明這個函數或拇指標籤或其他。當然,每個彙編程序都是不同的,所以不要假定gnu彙編程序指令適用於其他彙編程序指令。
如果你混合使用C和asm,C編譯器不是愚蠢的,它知道-mthumb使得所有的函數和全局變量(標籤)變成了拇指,並且取決於你在代碼中使用它們的方式和位置,鏈接器放置了正確的值。它甚至可以爲您正確切換模式,其中主要是手臂代碼的拇指代碼,並在切換模式的代碼中放置一個蹦牀。反之亦然,至少我已經看到這個工具可以做到這一點(並且在堆棧溢出答案中多次展示了它)。我不記得讓它工作與否是非常棘手的,你應該定期反編譯,並確保鏈接器爲你做這件事,否則就這樣做,或者你總是可以自己動手做。
所以
請記住,只有BX和BLX需要一套,所以LSb拇指和兩個個LSb重置分支武裝。 blx和bx指令將刪除該lsbit,並在pc中保留一個偶數編號的pc(非常簡單,請執行mov r0,pc然後用拇指代碼查看它)。
理想情況下,無條件和有條件的分支(而不是bx)不應該將模式分爲手臂和拇指。同樣的BL,但我已經看到GNU工具幫助了這一點,如果你想要你的代碼純然後加載地址在工具必須做的權利,否則整個工具鏈是一個失敗的寄存器,blx而不是BL該標籤並不依賴於爲你做蹦牀的工具鏈。