2017-04-07 76 views
1

我正在爲需要鏈接到現有THUMB代碼的嵌入式核心(ARM7TDMI)編寫THUMB代碼。我正在使用GNU ARM嵌入式工具鏈(link)。我無法讓鏈接器將現有的外部代碼視爲THUMB;它似乎總是認爲它是ARM。我鏈接到的現有代碼絕對是靜態的,不能更改/重新編譯(基本上,它是一個位於ROM芯片上的普通二進制文件)。如何鏈接到外部THUMB代碼?

下面是一個例子程序,multiply.c,演示該問題:

extern int externalFunction(int x); 
int multiply(int x, int y) 
{ 
    return externalFunction(x * y); 
} 

使用編譯:

arm-none-eabi-gcc -o multiply.o -c -O3 multiply.c -march=armv4t -mtune=arm7tdmi -mthumb 
arm-none-eabi-ld -o linked.o multiply.o -T symbols.txt 

symbols.txt是一個簡單的鏈接腳本:

SECTIONS 
{ 
    .text 0x8000000 : { *(.text) } 
} 
externalFunction = 0x8002000; 

當我objdump -d linked.o,我得到:

08000000 <multiply>: 
8000000:  b510   push {r4, lr} 
8000002:  4348   muls r0, r1 
8000004:  f000 f804  bl  8000010 <__externalFunction_from_thumb> 
8000008:  bc10   pop  {r4} 
800000a:  bc02   pop  {r1} 
800000c:  4708   bx  r1 
800000e:  46c0   nop      ; (mov r8, r8) 

08000010 <__externalFunction_from_thumb>: 
8000010:  4778   bx  pc 
8000012:  46c0   nop      ; (mov r8, r8) 
8000014:  ea0007f9  b  8002000 <externalFunction> 

它不是直接分支到0x8002000,而是分支到一個存根,它先切換到ARM模式,然後在ARM模式下分支到0x8002000。我想是BL直接跳轉到0x8002000並留在Thumb模式,讓我得到這個代替:

08000000 <multiply>: 
8000000:  b510   push {r4, lr} 
8000002:  4348   muls r0, r1 
8000004:  ???? ????  bl  8002000 <__externalFunction> 
8000008:  bc10   pop  {r4} 
800000a:  bc02   pop  {r1} 
800000c:  4708   bx  r1 

ABI和調用約定問題放在一邊,我該如何實現這一目標?

+0

最明顯的事情,試圖將'externalFunction = 0x8002001;' 。 –

+0

我使用'0x8002001'獲得與以前相同的輸出:它實際上在清除位後進行彙編。它似乎_very_致力於確保它是一個ARM調用... –

+1

似乎有一個關於這個報告的錯誤,https://sourceware.org/bugzilla/show_bug.cgi?id=15302,但不幸的是它的修復僅適用於您沒有使用的僅限拇指目標。如果您不介意非鏈接腳本解決方案,您可以這樣做:'static int(* const externalFunction)(int x)=(int(*)(int))0x80002001;' –

回答

1

一個辦法做到這一點是讓它做你想要什麼

branchto.s

.thumb 
.thumb_func 
.globl branchto 
branchto: 
    bx r0 

so.c

extern unsigned int externalFunction; 
extern int branchto (unsigned int, int); 
int fun (int x) 
{ 
    return(branchto(externalFunction,x)+3); 
} 

so.ld

SECTIONS 
{ 
    .text 0x8000000 : { *(.text) } 
} 
externalFunction = 0x8002001; 

生產

