我正在为需要链接到现有THUMB代码的嵌入式内核(ARM7TDMI)编写THUMB代码。我正在使用GNU ARM嵌入式工具链(link)。我无法让链接器将现有的外部代码视为THUMB;它似乎总是认为它是ARM。我链接到的现有代码是绝对静态的,无法更改/重新编译(它基本上是一个简单的二进制文件,位于ROM芯片上)。
以下是一个示例程序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 :(得分:1)
一种方法是让它做你想做的事情
branchto.s
function getSummingItems(a,t){
return a.reduce((h,n) => Object.keys(h)
.reduceRight((m,k) => +k+n <= t ? (m[+k+n] = m[+k+n] ? m[+k+n].concat(m[k].map(sa => sa.concat(n)))
: m[k].map(sa => sa.concat(n)),m)
: m, h), {0:[[]]})[t];
}
var arr = Array(20).fill().map((_,i) => i+1), // [1,2,..,20]
tgt = 42,
res = [];
console.time("test");
res = getSummingItems(arr,tgt);
console.timeEnd("test");
console.log("found",res.length,"subsequences summing to",tgt);
console.log(JSON.stringify(res));
so.c
.thumb
.thumb_func
.globl branchto
branchto:
bx r0
so.ld
extern unsigned int externalFunction;
extern int branchto ( unsigned int, int );
int fun ( int x )
{
return(branchto(externalFunction,x)+3);
}
制造
SECTIONS
{
.text 0x8000000 : { *(.text) }
}
externalFunction = 0x8002001;
Ross Ridge在评论中的解决方案
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);
}
我更喜欢这样的装配解决方案强制我想要的确切指令。当然,如果你在外部函数中链接它会/应该刚刚工作(有一些例外,但是gnu在链接器中为你解析了to / from arm / thumb非常好)。
我实际上并不认为它是一个gnu bug,而是他们需要在链接器脚本中使用一种方法将该变量声明为拇指函数地址,而不仅仅是一些通用链接器定义的变量(同样作为arm函数地址)。就像.thumb_func一样(或更长的函数/过程声明)
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链接器文档,可能希望得到你想要的东西
.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
ex.o来自虚拟功能,让每个人都开心
SECTIONS
{
.text0 0x08000000 : { so.o }
.text1 0x08002000 (NOLOAD) : { ex.o }
}
并且NOLOAD将虚拟函数保留在二进制文件之外。
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
注意它并不完美,因为有额外的垃圾被拉入,也许是符号
arm-none-eabi-objcopy so.elf -O srec --srec-forceS3 so.srec
S00A0000736F2E7372656338
S3150800000010B501F0FDFF033010BC02BC0847C0461E
S315080000104743433A2028474E552920362E322E305C
S31508000020004129000000616561626900011F000046
S3150800003000053454000602080109011204140115CA
S31008000040011703180119011A011E021E
S70500000000FA
你可以在srec中看到,但0x08002000代码不存在,所以你的实际外部函数会被调用。
如果你不想要任何asm,我会选择你想要的指令或带有赋值的函数指针。
答案 1 :(得分: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)