链接器在函数调用

链接器在函数调用

本文介绍了arm链接器在函数调用中使用的“单板"是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I just read https://www.keil.com/support/man/docs/armlink/armlink_pge1406301797482.htm. but can't understand what a veneer is that arm linker inserts between function calls.

在ARM 体系结构的过程调用标准"中文件,它说,

In "Procedure Call Standard for the ARM Architecture" document, it says,

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 提供了什么..)

Here is what I guess, is it something like this ? : when function A calls function B, and when those two functions are too far apart for the bl command to express, the linker inserts function C between function A and B in such a way function C is close to function B. Now function A uses b instruction to go to function C(copying all the registers between the function call), and function C uses bl instruction(copying all the registers too). Of course the r12 register is used to keep the remaining long jump address bits. Is this what veneer means? (I don't know why arm doesn't explain what veneer is but only what veneer provides..)

推荐答案

这只是一个蹦床.交互工作更容易演示,这里使用 gnu,但暗示 Kiel 也有解决方案.

It is just a trampoline. Interworking is the easier one to demonstrate, using gnu here, but the implication is that Kiel has a solution as well.

.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 和 thumb 之间切换模式,因此链接器添加了一个蹦床,正如我所说的,或者听说过它叫你跳上跳下到达目的地.在这种情况下,本质上是将 bl 的分支部分转换为 bx,他们利用的链接部分仅使用 bl.您可以看到拇指到手臂或手臂到拇指的情况.

You cannot use bl to switch modes between arm and thumb so the linker has added a trampoline as I call it or have heard it called that you hop on and off to get to the destination. In this case essentially converting the branch part of bl into a bx, the link part they take advantage of just using the bl. You can see this done for thumb to arm or arm to thumb.

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

The even_more function is in the same mode (ARM) so no need for the trampoline/veneer.

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

For the distance limit of bl lemme see. Wow, that was easy, and gnu called it a veneer as well:

.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 的相同模式.

Staying in the same mode it did not need the bx.

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

The alternative is that you replace every bl instruction at compile time with a more complicated solution just in case you need to do a far call. Or since the bl offset/immediate is computed at link time you can, at link time, put the trampoline/veneer in to change modes or cover the distance.

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

You should be able to repeat this yourself with Kiel tools, all you needed to do was either switch modes on an external function call or exceed the reach of the bl instruction.

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

Understand that toolchains vary and even within a toolchain, gcc 3.x.x was the first to support thumb and I do not know that I saw this back then. Note the linker is part of binutils which is as separate development from gcc. You mention "arm linker", well arm has its own toolchain, then they bought Kiel and perhaps replaced Kiel's with their own or not. Then there is gnu and clang/llvm and others. So it is not a case of "arm linker" doing this or that, it is a case of the toolchains linker doing this or that and each toolchain is first free to use whatever calling convention they want there is no mandate that they have to use ARM's recommendations, second they can choose to implement this or not or simply give you a warning and you have to deal with it (likely in assembly language or through function pointers).

ARM 不需要解释,或者说,架构参考手册里解释的很清楚(看 bl 指令,bx 指令找interworking 等字眼,都解释得很清楚)对于一个特定的架构.所以没有理由再解释一遍.尤其是对于 bl 的范围各不相同且每种架构具有不同的互通特性的一般性陈述,它可能是一长串的段落或一小章来解释已经清楚记录的内容.

ARM does not need to explain it, or let us say, it is clearly explained in the Architectural Reference Manual (look at the bl instruction, the bx instruction look for the words interworking, etc. All quite clearly explained) for a particular architecture. So there is no reason to explain it again. Especially for a generic statement where the reach of bl varies and each architecture has different interworking features, it would be a long set of paragraphs or a short chapter to explain something that is already clearly documented.

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

Anyone implementing a compiler and linker would be well versed in the instruction set before hand and understand the bl and conditional branch and other limitations of the instruction set. Some instruction sets offer near and far jumps and some of those the assembly language for the near and far may be the same mnemonic so the assembler will often decide if it does not see the label in the same file to implement a far jump/call rather than a near one so that the objects can be linked.

在任何情况下,在链接之前,您都必须编译和组装,工具链人员将完全理解架构的规则.ARM 在这里并不特别.

In any case before linking you have to compile and assembly and the toolchain folks will have fully understood the rules of the architecture. ARM is not special here.

这篇关于arm链接器在函数调用中使用的“单板"是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-06 19:31