我有以下代码
global main
section .text
main:
mov rax, 1
mov rdi, 1
mov rsi, msg
mov rdx, 6
syscall
mov rax,60
xor rdi,rdi
syscall
section .data
msg:
db "Hello",10,0
字符串
我把它编译成一个可执行文件:
nasm -f elf64 hello.asm
gcc hello.o
型
生成的可执行文件正确地打印出Hello后跟一个换行符。
但我在链接时收到以下警告消息。
$ gcc hello.o
/usr/bin/ld: hello.o: warning: relocation in read-only section `.text'
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE
型
我不明白这是什么意思。我想了解它的含义以及我必须改变什么才能摆脱这条消息。
1条答案
按热度按时间wvmv3b1j1#
使用RIP相关莱亚使警告静音:
lea rsi, [rel msg]
的。或
default rel
,因此lea rsi, [msg]
被视为[rel msg]
How to load address of function or label into register
64位绝对地址的
mov rsi, msg
是最差的方式;仅当可执行文件大于2GiB时才使用它(例如用巨大的数组),所以正常的方式不能达到。现代Linux发行版默认配置GCC生成PIE(位置独立可执行文件),这样它们就可以从ASLR(在随机基址加载)中受益。这包括在链接
.o
时将-pie
传递给ld
。32-bit absolute addresses no longer allowed in x86-64 Linux?更多**在PIE中使用绝对地址需要在内核选择一个地址来加载/Map您的可执行文件之后进行运行时修复(由动态链接器
ld.so
完成)。**在.text
部分(或可能其他只读部分),这些被称为“文本重定位”,DT_TEXTREL
,并且需要mprotect
系统调用来临时使页面读+写。如果您创建一个“静态PIE”(
gcc -static-pie
),_start
需要应用重定位(gcc的静态PIE CRT启动代码就是这样做的),或者如果您使用自己的_start
(gcc -static-pie -nostdlib
)-How are relocations supposed to work in static PIE binaries?,它们根本不会被执行使用像
movzx edx, byte [msg + rcx]
这样的32位绝对地址(寻址模式为[rcx+disp32]
)在64位PIE可执行文件或共享库中甚至是不可能的,因为它们需要在虚拟地址空间中的任何位置都是可重定位的,而不仅仅是低2 GiB:32-bit absolute addresses no longer allowed in x86-64 Linux?的应用文本重定位会弄脏整个内存页面,因此磁盘上的可执行文件不支持它(就像您修改的MAP_PRIVATE文件Map一样,它基本上变成了一个匿名页面,只能分页到交换空间)。而且它会占用元数据的空间来应用它。
当编译器使用64位绝对地址时,例如。对于静态指针变量,如全局
int *const p = &a;
(Godbolt上的例子)或指针数组,他们把它们放在一个特殊的部分,所以它们都分组在一起(.section .data.rel.ro.local,"aw"
表示只读,.data.rel.local
表示读写),所以希望只有一个脏页面,使.rodata
和.text
保持干净,以便可以在运行相同可执行文件或Map相同库的进程之间共享。这些编译器生成的绝对地址位于以读+写开头的页中(不是
.text
或.rodata),因此您不会收到DT_TEXTREL
警告。在启动期间应用重定位的相同机制仍然会发生,但没有先调用mprotect
。.data.rel.ro.local
部分是.data
的一个子部分,因此它开始读+写。我认为它在应用mprotect(PROT_READ)
重定位后变成只读。GCC在为
switch
创建跳转表时,通过使用32位相对偏移量来避免绝对地址:GCC Jump Table initialization code generating movsxd and add?此警告的存在是为了帮助编译器开发人员和手工编写asm的人找到他们在PIE或共享库中意外使用绝对地址的地方(而不是将它们放在特殊的部分中)。
或者编译器的用户使用
-fno-pie
构建一些文件,然后将它们链接到PIE或共享库中,尽管这通常会完全失败,出现像relocation R_X86_64_32S against
.data' can not be used when making a shared object; recompile with -fPIC这样的错误。(在大多数现代发行版上,GCC默认配置为
-fPIE,但过去不是这样,或者您可能对某些文件使用
-fno-pie。
DT_TEXTREL也适用于共享库。 你通常不希望64位绝对地址作为你的机器码的一部分放在首位,只作为数据。
mov rsi, msg* 不是 * 在64位代码中执行任务的好方法,除非您正在制作一个大于2GiB的大型可执行文件,因此标签距离指令超过+-2GiB(因此RIP-relative无法访问,如
-mcmodel=large)在传统的非PIE可执行文件中,您需要
mov esi, msg(32位绝对值),但我们在位置无关代码中所能做的最好的是
lea rsi, [rel msg](RIP + rel 32)。 参见[How to load address of function or label into register](https://stackoverflow.com/questions/57212012/how-to-load-address-of-function-or-label-into-register) (32位模式中不存在RIP相关寻址,因此避免32位代码中的文本重定位就不那么简单了,而且会降低性能。对于初学者,特别是我建议在链接32位代码时只使用
-no-pie。如果你想继续使用低效但“简单”的东西,比如
mov rsi, msg`,并消除警告,那么这也适用于64位代码。)