我刚刚读到https://www.keil.com/support/man/docs/armlink/armlink_pge1406301797482.htm.但无法理解arm链接器在函数调用之间插入的贴面是什么。
在";ARM体系结构的过程调用标准";文件,它说,
5.3.1.1链接器使用IP ARM和Thumb状态BL指令都无法寻址整个32位地址空间,因此连接件可能需要在调用例程和被调用的子程序。可能还需要贴面以支持ARM Thumb互操作或动态链接。任何贴面inserted必须保留除IP(r12)之外的所有寄存器的内容以及条件代码标志;一致性程序必须假设改变IP的单板可以插入到任何分支指令暴露于支持相互工作或长期工作的重新定位分支机构。注R_ARM_CALL、R_ARM_JUMP24、R_ARM_PC24、R_ARM_THM_CALL,R_ ARM_THM_JUMP24和R_具有此属性的重定位类型。有关的全部详细信息,请参阅[AAELF]
我猜是这样的吗?:当函数A调用函数B时,当这两个函数相距太远,bl
命令无法表达时,链接器在函数A和B之间插入函数C,使函数C接近函数B。现在,函数A使用b
指令转到函数C(复制函数调用之间的所有寄存器),而函数C使用CCD_ 3指令(也复制所有寄存器)。当然,r12寄存器用于保留剩余的长跳转地址位。这就是贴面的意思吗?(我不知道为什么arm没有解释什么是贴面,而只解释贴面提供了什么。)
.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在手臂和拇指之间切换模式,所以链接器添加了一个蹦床,我称之为蹦床,或者听说它叫你跳上跳下到达目的地。在这种情况下,本质上是将bl的分支部分转换为bx,链接部分他们只利用bl。你可以看到拇指对手臂或手臂对拇指这样做。
even_more功能处于相同模式(ARM),因此不需要蹦床/贴面。
关于bl的距离限制,请参阅。哇,这很容易,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指令的范围。
编辑
要知道工具链是不同的,即使在一个工具链中,gcc3.x.x也是第一个支持thumb的,我不知道当时我看到了这一点。请注意,链接器是binutils的一部分,它是与gcc分开开发的。你提到";臂连接体";,好吧,arm有自己的工具链,然后他们买下了基尔,也许用自己的或不用自己的取代了基尔。然后是gnu和clang/llvm等。因此,这不是";臂连接体";做这个或那个,这是一个工具链链接器做这个或做那个的例子,每个工具链首先可以自由使用他们想要的任何调用约定——没有强制要求他们必须使用ARM的建议,其次他们可以选择实现这个或不实现,或者只是给你一个警告,你必须处理它(可能是用汇编语言或通过函数指针)。
ARM不需要对其进行解释,或者说,它在特定体系结构的《体系结构参考手册》中有明确的解释(查看bl指令、bx指令,查找单词interworking等。所有这些都解释得很清楚)。所以没有理由再解释一遍。特别是对于bl的范围不同且每个架构具有不同互通功能的通用声明,它将是一长串段落或一小章来解释已经明确记录的内容。
任何实现编译器和链接器的人都将精通现有的指令集,并理解指令集的bl和条件分支以及其他限制。一些指令集提供了近跳转和远跳转,而其中一些指令集的汇编语言可能是相同的助记符,因此汇编程序通常会决定是否在同一文件中没有看到标签来实现远跳转/调用,而不是近跳转/调用以便链接对象。
在任何情况下,在链接之前,您都必须编译和组装,工具链人员将完全理解体系结构的规则。ARM在这里并不特别。
这是Raymond Chen的评论:
贴面必须靠近A,因为B离得太远了。A做bl并且单板组r12到达最终目的地(B),以及做bx r12。bx可以到达整个地址空间。
这已经足够回答我的问题了,但他不想写完整的答案(可能是因为时间不够…)我把它放在这里作为答案并选择它。如果有人发布了更好、更详细的答案,我会切换到它。