我编写了自己的链接脚本,将不同的变量放入两个不同的数据部分(A和B)。
A链接到零地址;B链接到代码附近,并且位于高地址空间(高于4G,不适用于x86-64中的常规绝对寻址)。
A可以通过绝对寻址访问,但不能通过RIP相对访问;可以通过相对RIP寻址来访问B,但不是绝对寻址;
我的问题:有什么方法可以为gcc中的不同变量选择相对于RIP的地址还是绝对地址?也许带有#pragma
之类的注释?
[无需破解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=
那个大小进行编译。