Thumb指令集之: ARM和Thumb的混合編程
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程序頭(ARMassemblerheader)。
基于以上原因,即使程序需要由Thumb代碼實(shí)現(xiàn),也必須通過(guò)ARM-Thumb互交(ARM-Thumbinterworking)進(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í),采用:
BXRn;
在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ǔ)器的固定地址取出字符串“helloworld”并輸出。
AREAHello,CODE,READONLY
SWI_WriteCEQU&0 ;軟中斷調(diào)用參數(shù)
SWI_ExitEQU&11 ;程序退出軟中斷調(diào)用參數(shù)
ENTRY
STARTADRr1,TEXT ;取字符串地址
LOOPLDRBr0,[r1],#1 ;取下一字節(jié)內(nèi)容
CMPr0,#0 ;判斷是否為字符串尾
SWINESWI_WriteC ;軟中斷調(diào)用打印字符
BENLOOP ;循環(huán)
SWISWI_Exit ;軟中斷調(diào)用退出程序執(zhí)行
TEXT=“HelloWorld”,&0a,&0d,0
END
下面的代碼將上面的ARM代碼轉(zhuǎn)換成等價(jià)的Thumb代碼。
AREAHelloW_Thumb,CODE,READONLY
SWI_WriteCEQU&0 ;軟中斷調(diào)用參數(shù)
SWI_ExitEQU&11 ;程序退出軟中斷調(diào)用參數(shù)
ENTRY ;程序入口點(diǎn)
CODE32進(jìn)入ARM狀態(tài)
ADRr0,START+1 ;取得Thumb代碼入口地址
BXr0 ;進(jìn)入Thumb代碼
CODE16 ;Thumb代碼入口點(diǎn)
STARTADRr1,TEXT ;r1->"HelloWorld"
LOOPLDRBr0,[r1] ;取下一字節(jié)內(nèi)容
ADDr1,r1,#1 ;地址指針加1**T
CMPr0,#0 ;判斷是否為字符串尾
BEQDONE ;完成?**T
SWISWI_WriteC ;如果不是字符串尾
BLOOP ;繼續(xù)循環(huán)
DONESWISWI_Exit ;程序退出
ALIGN ;字對(duì)齊
TEXTDATA
"HelloWorld",&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ò)“BXLR”指令來(lái)實(shí)現(xiàn)的。需要注意的是,在使用“BXLR”指令前,要對(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:CannotcallARMsymbol'arm_function'innon-interworkingobject
armsub.ofromTHUMBcodeinthumbmain.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(BranchandeXchange)
·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指令。
指令操作的偽代碼。
指令操作的偽代碼如下面程序段所示。
IfconditionPassed{cond}then
TFlag=Rm[0]
PC=RmAND0xfffffffe
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)寄存器,其操作方式和使用其他寄存器相同。
指令操作的偽代碼如下所示。
TFlag=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=addressoftheinstructionaftertheBLXinstruction
TFlag=1
PC=PC+PC=PC+(SignExtend(signed_immed_24)<<2)+(H<<1)
第二種格式BLX指令。
IfConditionPass{cond}then
LR=addressoftheinstructionafterthebranchinstruftion
TFlag=Rm[0]
PC=RmAND0xfffffffe
Thumb狀態(tài)下帶返回鏈接的跳轉(zhuǎn)指令BLX(1)提供了一種在Thumb狀態(tài)下無(wú)條件調(diào)用ARM子程序的方法,當(dāng)從子程序返回時(shí),通常使用下面的方式之一:
·BXLR
·加載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)。
指令操作的偽代碼為:
ifH==10then
LR=PC+(SignExtend(offset_11)<<12)
ElseifH==11then
PC=LR+(offset_11<<11)
LR=(addressofnextinstruction)|1
ElseifH==01then
PC=(LR+(offset_11<<1))AND0xFFFFFFFC
LR=(addressofnextinstruction)|1
ElseifH==01then
PC=(LR+(offset_11<<1))AND0Xfffffffc
LR=(addressofnextinstruction)|1
TFlag=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=(addressoftheinstructionafterthisBLX)|1
TFlag=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
AREAAddReg,CODE,READONLY ;段名為AddReg,屬性為READONLY
ENTRY ;程序入口
;SECTION1
main
ADRr0,ThumbProg+1 ;確定跳轉(zhuǎn)地址
;并將bit[0]置1
;使程序切換到thumb狀態(tài)
BXr0 ;程序跳轉(zhuǎn)并執(zhí)行狀態(tài)切換
;SECTION2
CODE16 ;Thumb代碼指示偽操作
ThumbProg
MOVr2,#2 ;r2=2
MOVr3,#3 ;r2=3
ADDr2,r2,r3 ;r2=r2+r3
ADRr0,ARMProg
BXr0 ;程序跳轉(zhuǎn)并執(zhí)行狀態(tài)切換
;SECTION3
CODE32 ;ARM代碼指示偽操作
ARMProg
MOVr4,#4
MOVr5,#5
ADDr4,r4,r5
;SECTION4
stopMOVr0,#0x18 ;設(shè)置semihosting軟中斷號(hào)
LDRr1,=0x20026 ;ADP_Stopped_ApplicationExit
SWI0x123456 ;ARMsemihostingSWI軟中斷調(diào)用
END ;文件結(jié)束
上面的例子分為4部分,通過(guò)下面的步驟編譯和運(yùn)行。
①使用文本編輯器,如notepad,輸入上面的代碼,并保存成文件addreg.s;
②在命令行中鍵入?yún)R編命令armasm–gaddreg.s;
③在命令行中鍵入鏈接命令armlinkaddreg.o-oaddreg;
④使用調(diào)試器(Debugger)(如,RealViewDebuggerorAXD)運(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代碼互交。
·BLXaddress
該指令跳轉(zhuǎn)到指令中指定的地址處執(zhí)行程序并進(jìn)行程序狀態(tài)切換,該地址是“PC相關(guān)的”,地址范圍為±32MB(ARM狀態(tài))或±4MB(Thumb狀態(tài))。
·BLXregister
在該中格式的跳轉(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ì)于葉子程序(leaffunction,即程序中沒(méi)有其他子程序調(diào)用的程序),編譯器將程序中的“MOVPC,LR”指令替換成“BXLR”指令,因?yàn)?ldquo;MOVPC”指令不能進(jìn)行狀態(tài)切換。
·對(duì)于非葉子程序,要進(jìn)行一系列的指令替換,如:
POP{r4,r5,pc}
替換為:
POP{r4,r5}
POP{r3}
BXr3
下面的例子顯示了一段帶子程序調(diào)用的C語(yǔ)言程序,使用--apcs/interwork選項(xiàng)進(jìn)行編譯時(shí),對(duì)代碼產(chǎn)生的影響。
C語(yǔ)言源程序。
Voidfunc(void)
{
….
Sub()
..
}
使用armcc--apcs/interwork選項(xiàng)進(jìn)行編譯產(chǎn)生結(jié)果如下。
Func
STMFDsp!,{r4-r7,lr}
….
BLsub
….
LDMFDsp!,{r4-r7,lr}
BXlr
使用tcc--apcs/interwork選項(xiàng)進(jìn)行編譯產(chǎn)生結(jié)果如下。
PUSH{r4-r7,lr}
….
BLsub
….
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>
externvoidarm_function(void);
intmain(void)
{
printf("HellofromThumbWorld\n");
arm_function();
printf("AndgoodbyefromThumbWorld\n");
return(0);
}
/*********************
*armsub.c*
**********************/
#include<stdio.h>
voidarm_function(void)
{
printf("HelloandGoodbyefromARMworld\n");
}
使用下面的命令對(duì)程序進(jìn)行編譯連接。
①編譯生成帶互交的Thumb代碼。
armcc--thumb-c-g--apcs/interwork-othumbmain.othumbmain.c
②編譯生成帶互交的ARM代碼。
armcc-c-g--apcs/interwork-oarmsub.oarmsub.c
③連接目標(biāo)文件。
armlinkthumbmain.oarmsub.o-othumbtoarm.axf
另外,可以使用--info選項(xiàng)使連接器輸出由于互交所增加的代碼大小。
armlinkarmsub.othumbmain.o-othumbtoarm.axf--infoveneers
輸出信息如下所示。
AddingVeneerstotheimage
AddingTAveneer(4bytes,Inline)forcallto'arm_function'fromthumbmain.o(.text).
AddingATveneer(8bytes,Inline)forcallto'__0printf'fromarmsub.o(.text).
AddingATveneer(8bytes,Inline)forcallto'__rt_lib_init'fromkernel.o(.text).
AddingATveneer(12bytes,Long)forcallto'__rt_lib_shutdown'fromkernel.o(.text).
AddingTAveneer(4bytes,Inline)forcallto'__rt_memclr_w'fromstdio.o(.text).
AddingTAveneer(4bytes,Inline)forcallto'__rt_raise'fromstdio.o(.text).
AddingTAveneer(8bytes,Short)forcallto'__rt_exit'fromexit.o(.text).
AddingTAveneer(4bytes,Inline)forcallto'__user_libspace'fromfree.o(.text).
AddingTAveneer(4bytes,Inline)forcallto'_fp_init'fromlib_init.o(.text).
AddingTAveneer(4bytes,Inline)forcallto'__heap_extend'frommalloc.o(.text).
AddingATveneer(8bytes,Inline)forcallto'__raise'fromrt_raise.o(.text).
AddingTAveneer(4bytes,Inline)forcallto'__rt_errno_addr'fromftell.o(.text).
12Veneer(s)(total72bytes)addedtotheimage.
(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代碼的功能指針的使用。
typedefint(*FN)();
myfunc(){
FNfnptrs[]={
(FN)(0x8084+1), //有效的Thumb地址
(FN)(0x8074) //無(wú)效的Thumb地址
};
FN*myfunctions=fnptrs;
myfunctions[0](); //調(diào)用成功
myfunctions[1](); //調(diào)用失敗
}