11.10 ARM和Thumb的混合編程
11.10.1 互交工作基礎(chǔ)
Thumb以其較高的代碼密度和在窄存儲(chǔ)器上的性能,使得它在很多系統(tǒng)中得到廣泛應(yīng)用。但在很多情況下,還是不得不使用ARM指令,這是因?yàn)椋?/p>
① ARM代碼比Thumb代碼有更快的執(zhí)行速度;
② ARM處理器的一些特定功能必須由ARM指令實(shí)現(xiàn),其中包括PSR指令、協(xié)處理器指令;
③ 異常發(fā)生時(shí),處理器自動(dòng)進(jìn)入ARM狀態(tài),如果異常處理程序需要使用Thumb指令也必須通用一個(gè)ARM程序頭(ARM assembler header)。
基于以上原因,即使程序需要由Thumb代碼實(shí)現(xiàn),也必須通過(guò)ARM-Thumb互交(ARM-Thumb interworking)進(jìn)入Thumb狀態(tài)。
ARM-Thumb互交是指對(duì)匯編語(yǔ)言和C/C++語(yǔ)言的ARM和Thumb代碼進(jìn)行連接的方法,它進(jìn)行兩種狀態(tài)(ARM和Thumb狀態(tài))間的切換。在進(jìn)行這種切換時(shí),有時(shí)需使用額外的代碼,這些代碼被稱(chēng)為Veneer。AAPCS定義了ARM和Thumb過(guò)程調(diào)用的標(biāo)準(zhǔn)。
從一個(gè)ARM例程調(diào)用一個(gè)Thumb例程,內(nèi)核必須進(jìn)行狀態(tài)切換。狀態(tài)的變化由CPSR的T位來(lái)顯示。在跳轉(zhuǎn)到一個(gè)例程時(shí)BX指令可用于ARM和Thumb狀態(tài)切換,具體用法如下。
在Thumb狀態(tài)調(diào)用ARM例程時(shí),采用:
BX Rn;
在ARM狀態(tài)調(diào)用Thumb例程時(shí),采用:
BX{cond} Rn;
其中,Rn可以是r0~r15中的任意寄存器。
這種帶狀態(tài)切換的跳轉(zhuǎn)指令BX,將寄存器Rn的內(nèi)容拷貝到程序計(jì)數(shù)器寄存器PC,因此可以實(shí)現(xiàn)4G空間的跳轉(zhuǎn)。指令根據(jù)寄存器Rn的bit[0]來(lái)決定處理器是否進(jìn)行狀態(tài)切換,詳細(xì)內(nèi)容參見(jiàn)ARM指令一節(jié)。
下面是一段ARM程序,該程序調(diào)用虛擬的SWI_writeC子程序從存儲(chǔ)器的固定地址取出字符串“hello world”并輸出。
AREA Hello,CODE,READONLY
SWI_WriteC EQU &0 ;軟中斷調(diào)用參數(shù)
SWI_Exit EQU &11 ;程序退出軟中斷調(diào)用參數(shù)
ENTRY
START ADR r1,TEXT ;取字符串地址
LOOP LDRB r0,[r1],#1 ;取下一字節(jié)內(nèi)容
CMP r0,#0 ;判斷是否為字符串尾
SWINE SWI_WriteC ;軟中斷調(diào)用打印字符
BEN LOOP ;循環(huán)
SWI SWI_Exit ;軟中斷調(diào)用退出程序執(zhí)行
TEXT = “Hello World”,&0a,&0d,0
END
下面的代碼將上面的ARM代碼轉(zhuǎn)換成等價(jià)的Thumb代碼。
AREA HelloW_Thumb,CODE,READONLY
SWI_WriteC EQU &0 ;軟中斷調(diào)用參數(shù)
SWI_Exit EQU &11 ;程序退出軟中斷調(diào)用參數(shù)
ENTRY ;程序入口點(diǎn)
CODE32 進(jìn)入ARM狀態(tài)
ADR r0, START+1 ;取得Thumb代碼入口地址
BX r0 ;進(jìn)入Thumb代碼
CODE16 ;Thumb代碼入口點(diǎn)
START ADR r1, TEXT ;r1 -> "Hello World"
LOOP LDRB r0, [r1] ;取下一字節(jié)內(nèi)容
ADD r1, r1, #1 ;地址指針加1 **T
CMP r0, #0 ;判斷是否為字符串尾
BEQ DONE ;完成? **T
SWI SWI_WriteC ;如果不是字符串尾
B LOOP ;繼續(xù)循環(huán)
DONE SWI SWI_Exit ;程序退出
ALIGN ;字對(duì)齊
TEXT DATA
"Hello World",&0a,&0d,&00
END
上例中,ARM代碼到Thumb代碼轉(zhuǎn)換過(guò)程中新增加的指令用“**T”標(biāo)注。
在實(shí)現(xiàn)ARM代碼和Thumb代碼轉(zhuǎn)換時(shí),大部分的ARM指令有等價(jià)的Thumb指令,只有少數(shù)指令沒(méi)有。如加載字節(jié)指令(LDR)不支持自動(dòng)變址,軟中斷指令不能條件執(zhí)行。
在編寫(xiě)Thumb代碼時(shí)要注意以下幾點(diǎn)。
① 匯編器需要知道什么時(shí)候產(chǎn)生ARM代碼、什么時(shí)候產(chǎn)生Thumb代碼,程序中使用CODE32和CODE16偽操作提供給編譯器這些信息。
② 由于處理器上電執(zhí)行是在ARM狀態(tài)下完成的,所以要使用Thumb指令必須由ARM指令調(diào)用Thumb指令,這一過(guò)程是通過(guò)“BX LR”指令來(lái)實(shí)現(xiàn)的。需要注意的是,在使用“BX LR”指令前,要對(duì)寄存器LR做正確的初始化。
③ 在ARM和Thumb混合編程時(shí),常使用ALIGN偽操作保證內(nèi)存地址對(duì)齊。
11.10.2 互交子程序
編寫(xiě)ARM/Thumb互交代碼時(shí),下面兩點(diǎn)需要注意。
① 對(duì)于C/C++子程序而言,只要在編譯時(shí)指定--apcs/interwork選項(xiàng),匯編器會(huì)生成合適的返回代碼,使得程序返回到和調(diào)用程序相同的狀態(tài)。
② 在匯編語(yǔ)言子程序中,用戶(hù)必須自己編寫(xiě)相應(yīng)的返回代碼,使得程序返回到和調(diào)用程序相同的狀態(tài)。
如果目標(biāo)代碼包含以下內(nèi)容,應(yīng)該在編譯或匯編時(shí)使用--apcs/interwork選項(xiàng)使處理器能夠在ARM和Thumb代碼間進(jìn)行正確的切換,這種情況包含以下4種。
① 需要返回到ARM狀態(tài)的Thumb子程序。
② 需要返回到Thumb狀態(tài)的ARM子程序。
③ 間接調(diào)用ARM子程序的Thumb子程序。
④ 間接調(diào)用Thumb子程序的ARM子程序。
如果在程序連接階段,連接器發(fā)現(xiàn)ARM子程序和Thumb子程序間存在相互調(diào)用,而源文件在編譯時(shí)沒(méi)有使用--apcs/interwork選項(xiàng),則連接器將報(bào)告以下錯(cuò)誤。
Error: L6239E: Cannot call ARM symbol 'arm_function' in non-interworking object
armsub.o from THUMB code in thumbmain.o(.text)
其中,“arm_function”為需要進(jìn)行狀態(tài)切換的子程序名。
在這種情況下,用戶(hù)必須使用--apcs/interwork選項(xiàng)重新對(duì)源文件進(jìn)行編譯。
但在下面兩種情況下,不必指定--apcs/interwork選項(xiàng)。
① 在Thumb狀態(tài)下,發(fā)生異常中斷時(shí),處理器自動(dòng)切換到ARM狀態(tài),這時(shí)不需要添加狀態(tài)切換代碼。
② 當(dāng)異常發(fā)生在Thumb狀態(tài)時(shí),從異常返回不需要添加狀態(tài)切換的Veneer代碼。
1.使用匯編語(yǔ)言實(shí)現(xiàn)互交
對(duì)于匯編程序來(lái)說(shuō),可以有兩種方法來(lái)實(shí)現(xiàn)程序狀態(tài)的切換。第一種方法是利用連接器提供的交互子程序Veneer來(lái)實(shí)現(xiàn)程序狀態(tài)的切換,這時(shí)用戶(hù)可以使用指令BL來(lái)調(diào)用子程序;另一種方法是用戶(hù)自己編寫(xiě)狀態(tài)切換的程序。
在ARMv4版本及其以前的版本中,可以使用BX指令實(shí)現(xiàn)程序狀態(tài)的切換。
從ARMv5版本開(kāi)始,下面的指令也可以用來(lái)實(shí)現(xiàn)程序的狀態(tài)切換。
· BX(Branch and eXchange)
· BLX、LDR、LDM和POP
下面的兩個(gè)偽操作用來(lái)區(qū)分源程序中的ARM代碼和Thumb代碼。
· CODE16
· CODE32
下面簡(jiǎn)單介紹用于狀態(tài)切換的指令和偽操作,更詳細(xì)的信息請(qǐng)分別參見(jiàn)相關(guān)章節(jié)。
(1)BX指令
ARM狀態(tài)下的BX指令,使程序跳轉(zhuǎn)到指令中指定的參數(shù)Rm指定的地址執(zhí)行程序,Rm的第0位拷貝到CPSR中T位,位[31∶1]移入PC。若Rm的bit[0]為1,則跳轉(zhuǎn)時(shí)自動(dòng)將CPSR中的標(biāo)志位T置位,即把目標(biāo)地址的代碼解釋為T(mén)humb代碼;若Rm的位bit[0]為0,則跳轉(zhuǎn)時(shí)自動(dòng)將CPSR中的標(biāo)志位T復(fù)位,即把目標(biāo)地址代碼解釋為ARM代碼。指令的語(yǔ)法格式如下。
BX{<cond>} <Rm>
① <cond>
為指令編碼中的條件域。它指示指令在什么條件下執(zhí)行。當(dāng)<cond>忽略時(shí),指令為無(wú)條件執(zhí)行(cond=AL(Alway))。
② <Rm>
包含跳轉(zhuǎn)指令的目標(biāo)地址。如果Rm的bit[0]=0,目標(biāo)地址處指令為ARM指令;如果Rm的bit[0]=1,目標(biāo)地址處指令為T(mén)humb指令。
指令操作的偽代碼。
指令操作的偽代碼如下面程序段所示。
If conditionPassed{cond} then
T Flag=Rm[0]
PC = Rm AND 0xfffffffe
Thumb狀態(tài)下的BX指令,用于ARM和Thumb代碼間的相互調(diào)用。
指令的語(yǔ)法格式。
BX <Rm>
其中<Rm>為目標(biāo)地址寄存器,包含程序的跳轉(zhuǎn)地址。BX指令的目標(biāo)地址寄存器可以是r0~r15中的任意寄存器。
注意 |
如果Rm[1:0]=0b10,不滿(mǎn)足ARM指令的內(nèi)存對(duì)齊方式。指令的執(zhí)行結(jié)果不可預(yù)知。如果該指令使用r15作為目標(biāo)寄存器,其操作方式和使用其他寄存器相同。 |
指令操作的偽代碼如下所示。
T Flag=Rm[0]
PC=Rm[31:1]<<1
ARM指令集中的BX指令和Thumb指令集中的BX指令相差較大,它們分別為不同方向的跳轉(zhuǎn)。當(dāng)r15作為目的寄存器使用時(shí),要特別注意該指令在兩個(gè)指令集中的區(qū)別。
(2)BLX指令
ARM狀態(tài)下的BLX指令使用一個(gè)寄存器中的絕對(duì)地址或標(biāo)號(hào),用于使程序跳轉(zhuǎn)到Thumb狀態(tài)或從Thumb狀態(tài)返回,該指令用分支寄存器的最低位來(lái)更新CPSR中的T位,并將返回地址寫(xiě)入到連接寄存器LR中。
指令的語(yǔ)法格式如下所示:
① BLX <target_addr>
② BLX{<cond>} <Rm>
第一種格式中,轉(zhuǎn)移目標(biāo)按下述方法計(jì)算。將指令中指定的24位偏移量進(jìn)行符號(hào)擴(kuò)展,左移兩位形成字偏移量,然后將其累加進(jìn)程序計(jì)數(shù)器PC中。這時(shí),程序計(jì)數(shù)器的內(nèi)容為BX指令地址加8字節(jié)。位H(bit[24])也加到結(jié)果地址的第一位(bit[1]),使目標(biāo)地址為半字地址,以執(zhí)行接下來(lái)的Thumb指令。計(jì)算偏移量的工作一般由ARM匯編器來(lái)完成。這種形式的跳轉(zhuǎn)指令只能實(shí)現(xiàn)±32MB空間的跳轉(zhuǎn)。
第二種格式中,寄存器Rm指定轉(zhuǎn)移目標(biāo),Rm的第0位拷貝到CPSR中的T位,bit[31:0]移入PC。
· 如果Rm的bit[0]=1,則跳轉(zhuǎn)時(shí)自動(dòng)將CPSR中的標(biāo)志位T置位,即把目標(biāo)地址的代碼解釋為T(mén)humb代碼。
· 如果Rm的bit[0]=0,則跳轉(zhuǎn)時(shí)自動(dòng)將CPSR中的標(biāo)志位T復(fù)位,即把目標(biāo)地址的代碼解釋為ARM代碼。
指令操作的偽代碼如下面程序段所示。
第一種格式BLX指令。
LR=address of the instruction after the BLX instruction
T Flag=1
PC=PC + PC = PC + (SignExtend(signed_immed_24)<<2) + (H<<1)
第二種格式BLX指令。
If ConditionPass{cond} then
LR = address of the instruction after the branch instruftion
T Flag=Rm[0]
PC=Rm AND 0xfffffffe
Thumb狀態(tài)下帶返回鏈接的跳轉(zhuǎn)指令BLX(1)提供了一種在Thumb狀態(tài)下無(wú)條件調(diào)用ARM子程序的方法,當(dāng)從子程序返回時(shí),通常使用下面的方式之一:
· BX LR
· 加載PC的LDR或LDM指令。
BLX指令不可條件執(zhí)行,可以實(shí)現(xiàn)在大約±4MB的地址空間范圍內(nèi)跳轉(zhuǎn),實(shí)現(xiàn)方法是將一條BLX指令編譯成兩條16位的Thumb指令,從而實(shí)現(xiàn)上述跳轉(zhuǎn)。對(duì)編譯后的兩條指令說(shuō)明如下。
① H=10的跳轉(zhuǎn)指令。該跳轉(zhuǎn)包含跳轉(zhuǎn)偏移量的高位部分。
② H=01的跳轉(zhuǎn)指令。該跳轉(zhuǎn)包含跳轉(zhuǎn)偏移量的低位部分。
指令的語(yǔ)法格式
BL <target_address>
<target_address>
指定程序跳轉(zhuǎn)的目標(biāo)地址。指令通過(guò)下面的方法計(jì)算目標(biāo)地址。
· 將H=10的BL指令的offset_11域左移12位。
· 將結(jié)果符號(hào)擴(kuò)展為32位。
· 將得到的值加到PC寄存器中。
· 與H=11的BL指令的offset_11域相加。
因此BL指令可以實(shí)現(xiàn)在大約±4MB的地址空間范圍內(nèi)跳轉(zhuǎn)。
指令操作的偽代碼為:
if H==10 then
LR=PC+(SignExtend(offset_11)<<12)
Else if H==11 then
PC=LR+(offset_11<<11)
LR=(address of next instruction)|1
Else if H==01 then
PC=(LR+(offset_11<<1)) AND 0xFFFFFFFC
LR=(address of next instruction)|1
Else if H==01 then
PC=(LR+(offset_11<<1)) AND 0Xfffffffc
LR=(address of next instruction)|1
T Flag=0
另外Thumb狀態(tài)下包含另一種格式的BLX指令,該BLX(2)指令用于ARM和Thumb子程序間的相互調(diào)用。程序狀態(tài)字的T標(biāo)志位根據(jù)目的寄存器的bit[0]位而改變。
指令的語(yǔ)法格式為:
BLX <Rm>
其中<Rm>為目標(biāo)地址寄存器,r0~r14寄存器均可以作為目標(biāo)地址寄存器。
注意 |
如果在此指令中使用r15作為目的寄存器,指令的執(zhí)行結(jié)果不可預(yù)知。 |
此指令只在ARMv5版本以上指令集中被支持。
指令操作的偽代碼為:
LR = (address of the instruction after this BLX)|1
T Flag = Rm[0]
PC = Rm[31:1]<<1
(3)匯編偽指令
匯編編譯器可以產(chǎn)生ARM代碼也可以產(chǎn)生Thumb代碼。使用--thumb或--16選項(xiàng)指示編譯器產(chǎn)生Thumb代碼。由于所有支持Thumb代碼的ARM處理器都從ARM狀態(tài)開(kāi)始執(zhí)行,所以必須人為地使用BX或BLX指令,使處理器狀態(tài)切換到Thumb狀態(tài)并使用下面的偽操作使編譯器產(chǎn)生Thumb代碼。
· CODE16
· CODE32
CODE32偽操作指示匯編器后面的指令為32位的ARM指令。
ARM和CODE32偽操作的意義相同。
當(dāng)匯編器對(duì)源程序進(jìn)行編譯時(shí),如果需要,將會(huì)在程序中插入空指令,以保證內(nèi)存單元字對(duì)齊。
語(yǔ)法格式如下。
ARM
CODE32
使用在同時(shí)包含ARM指令和Thumb指令的源文件中。當(dāng)需要從ARM指令序列切換到Thumb指令序列時(shí),使用偽操作ARM(或CODE32);當(dāng)需要從Thumb指令序列切換到ARM指令序列時(shí)使用Thumb偽操作。ARM(或CODE32)偽操作只是指示匯編器后面的指令類(lèi)型是ARM指令。該偽操作本身并不進(jìn)行程序狀態(tài)的切換,要進(jìn)行狀態(tài)切換,可以使用BX指令操作。
CODE16偽指令通知編譯器,其后的指令序列為16位的Thumb指令。
語(yǔ)法格式如下。
CODE16
若在匯編源程序中同時(shí)包含ARM指令和Thumb指令時(shí),可用CODE16偽指令通知編譯器其后的指令序列為16位的Thumb指令。
(4)編程實(shí)例
PRESERVE8
AREA AddReg,CODE,READONLY ;段名為AddReg,屬性為READONLY
ENTRY ;程序入口
; SECTION 1
main
ADR r0, ThumbProg + 1 ;確定跳轉(zhuǎn)地址
;并將bit[0]置1
;使程序切換到thumb狀態(tài)
BX r0 ;程序跳轉(zhuǎn)并執(zhí)行狀態(tài)切換
; SECTION 2
CODE16 ;Thumb代碼指示偽操作
ThumbProg
MOV r2, #2 ;r2 = 2
MOV r3, #3 ;r2 = 3
ADD r2, r2, r3 ;r2 = r2 + r3
ADR r0, ARMProg
BX r0 ;程序跳轉(zhuǎn)并執(zhí)行狀態(tài)切換
; SECTION 3
CODE32 ;ARM代碼指示偽操作
ARMProg
MOV r4, #4
MOV r5, #5
ADD r4, r4, r5
; SECTION 4
stop MOV r0, #0x18 ;設(shè)置semihosting軟中斷號(hào)
LDR r1, =0x20026 ;ADP_Stopped_ApplicationExit
SWI 0x123456 ;ARM semihosting SWI軟中斷調(diào)用
END ;文件結(jié)束
上面的例子分為4部分,通過(guò)下面的步驟編譯和運(yùn)行。
① 使用文本編輯器,如notepad,輸入上面的代碼,并保存成文件addreg.s;
② 在命令行中鍵入?yún)R編命令 armasm –g addreg.s;
③ 在命令行中鍵入鏈接命令 armlink addreg.o -o addreg;
④ 使用調(diào)試器(Debugger)(如,RealView Debuggeror AXD)運(yùn)行映像文件??梢允褂脝尾綀?zhí)行,觀察代碼在Thumb狀態(tài)下的執(zhí)行。
注意 |
Thumb代碼的地址標(biāo)號(hào)如果用偽操作export聲明為“外部的”,則連接器會(huì)自動(dòng)調(diào)整該地址標(biāo)號(hào)使其bit[0]等于1;如果該地址標(biāo)號(hào)沒(méi)有被聲明為“外部的”,則使用者必須手動(dòng)地對(duì)標(biāo)號(hào)進(jìn)行調(diào)整,如上例中的ThumProg+1。 |
(5)ARMv5架構(gòu)下的狀態(tài)切換
在ARMv5體系結(jié)構(gòu)的指令集中,增加了下面兩條指令用于ARM和Thumb代碼互交。
· BLX address
該指令跳轉(zhuǎn)到指令中指定的地址處執(zhí)行程序并進(jìn)行程序狀態(tài)切換,該地址是“PC相關(guān)的”,地址范圍為±32MB(ARM狀態(tài))或±4MB(Thumb狀態(tài))。
· BLX register
在該中格式的跳轉(zhuǎn)指令中寄存器Rm指定轉(zhuǎn)移目標(biāo),Rm的第0位拷貝到CPSR中的T位,bit[31:0]移入PC。
使用上面兩條指令,在執(zhí)行程序跳轉(zhuǎn)之前,處理器自動(dòng)將返回連接寄存器LR的bit[0]位更新為CPSR寄存器的T位,所以無(wú)論處理器狀態(tài)是否發(fā)生變化,程序都能正確返回。
當(dāng)使用LDR、LDM及POP指令向PC寄存器中賦值時(shí),寄存器CPSR中的Thumb位將被設(shè)置成PC寄存器的bit[0],這時(shí)就實(shí)現(xiàn)了程序狀態(tài)的切換。這種方法在子程序的返回時(shí)非常有效,同樣的指令可以根據(jù)需要返回到ARM狀態(tài)或Thumb狀態(tài)。
連接器在對(duì)目標(biāo)代碼進(jìn)行連續(xù)時(shí),將代碼中的地址標(biāo)號(hào)分為3類(lèi)。
· ARM指令地址標(biāo)號(hào)。
· Thumb指令地址標(biāo)號(hào)。
· 數(shù)據(jù)(Data)地址標(biāo)號(hào)。
當(dāng)連接器重定位Thumb代碼中的地址標(biāo)號(hào)時(shí),地址標(biāo)號(hào)的bit[0]位將被自動(dòng)設(shè)置為1。這就意味著跳轉(zhuǎn)指令(這些指令包括BX、BLX和LDR)可以根據(jù)目標(biāo)地址正確的進(jìn)行狀態(tài)切換。
注意 |
上面提到的連接器自動(dòng)設(shè)置目的地址的行為,只有在ARMv5及其以上版本中支持。 |
2.使用C和C++語(yǔ)言實(shí)現(xiàn)互交
對(duì)于不同的C和C++源程序,可以有些程序中包含ARM指令,有些程序中包含Thumb指令,這些程序可以相互調(diào)用,只是在編譯這些程序時(shí)指定--apcs/interwork選項(xiàng)。當(dāng)使用了--apcs/interwork選項(xiàng),編譯器會(huì)自動(dòng)進(jìn)行一些相應(yīng)處理;連接器在檢測(cè)到程序中存在互交工作時(shí),會(huì)生成一些用于程序狀態(tài)切換的代碼。
(1)代碼編譯
可以使用下面的指令,將C或C++程序編譯為可以執(zhí)行互交的目標(biāo)代碼。
armcc --c90 --thumb --apcs /interwork
armcc --c90 --arm --apcs /interwork
armcc --cpp --thumb --apcs /interwork
armcc --cpp --arm --apcs /interwork
注意 |
--cpp是C++文件(文件后綴為.cpp)默認(rèn)的編譯選項(xiàng)。 |
使用--apcs/interwork選項(xiàng)對(duì)文件進(jìn)行編譯時(shí),編譯器會(huì)進(jìn)行如下處理。
· 對(duì)于葉子程序(leaf function,即程序中沒(méi)有其他子程序調(diào)用的程序),編譯器將程序中的“MOV PC,LR”指令替換成“BX LR”指令,因?yàn)?ldquo;MOV PC”指令不能進(jìn)行狀態(tài)切換。
· 對(duì)于非葉子程序,要進(jìn)行一系列的指令替換,如:
POP {r4,r5,pc}
替換為:
POP {r4,r5}
POP {r3}
BX r3
下面的例子顯示了一段帶子程序調(diào)用的C語(yǔ)言程序,使用--apcs/interwork選項(xiàng)進(jìn)行編譯時(shí),對(duì)代碼產(chǎn)生的影響。
C語(yǔ)言源程序。
Void func(void)
{
….
Sub()
..
}
使用armcc --apcs/interwork選項(xiàng)進(jìn)行編譯產(chǎn)生結(jié)果如下。
Func
STMFD sp!,{r4-r7,lr}
….
BL sub
….
LDMFD sp!, {r4-r7,lr}
BX lr
使用tcc --apcs/interwork選項(xiàng)進(jìn)行編譯產(chǎn)生結(jié)果如下。
PUSH {r4-r7,lr}
….
BL sub
….
POP {r4-r7}
POP {r3}
BX
(2)C語(yǔ)言的互交實(shí)例
下面的例子顯示了一個(gè)Thumb狀態(tài)下的代碼通過(guò)互交調(diào)用ARM子程序;而后又在ARM子程序中調(diào)用Thumb指令集的庫(kù)函數(shù)printf()。
/*********************
* thumbmain.c *
**********************/
#include <stdio.h>
extern void arm_function(void);
int main(void)
{
printf("Hello from Thumb Worldn");
arm_function();
printf("And goodbye from Thumb Worldn");
return (0);
}
/*********************
* armsub.c *
**********************/
#include <stdio.h>
void arm_function(void)
{
printf("Hello and Goodbye from ARM worldn");
}
使用下面的命令對(duì)程序進(jìn)行編譯連接。
① 編譯生成帶互交的Thumb代碼。
armcc --thumb -c -g --apcs /interwork -o thumbmain.o thumbmain.c
② 編譯生成帶互交的ARM代碼。
armcc -c -g --apcs /interwork -o armsub.o armsub.c
③ 連接目標(biāo)文件。
armlink thumbmain.o armsub.o -o thumbtoarm.axf
另外,可以使用--info選項(xiàng)使連接器輸出由于互交所增加的代碼大小。
armlink armsub.o thumbmain.o -o thumbtoarm.axf --info veneers
輸出信息如下所示。
Adding Veneers to the image
Adding TA veneer(4 bytes, Inline) for call to'arm_function'from thumbmain.o(.text).
Adding AT veneer (8 bytes, Inline) for call to '__0printf' from armsub.o(.text).
Adding AT veneer (8 bytes, Inline) for call to '__rt_lib_init' from kernel.o(.text).
Adding AT veneer (12 bytes,Long) for call to'__rt_lib_shutdown'from kernel.o(.text).
Adding TA veneer (4 bytes, Inline) for call to '__rt_memclr_w' from stdio.o(.text).
Adding TA veneer (4 bytes, Inline) for call to '__rt_raise' from stdio.o(.text).
Adding TA veneer (8 bytes, Short) for call to '__rt_exit' from exit.o(.text).
Adding TA veneer (4 bytes, Inline) for call to '__user_libspace' from free.o(.text).
Adding TA veneer (4 bytes, Inline) for call to '_fp_init' from lib_init.o(.text).
Adding TA veneer (4 bytes, Inline) for call to '__heap_extend' from malloc.o(.text).
Adding AT veneer (8 bytes, Inline) for call to '__raise' from rt_raise.o(.text).
Adding TA veneer (4 bytes, Inline) for call to '__rt_errno_addr' from ftell.o(.text).
12 Veneer(s) (total 72 bytes) added to the image.
(3)Thumb狀態(tài)下的功能指針
任何指向Thumb函數(shù)(由Thumb指令完成的功能函數(shù)并且其返回狀態(tài)也為T(mén)humb狀態(tài))的指針,其最低有效位(LSB)必為1。
當(dāng)重定位Thumb代碼中的地址標(biāo)號(hào)時(shí),連接器將自動(dòng)設(shè)置地址的最低有效位。如果在程序中使用絕對(duì)地址,連接器將無(wú)法完成該設(shè)置。因此,如果在Thumb代碼中使用絕對(duì)地址時(shí),必須手工設(shè)置為其地址加1。
下面的例子顯示了Thumb代碼的功能指針的使用。
typedef int (*FN)();
myfunc() {
FN fnptrs[] = {
(FN)(0x8084 + 1), // 有效的Thumb地址
(FN)(0x8074) // 無(wú)效的Thumb地址
};
FN* myfunctions = fnptrs;
myfunctions[0](); // 調(diào)用成功
myfunctions[1](); // 調(diào)用失敗
}