将符号的64位地址加载到AArch64上的寄存器

时间:2016-10-26 19:21:18

标签: assembly arm constants arm64

我将AArch64程序集文件链接到C / C ++中的项目。 C / C ++代码包含一个存储函数指针的变量。我需要调用此函数(如果指针不为null),所以我的计划是在X1中加载变量的地址,然后通过{加载变量的值到X2 {1}},然后通过LDR X2, [X1]调用该函数。但是,我无法弄清楚:如何加载BLR X2变量的地址?

下面是一个代码示例。在其中我需要加载X1变量X1的64位地址。

_funcPtr

我正在使用GNU / Clang汇编程序。

3 个答案:

答案 0 :(得分:1)

使用相对于 PC 的寻址,您实际上可以通过 2 条指令从静态存储中加载指针 本身,或者仅使用 2 条纯 ALU 指令(某些 CPU 可能会将其融合并处理为单宽指令)。除非您确实需要支持符号插入,否则无需通过 GOT。

像往常一样,请编译器为您制作一个 asm 示例:

void (*fptr)(void);

auto load_global() {
    return fptr;
}

g++ 8.2 -O3 用于 AArch64,on the Godbolt compiler explorer。您可以直接使用 fptr 而不是 .LANCHOR0 别名,尽管同一位置的第二个符号可能会避免链接器抱怨没有通过 GOT 获取带有 global(而不是隐藏)ELF 可见性。

.text
load_global:
        adrp    x0, .LANCHOR0
        ldr     x0, [x0, #:lo12:.LANCHOR0]
        ret

# and later, in .bss where fptr is declared.
 .bss
 .align  3
 .set    .LANCHOR0,. + 0     # .LANCHOR0 has the same address as fptr, but isn't .global
                             # could be .LANCHOR0: instead of .set
.global fptr
fptr:
   .zero   8

我修剪了 asm 输出(.size.type),并将指令重新排序为更合理的顺序以对相关内容进行分组。


实际上只是获取地址:

auto address_of_global() {
    return &fptr;
}
address_of_global:
        adrp    x0, .LANCHOR0
        add     x0, x0, :lo12:.LANCHOR0
        ret

同样,这可能直接适用于 fptr 而不是 .LANCHOR0。如果 fptrstatic(文件私有),而不是 .global


请注意,这与加载 uintptr_t x; 全局变量没有区别;一旦将 var 的值放入寄存器中,您对它的处理完全取决于您。检查它是否为非零并通过它调用不会改变你加载它的方式。我使用 C++ 函数指针只是为了解决这个问题,即使它使语法更难,特别是如果您使用 C,其中 auto 的工作方式与 C++11 不同。

假设 fptr 在您的代码的 ADRP range (+-4GiB) 范围内,而不是任意的 64 位距离。这是编译器通常对全局变量的假设,因此该内存模型是完全标准的。链接器默认将 .rodata.bss.data 部分放置在足够接近 .text 的位置,这样就可以了,除非您在静态存储中有一些巨大的数组。

(然后您可能需要使用“中等”代码模型,其中巨大的对象位于一个特殊的部分,该部分可能与 ADRP 的代码相距太远而无法工作,在那里地址的处理效率会降低,例如通过 GOT 或附近的文字池,如其他答案所示。)

答案 1 :(得分:0)

这应该有效:

adrp    x1,:got:_funcPtr
ldr     x1,[x1,:got_lo12:_funcPtr]
从全局偏移量表中

加载 _funcPtr 的地址。必须将 _funcPtr 定义为 extern ,否则GOT中将没有用于您的功能的条目。

答案 2 :(得分:0)

<块引用>

我想不通:如何在 X1 中加载变量的地址?

==>我认为你可以做到

ldr x1, =var1  

然后编译器在一个地址中分配一个空间用来存放'var1的地址'(姑且称之为来自当前PC+0x100,例如0x40000100),上面的代码实际上被翻译成

   addr            instr.      disassem.
0x40000000    58000801        ldr x1, 0x100   <== here 0x100 is PC-relative
...
0x40000100    50003400        .inst. undefined   <== 0x50003400 is actual address of var1, it's just a data

如您所知,这是因为单个 ldr 指令本身不能包含 32 位立即数。 (所以使用 19 位的 PC 相对地址。)

相关问题