08000000 <fun>: 
8000000: 4b04  ldr r3, [pc, #16] ; (8000014 <fun+0x14>) 
8000002: b510  push {r4, lr} 
8000004: 0001  movs r1, r0 
8000006: 6818  ldr r0, [r3, #0] 
8000008: f000 f806 bl 8000018 <branchto> 
800000c: 3003  adds r0, #3 
800000e: bc10  pop {r4} 
8000010: bc02  pop {r1} 
8000012: 4708  bx r1 
8000014: 08002001 stmdaeq r0, {r0, sp} 

08000018 <branchto>: 
8000018: 4700  bx r0 

羅斯里奇在評論解決方案工作

static int (* const externalFunction)(int x) = (int (*)(int)) 0x80002001; 
int fun (int x) 
{ 
    return((* externalFunction)(x)+3); 
} 

但是硬編碼地址是在代碼不是鏈接腳本,如果該事項,試圖解決這一點,不可能。

08000000 <fun>: 
8000000: b510  push {r4, lr} 
8000002: 4b03  ldr r3, [pc, #12] ; (8000010 <fun+0x10>) 
8000004: f000 f806 bl 8000014 <fun+0x14> 
8000008: 3003  adds r0, #3 
800000a: bc10  pop {r4} 
800000c: bc02  pop {r1} 
800000e: 4708  bx r1 
8000010: 80002001 andhi r2, r0, r1 
8000014: 4718  bx r3 
8000016: 46c0  nop   ; (mov r8, r8) 

我更喜歡這樣的裝配解決方案來強制我想要的確切指令。當然,如果你在外部函數中鏈接了它,它應該會/應該剛剛工作(有一些例外,但是gnu在鏈接器中解決了你進出手臂/拇指的問題)。

我不認爲它是一個實際的gnu錯誤,而是他們需要在鏈接描述文件中將該變量聲明爲拇指函數地址,而不僅僅是一些通用鏈接器定義變量(同樣也是一個手臂函數地址)。就像 。thumb_func做(或更長的功能/過程聲明)

.word branchto 

.thumb 
.globl branchto 
branchto: 
    bx r0 

8000018: 0800001c stmdaeq r0, {r2, r3, r4} 

0800001c <branchto>: 
800001c: 4700  bx r0 


.word branchto 

.thumb 
.thumb_func 
.globl branchto 
branchto: 
    bx r0 

8000018: 0800001d stmdaeq r0, {r0, r2, r3, r4} 

0800001c <branchto>: 
800001c: 4700  bx r0 

僅僅閱讀GNU鏈接的文件有可能被希望能得到你想要的東西

SECTIONS 
{ 
    .text0 0x08000000 : { so.o } 
    .text1 0x08002000 (NOLOAD) : { ex.o } 
} 

ex.o從虛擬函數對正在添加讓每個人都開心

int externalFunction (int x) 
{ 
    return(x); 
} 

08000000 <fun>: 
8000000: b510  push {r4, lr} 
8000002: f001 fffd bl 8002000 <externalFunction> 
8000006: 3003  adds r0, #3 
8000008: bc10  pop {r4} 
800000a: bc02  pop {r1} 
800000c: 4708  bx r1 

和NOLOAD保持二進制的虛擬函數。

arm-none-eabi-objcopy so.elf -O srec --srec-forceS3 so.srec 

S00A0000736F2E7372656338 
S3150800000010B501F0FDFF033010BC02BC0847C0461E 
S315080000104743433A2028474E552920362E322E305C 
S31508000020004129000000616561626900011F000046 
S3150800003000053454000602080109011204140115CA 
S31008000040011703180119011A011E021E 
S70500000000FA 

注意到它不是完美有額外的垃圾是得到了拉昇的,或許符號

08000000 <fun>: 
8000000: b510  push {r4, lr} 
8000002: f001 fffd bl 8002000 <externalFunction> 
8000006: 3003  adds r0, #3 
8000008: bc10  pop {r4} 
800000a: bc02  pop {r1} 
800000c: 4708  bx r1 
800000e: 46c0  nop   ; (mov r8, r8) 
8000010: 3a434347  
8000014: 4e472820  
8000018: 36202955  
800001c: 302e322e  
8000020: 00294100  
8000024: 65610000  
8000028: 00696261  
800002c: 00001f01  
8000030: 54340500  
8000034: 08020600  
8000038: 12010901  
800003c: 15011404  
8000040: 18031701  
8000044: 1a011901  

,你可以在SREC看到,但0x08002000代碼是不是有那麼您的實際外部函數打電話。

如果你不想要任何asm,我只要做出你想要的指令或函數指針就可以了。

0

使用長分支機構的其他評論/回答可以正常工作,但直接BL呼叫並避免不必要的負載仍然很好。

我相信我找到了解決方法here。創建一個虛擬文件(姑且稱之爲ext.c)有:

__attribute__((naked)) int externalFunction(int x){} 

編譯該文件ext.o(同樣的方式編譯multiply.c)。這會產生一個虛擬的對象文件與externalFunction裝飾正確的函數符號,其地址被鏈接器腳本覆蓋,從而產生所需的BL指令:

Disassembly of section .text: 

08000000 <multiply>: 
8000000:  b510   push {r4, lr} 
8000002:  4348   muls r0, r1 
8000004:  f001 fffc  bl  8002000 <externalFunction> 
8000008:  bc10   pop  {r4} 
800000a:  bc02   pop  {r1} 
800000c:  4708   bx  r1 
800000e:  46c0   nop      ; (mov r8, r8) 
+1

請注意,如果在你的真實情況下,BL指令不在+/- 16M範圍內,鏈接器無論如何都會將其替換爲長分支。 –