arm 链接器在函数调用中使用的“单板"是什么? [英] What is 'veneer' that arm linker uses in function call?

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

问题描述

我刚刚阅读了 https://www.keil.com/support/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]

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 命令无法表达时,链接器将函数 C 插入函数 A 和 B 之间,使函数 C 接近函数B.现在函数A使用b指令去函数C(复制函数调用之间的所有寄存器),函数C使用bl指令(复制所有寄存器也).当然r12寄存器是用来保存剩余的长跳转地址位的.这是贴面的意思吗?(我不知道为什么 arm 不解释什么是贴面,而只解释了贴面提供了什么..)

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 在手臂和拇指之间切换模式,因此链接器添加了一个蹦床,正如我所说的那样,或者听说它称您跳上跳下以到达目的地.在这种情况下,本质上是将 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 也称它为单板:

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 是第一个支持拇指的,我不知道我当时看到了这一点.注意链接器是 binutils 的一部分,它是独立于 gcc 的开发.你提到手臂链接器",手臂有自己的工具链,然后他们买了基尔,也许用他们自己的代替基尔.然后是 gnu 和 clang/llvm 等.所以这不是臂链接器"的情况.这样做或那样做,这是工具链链接器这样做或那样做的一种情况,每个工具链首先可以自由使用他们想要的任何调用约定,没有强制要求他们必须使用 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 链接器在函数调用中使用的“单板"是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