我编写了自己的链接脚本,将不同的变量放入两个不同的数据段(A和B)中。
A与零地址相连; B与代码相邻,位于高地址空间(高于4G,这在x86-64的常规绝对寻址中不可用)。
可以通过绝对寻址访问A,但不能通过RIP相对寻址访问; 可以通过RIP相对寻址访问B,但不能通过绝对寻址访问。
我的问题是:在gcc中是否有办法为不同的变量选择RIP相对或者绝对寻址?也许通过某些类似于#pragma
的注释?
我编写了自己的链接脚本,将不同的变量放入两个不同的数据段(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文档。或者查看GCC文档以了解-mcmodel=
和-mlarge-data-threshold=
: 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]; }
汇编输出( 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特定属性,但这些属性也没用。
将所有Section-A变量放入一个大结构体中,比任何Section-B全局/静态对象都要大。 可能在末尾用虚拟数组进行填充以使其更大:您的链接器脚本可能可以避免为该虚拟数组分配额外空间。
然后使用-mcmodel = medium mlarge-data-threshold =
编译该大小。