如何链接到外部THUMB代码?

时间:2017-04-07 06:14:16

标签: gcc arm cross-compiling ld thumb

我正在为需要链接到现有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和调用会议问题不谈,我该如何实现?

2 个答案:

答案 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)