我刚看完 https://www.keil.com/support/man/docs/armlink/armlink_pge1406301797482.htm .但无法理解 arm 链接器在函数调用之间插入的单板是什么。
在“ARM 架构的过程调用标准”文档中,它说,
5.3.1.1 Use of IP by the linker Both the ARM- and Thumb-state BL instructions are unable to address the full 32-bit address space, so it may be necessary for the linker to insert a veneer between the calling routine and the called subroutine. Veneers may also be needed to support ARM-Thumb inter-working or dynamic linking. Any veneer inserted must preserve the contents of all registers except IP (r12) and the condition code flags; a conforming program must assume that a veneer that alters IP may be inserted at any branch instruction that is exposed to a relocation that supports inter-working or long branches. Note R_ARM_CALL, R_ARM_JUMP24, R_ARM_PC24, R_ARM_THM_CALL, R_ARM_THM_JUMP24 and R_ARM_THM_JUMP19 are examples of the ELF relocation types with this property. See [AAELF] for full details
这是我的猜测,是这样的吗? : 当函数 A 调用函数 B 时,并且当这两个函数对于
bl
来说相距太远时命令来表达,链接器在函数A和B之间插入函数C,这样函数C接近函数B。现在函数A使用b
去函数C的指令(复制函数调用之间的所有寄存器),函数C使用bl
指令(也复制所有寄存器)。当然r12寄存器是用来保存剩余的长跳转地址位的。这是贴面的意思吗? (我不知道为什么 arm 不解释什么是贴面,而只解释了贴面提供了什么..)
最佳答案
它只是一个蹦床。交互工作更容易演示,这里使用 gnu,但暗示 Kiel 也有一个解决方案。
.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
您不能使用 bl 在 ARM 和拇指之间切换模式,因此链接器添加了一个蹦床,正如我所说的那样,或者听说它称您跳上跳下以到达目的地。在这种情况下,本质上是将 bl 的分支部分转换为 bx,他们利用的链接部分仅使用 bl。你可以看到这是用拇指对 ARM 或 ARM 对拇指完成的。even_more 函数处于相同模式 (ARM),因此不需要蹦床/单板。
有关 bl lemme 的距离限制,请参阅。哇,这很简单,gnu 也称它为单板:
.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
保持相同的模式,它不需要 bx。另一种方法是在编译时用更复杂的解决方案替换每个 bl 指令,以防万一您需要进行远程调用。或者由于 bl 偏移/立即数是在链接时计算的,因此您可以在链接时放置蹦床/单板以改变模式或覆盖距离。
您应该能够使用 Kiel 工具自己重复这一点,您需要做的就是在外部函数调用中切换模式或超出 bl 指令的范围。
编辑
了解工具链各不相同,甚至在一个工具链中,gcc 3.x.x 是第一个支持拇指的,我不知道我当时看到了这一点。注意链接器是 binutils 的一部分,它是独立于 gcc 的开发。你提到“ ARM 链接器”, ARM 有自己的工具链,然后他们买了基尔,也许用他们自己的代替基尔。然后是 gnu 和 clang/llvm 等。因此,这不是“臂链接器”这样做或那样的情况,而是工具链链接器这样做或那样做的情况,并且每个工具链首先可以自由使用他们想要的任何调用约定,没有强制要求他们必须使用ARM 的建议,其次他们可以选择实现或不实现,或者只是给你一个警告,你必须处理它(可能用汇编语言或通过函数指针)。
ARM 不需要解释它,或者让我们说,它在架构引用手册中清楚地解释了(查看 bl 指令,bx 指令查找单词 interworking 等。所有解释都非常清楚)针对特定架构。所以没有理由再解释了。特别是对于 bl 的范围不同且每个架构具有不同的互通特性的通用声明,解释已经清楚记录的内容将是一长串段落或一小章。
任何实现编译器和链接器的人都会事先熟悉指令集,并了解 bl 和条件分支以及指令集的其他限制。一些指令集提供近和远跳转,其中一些指令集用于近和远的汇编语言可能是相同的助记符,因此汇编器通常会决定是否在同一文件中没有看到标签来实现远跳转/调用比近一个,以便可以链接对象。
在任何情况下,在链接之前,您都必须进行编译和组装,工具链人员将完全理解架构规则。 ARM 在这里并不特别。
关于c - arm 链接器在函数调用中使用的 'veneer' 是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64893770/