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

时间:2011-03-11 05:37:11

标签: gcc x86-64 relative absolute addressing

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

A与零地址相关联; B在代码附近和高地址空间(高于4G,在x86-64中不能用于正常的绝对寻址)链接。

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

我的问题:有没有办法为gcc中的不同变量选择RIP相对或绝对寻址?也许有一些像#pragma?

这样的注释

1 个答案:

答案 0 :(得分:1)

无需破解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]

您不会让gcc对A部分使用 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=大小进行编译。