Gcc 如何将函数或标签的地址加载到寄存器中
我试图将“main”的地址加载到GNU汇编程序的寄存器(R10)中。我不能。这里是我所拥有的和收到的错误消息Gcc 如何将函数或标签的地址加载到寄存器中,gcc,assembly,x86-64,att,addressing-mode,Gcc,Assembly,X86 64,Att,Addressing Mode,我试图将“main”的地址加载到GNU汇编程序的寄存器(R10)中。我不能。这里是我所拥有的和收到的错误消息 main: lea main, %r10 我还尝试了以下语法(这次使用mov) 通过以上两种方法,我得到以下错误: /usr/bin/ld: /tmp/ccxZ8pWr.o: relocation R_X86_64_32S against symbol `main' can not be used when making a shared object; recompile wi
main:
lea main, %r10
我还尝试了以下语法(这次使用mov)
通过以上两种方法,我得到以下错误:
/usr/bin/ld: /tmp/ccxZ8pWr.o: relocation R_X86_64_32S against symbol `main' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Nonrepresentable section on output
collect2: error: ld returned 1 exit status
使用-fPIC编译并不能解决这个问题,只会给我同样的错误。在x86-64中,大多数直接和位移仍然是32位,因为64位会浪费太多的代码大小(I-cache占用空间和获取/解码带宽)
leamain,%reg
是一种绝对的disp32
寻址模式,它将阻止加载时间地址随机化(ASLR)选择随机64位(或47位)地址。因此,在Linux上,除了位置相关的可执行文件,或者在MacOS上,静态代码/数据总是加载在低位32位之外。(有关文档和指南的链接,请参阅。)在Windows上,您可以将可执行文件构建为“大地址感知”或“不感知”。如果不选择,地址将适合32位
将静态地址放入寄存器的标准有效方法是RIP相对LEA:
#RIP relative LEA始终有效。各种汇编程序的语法:
lea main(%rip),%r10#AT&T语法
lea r10,[rip+main]#GAS.intel#
lea r10,[rel main];NASM等效,或使用默认的rel
lea r10,[main];FASM默认为RIP relative。MASM也可能
有关3种语法的解释,请参见;和(和)有关为什么RIP relative是处理静态数据的标准方法的原因,请参见
这使用了当前指令末尾的32位相对位移,如jmp
/call
。这可以访问.data
、.bss
、.rodata
中的任何静态数据,或.text
中的函数,假设静态代码+数据的总大小通常为2GB
在Linux上的位置相关代码(例如使用
gcc-fno pie-no pie
构建)中,您可以利用32位绝对寻址来节省代码大小。另外,mov r32、imm32
在Intel/AMD CPU上的吞吐量略高于RIP相对LEA,因此无序执行可能会更好地与周围的代码重叠。(优化代码大小通常不如其他大多数事情重要,但当所有其他因素都相等时,选择较短的指令。在这种情况下,所有其他因素至少相等,或者使用mov imm32
更好)
有关如何将饼图可执行文件作为默认文件的详细信息,请参见。(这就是为什么使用32位绝对值时出现关于-fPIC
的链接错误。)
#在非饼图可执行文件中,将imm32移动到32位寄存器更好
#与您在32位代码中使用的相同
##气体AT&T语法
mov$main,%r10d#6字节
mov$main,%edi#5字节:“遗留”寄存器不需要REX前缀
##GAS.intel\u语法
移动edi,偏移主管道
;; mov edi,main;NASM和FASM语法
请注意,写入任何32位寄存器始终为零将扩展到完整的64位寄存器(R10和RDI)
lea-main、%edi
或lea-main、%rdi
也可以在Linux非PIE可执行文件中工作,但决不能将lea与[disp32]
绝对寻址模式一起使用(即使在不需要SIB字节的32位代码中)mov
至少总是一样好
如果有唯一确定操作数大小的寄存器操作数,则操作数大小后缀是冗余的;我更喜欢只写mov
,而不是movl
或movq
愚蠢/糟糕的方法是将10字节64位绝对地址作为立即数:
#效率低下,不要使用
movabs$main,%r10#10字节,包括64位绝对地址
如果您使用mov-rdi,main
而不是mov-edi,main
那么许多人最终都会这样做,这就是NASM中的结果。Linux动态链接实际上支持64位绝对地址的运行时修复。但是这种情况下的用例是跳转表,而不是直接的绝对地址
movq$sign\u extended\u imm32,%reg
(7字节)仍然使用32位绝对地址,但在符号上浪费代码字节,将mov
扩展到64位寄存器,而不是写入32位寄存器时隐式零扩展到64位
通过使用movq
,您告诉GAS您想要R_X86_64_32S
重定位,而不是R_X86_64_64
64位绝对重定位
您希望使用这种编码的唯一原因是用于内核代码,其中静态地址位于64位虚拟地址空间的上2GiB,而不是下2GiBmov
在某些CPU上(例如,在更多端口上运行)比lea稍有性能优势,但通常情况下,如果您可以使用32位绝对值,则它位于mov r32、imm32
工作的低2GiB虚拟地址空间中
(相关:)
PS:我有意省略了任何关于“大”或“大”内存/代码模型的讨论,其中RIP relative+-2GiB寻址无法访问静态数据,或者甚至可能无法访问其他代码地址。以上内容适用于x86-64 System V ABI的“小型”和/或“小型PIC”代码模型。对于中型和大型机型,您可能需要
movabs$imm64
,但这种情况非常罕见
我不知道mov$imm32,%r32
是否适用于Windows x64可执行文件或具有运行时修复的DLL,但RIP relative LEA肯定适用
半相关:-如果要进行JIT,请尝试将JIT缓冲区放在现有代码附近,以便
调用rel32
,否则movabs
将指针放入寄存器。在x86-64中,大多数即时和置换仍然是32位,因为64位会浪费太多的代码大小(I-cache占用空间和获取/解码带宽)
/usr/bin/ld: /tmp/ccxZ8pWr.o: relocation R_X86_64_32S against symbol `main' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Nonrepresentable section on output
collect2: error: ld returned 1 exit status