我可以在x86-64中使用gcc为不同变量选择RIP相对寻址还是绝对寻址

问题描述 投票:3回答:1

我编写了自己的链接脚本,将不同的变量放入两个不同的数据部分(A和B)。

A链接到零地址;B链接到代码附近,并且位于高地址空间(高于4G,不适用于x86-64中的常规绝对寻址)。

A可以通过绝对寻址访问,但不能通过RIP相对访问;可以通过相对RIP寻址来访问B,但不是绝对寻址;

我的问题:有什么方法可以为gcc中的不同变量选择相对于RIP的地址还是绝对地址?也许带有#pragma之类的注释?

gcc x86-64 addressing-mode relative-addressing
1个回答
0
投票

[无需破解GCC源代码,您不会让它发出32位绝对地址,但是在某些情况下,gcc将使用64位绝对地址。


-mcmodel=medium将大对象放在单独的部分中,对大数据部分使用64位绝对地址。 (通过-mlarge-data-threshold=设置所有对象都必须同意的大小阈值)。但对于其他所有变量,仍使用相对RIP。

有关不同内存模型的更多信息,请参见x86-64 System V ABI文档。和/或-mcmodel=-mlarge-data-threshold=的GCC文档:https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html默认值为-mcmodel=small:所有其他内容均在2GiB之内,因此RIP相对有效。对于非PIE可执行文件,这是虚拟地址空间的低2GiB,因此静态地址可以是32位绝对符号或零扩展立即数,或在寻址模式下为disp32。

int a[1000000];
int b[1];

int fa() {   return a[0];  }
int fb() {   return b[0];  }

ASM输出(Godbolt):

# gcc9.2 -O3 -mcmodel=medium
fa():
        movabs  eax, DWORD PTR [a]     # 64-bit absolute address, special encoding for EAX
        ret
fb():
        mov     eax, DWORD PTR b[rip]
        ret

要加载到AL / AX / EAX / RAX以外的寄存器中,GCC将使用movabs r64, imm64作为地址,然后使用mov reg, [reg]

[A部分,您不会让gcc使用32位绝对寻址。它将始终使用64位绝对寻址,从不使用[array + rdx*4][abs foo](NASM语法)。而且,永远不要mov edi, msg(imm32)将地址放入寄存器,总是要mov rdi, qword msg(imm64)。

GCC将b放在.lbss部分中,并将a放在常规.bss中。大概您可以在[]上使用__attribute__((section("name")))

        .globl  b
        .section        .lbss,"aw"           # "aw" = allocate(?), writeable
        .align 32
        .size   b, 4000000
b:
        .zero   4000000

        .globl  a
        .bss                      # shortcut for .section
        .align 4
a:
        .zero   4

不起作用的东西:

  • __attribute__((optimize("mcmodel=large")))(按功能)。实际上不起作用,而且每个功能也不是每个变量。
  • https://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html没有记录任何与内存模型或大小有关的x86或公共变量属性。唯一x86特定的变量属性是ms vs gcc结构布局。

  • 有一些函数和类型的x86特定属性,但这些无济于事。


    可能的黑客:

将所有A部分变量放入一个大结构中,大于所有B部分全局/静态对象。可能在最后用一个虚拟数组填充它以使其更大:您的链接程序脚本可能可以避免为该虚拟数组实际分配额外的空间。

然后使用-mcmodel=medium mlarge-data-threshold=那个大小进行编译。

© www.soinside.com 2019 - 2024. All rights reserved.