【问题标题】:How do I link to external THUMB code?如何链接到外部 THUMB 代码?
【发布时间】:2017-09-02 09:54:29
【问题描述】:

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

【问题讨论】:

  • 显而易见的尝试是externalFunction = 0x8002001;
  • 我得到与使用0x8002001 之前相同的输出:它实际上是在清除位的情况下组装的。似乎非常致力于确保这是一个 ARM 调用...
  • 似乎有一个关于此的错误报告,sourceware.org/bugzilla/show_bug.cgi?id=15302,但不幸的是,它的修复仅适用于您似乎没有使用的 Thumb 目标。如果您不介意非链接器脚本解决方案,您可以这样做:static int (* const externalFunction)(int x) = (int (*)(int)) 0x80002001;

标签: gcc arm cross-compiling ld thumb


【解决方案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

Ross Ridge 在 cmets 中的解决方案有效

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 非常擅长在链接器中为您解决 arm/thumb 的往返问题)。

我实际上并不认为它是一个 gnu 错误,而是他们需要在链接器脚本中将该变量声明为 thumb 函数地址,而不仅仅是一些通用链接器定义的变量(同样作为 arm 函数地址)。就像 .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,我会只做你想要的指令或函数指针。

【讨论】:

    【解决方案2】:

    使用长分支的其他 cmets/answers 确实有效,但直接调用 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)
    

    【讨论】:

    • 请注意,如果 BL 指令的目标不在实际情况下的 +/-16M 范围内,则链接器无论如何都会用长分支替换它。
    猜你喜欢
    • 1970-01-01
    • 2011-05-24
    • 1970-01-01
    • 1970-01-01
    • 2012-08-18
    • 1970-01-01
    • 2015-06-14
    • 2019-09-28
    • 2014-05-28
    相关资源
    最近更新 更多