【问题标题】:how to avoid relative branch in arm veneer code?如何避免手臂单板代码中的相对分支?
【发布时间】:2022-01-04 00:15:17
【问题描述】:

假设在裸机(arm-none-eabi-gcc)arm v5 环境中,函数存储在固定位置,并且底层“应用程序”只能通过绝对地址访问函数. 所以,函数定义为:

    .type name, %function; \
    .extern name; \
    .equ name,0x400099

可以像这样从 C 代码调用 name(args); 但是,由于共享二进制文件(编译为-fPIE)的性质,生成的单板如下:

00012294 <name_veneer>:
00012294 ldr        r12,[DAT_0001229c]
00012298 add        pc=>LAB_412331,pc,r12
0001229c .word 400099h

链接器将 PC 的当前位置添加到不正确的最终目标位置,而是首选如下内容:

00012294 <name_veneer>:
00012294 ldr        r12,[DAT_0001229c]
00012298 mov pc,r12
0001229c .word 400099h

系统信息

  • 底层应用程序在运行时有一个未知的入口点,因此需要 PIE。
  • 应用程序从网络加载以进行调试。
  • 包含 CPU 的 SOC 是专有设计。

【问题讨论】:

  • 如果没有变通方法,它在 C 级别上是不可排序的。
  • 我怀疑你遗漏了一些东西。见my answer to static linked libraries。主要是,如果不需要重新定位静态基地,为什么要使用 PIE?然后只需 PIC 或 PC 相关代码就可以了。 PIE 需要更新静态库。
  • 是的,运行时处理程序在应用程序本身上运行,它将 R9 寄存器指向 GOT,然后由只能在共享二进制文件上完成的 reloc 表修复

标签: c gcc arm ld


【解决方案1】:

您可以通过类似宏的方式直接加载函数地址以进行注册(如果我的问题正确的话)

.equ name,0x400099

movw    r12, #:lower16:name
movt    r12, #:upper16:name  // after this instruction r12 == 0x400099
mov     pc,  r12

【讨论】:

  • 感谢您的回答,但不幸的是,这不是我所需要的,.equ name,0x400099 部分实际上是在一个汇编文件中,负责将定义的符号“导出”到链接器以将其视为一个函数,所以胶合代码由链接器生成。所以我真正想要的是修改链接器以获得直接跳转并跳过相关分支。
  • ARM v5 不支持movwmovt。但是,您可以对胶合板进行编码或将其作为函数指针列出。 int *name(args) = (int(*)(args))0x400099; 在标题中并使用它。 PIE 与 PIC 的不同之处在于数据部分被重新定位。在this page 中,它记录了 R12 (IP) 将用于更新静态库。所以我认为你不能忽略这一点,除非 PIE 不使用静态/全局变量,但它只是 PIC,为什么要编译为 PIE?
  • PIE 是必需的,因为底层应用程序确实使用了全局变量,这些全局变量可以在运行时通过在编译为共享二进制文件时重新定位导出的 ELF 段来更新(除非 PIC 可以以某种方式导出 必需的 可重定位段。即函数数组void*funct[] = {&amp;func1,&amp;func2};)
最近更新 更多