Arm链接器在函数调用中使用的“胶合板”是什么?

Cha*_*Kim 5 c arm function eabi

我刚刚阅读了https://www.keil.com/support/man/docs/armlink/armlink_pge1406301797482.htm。但无法理解arm链接器在函数调用之间插入的胶合板是什么。

在“ARM 架构的过程调用标准”文档中,它说:

5.3.1.1 链接器对 IP 的使用 ARM 和 Thumb 状态 BL 指令都无法寻址完整的 32 位地址空间,因此链接器可能需要在调用例程和被调用例程之间插入一个胶合板。子程序。还可能需要胶合板来支持 ARM-Thumb 互操作或动态链接。插入的任何单板必须保留除 IP (r12) 和条件代码标志之外的所有寄存器的内容;符合要求的程序必须假设可以在任何支持互操作或长分支的重定位的分支指令处插入更改 IP 的胶合代码。注意 R_ARM_CALL、R_ARM_JUMP24、R_ARM_PC24、R_ARM_THM_CALL、R_ARM_THM_JUMP24 和 R_ARM_THM_JUMP19 是具有此属性的 ELF 重定位类型的示例。请参阅 [AAELF] 了解完整详细信息

这是我的猜测,是这样的吗?:当函数 A 调用函数 B 时,并且当这两个函数相距太远而命令bl无法表达时,链接器会在函数 A 和 B 之间插入函数 C,使得函数 C 靠近函数 B。现在函数 A 使用b指令转到函数C(在函数调用之间复制所有寄存器),并且函数C使用bl指令(也复制所有寄存器)。当然r12寄存器是用来保存剩余的长跳转地址位的。这就是贴面的意思吗?(我不知道为什么arm不解释veneer是什么,而只解释veneer提供了什么..)

old*_*mer 6

这只是一个蹦床。互通是更容易演示的方法,这里使用 gnu,但这意味着基尔也有一个解决方案。

.globl even_more
.type eve_more,%function
even_more:
    bx lr

.thumb

.globl more_fun
.thumb_func
more_fun:
    bx lr



extern unsigned int more_fun ( unsigned int x );
extern unsigned int even_more ( unsigned int x );
unsigned int fun ( unsigned int a )
{
    return(more_fun(a)+even_more(a));
}
    
Unlinked object:

Disassembly of section .text:

00000000 <fun>:
   0:   e92d4070    push    {r4, r5, r6, lr}
   4:   e1a05000    mov r5, r0
   8:   ebfffffe    bl  0 <more_fun>
   c:   e1a04000    mov r4, r0
  10:   e1a00005    mov r0, r5
  14:   ebfffffe    bl  0 <even_more>
  18:   e0840000    add r0, r4, r0
  1c:   e8bd4070    pop {r4, r5, r6, lr}
  20:   e12fff1e    bx  lr

Linked binary (yes completely unusable, but demonstrates what the tool does)

Disassembly of section .text:

00001000 <fun>:
    1000:   e92d4070    push    {r4, r5, r6, lr}
    1004:   e1a05000    mov r5, r0
    1008:   eb000008    bl  1030 <__more_fun_from_arm>
    100c:   e1a04000    mov r4, r0
    1010:   e1a00005    mov r0, r5
    1014:   eb000002    bl  1024 <even_more>
    1018:   e0840000    add r0, r4, r0
    101c:   e8bd4070    pop {r4, r5, r6, lr}
    1020:   e12fff1e    bx  lr

00001024 <even_more>:
    1024:   e12fff1e    bx  lr

00001028 <more_fun>:
    1028:   4770        bx  lr
    102a:   46c0        nop         ; (mov r8, r8)
    102c:   0000        movs    r0, r0
    ...

00001030 <__more_fun_from_arm>:
    1030:   e59fc000    ldr r12, [pc]   ; 1038 <__more_fun_from_arm+0x8>
    1034:   e12fff1c    bx  r12
    1038:   00001029    .word   0x00001029
    103c:   00000000    .word   0x00000000
Run Code Online (Sandbox Code Playgroud)

你不能使用 bl 在手臂和拇指之间切换模式,因此链接器添加了一个蹦床,我称之为或听说过它叫你跳上跳下到达目的地。在这种情况下,本质上将 bl 的分支部分转换为 bx,他们仅使用 bl 来利用链接部分。您可以看到拇指到手臂或手臂到拇指的情况。

Even_more 函数处于相同模式 (ARM),因此不需要蹦床/单板。

对于 bl 的距离限制请参阅。哇,这很简单,gnu 也称它为 veneer:

.globl more_fun
.type more_fun,%function
more_fun:
    bx lr

extern unsigned int more_fun ( unsigned int x );
unsigned int fun ( unsigned int a )
{
    return(more_fun(a)+1);
}

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
    .some   : { so.o(.text*)       } > bob
    .more   : { more.o(.text*)      } > ted
}

Disassembly of section .some:

00000000 <fun>:
   0:   e92d4010    push    {r4, lr}
   4:   eb000003    bl  18 <__more_fun_veneer>
   8:   e8bd4010    pop {r4, lr}
   c:   e2800001    add r0, r0, #1
  10:   e12fff1e    bx  lr
  14:   00000000    andeq   r0, r0, r0

00000018 <__more_fun_veneer>:
  18:   e51ff004    ldr pc, [pc, #-4]   ; 1c <__more_fun_veneer+0x4>
  1c:   20000000    .word   0x20000000

Disassembly of section .more:

20000000 <more_fun>:
20000000:   e12fff1e    bx  lr
Run Code Online (Sandbox Code Playgroud)

保持相同的模式不需要 bx。

另一种方法是在编译时用更复杂的解决方案替换每个 bl 指令,以防万一您需要进行远调用。或者,由于 bl 偏移量/立即数是在链接时计算的,因此您可以在链接时将蹦床/单板放入以更改模式或覆盖距离。

您应该能够使用 Kiel 工具自己重复此操作,您所需要做的就是在外部函数调用上切换模式或超出 bl 指令的范围。

编辑

了解工具链各不相同,即使在工具链内,gcc 3.xx 也是第一个支持拇指的,我不知道我当时看到过这个。请注意,链接器是 binutils 的一部分,它是与 gcc 分开开发的。你提到“arm linker”,arm有自己的工具链,然后他们买了Kiel,也许用自己的或没有替换了Kiel。然后还有 gnu 和 clang/llvm 等。因此,这不是“arm链接器”做这个或那个的情况,而是工具链链接器做这个或那个的情况,每个工具链首先可以自由地使用他们想要的任何调用约定,没有强制要求他们必须使用ARM 的建议,其次他们可以选择是否实现这一点,或者只是给您一个警告,您必须处理它(可能使用汇编语言或通过函数指针)。

ARM不需要解释它,或者让我们说,它在特定架构的架构参考手册中已经清楚地解释了(查看bl指令,bx指令查找互通一词,等等。所有这些都解释得很清楚)。所以没有理由再解释一遍。特别是对于 BL 的范围各不相同且每种架构具有不同的互通功能的通用声明来说,这将是一组很长的段落或一个简短的章节来解释已经清楚记录的内容。

任何实现编译器和链接器的人都会事先精通指令集,并理解 bl 和条件分支以及指令集的其他限制。一些指令集提供近跳转和远跳转,其中一些指令集的近跳转和远跳转可能具有相同的助记符,因此汇编器通常会决定是否在同一文件中看到标签来实现远跳转/调用,而不是比近的一个,以便可以链接对象。

无论如何,在链接之前,您必须进行编译和汇编,工具链人员将充分理解架构的规则。ARM在这里并不特殊。