assembly 了解DT_TEXTREL警告

qco9c6ql  于 2023-08-06  发布在  其他
关注(0)|答案(1)|浏览(115)

我有以下代码

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


我不明白这是什么意思。我想了解它的含义以及我必须改变什么才能摆脱这条消息。

wvmv3b1j

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传递给ld32-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启动代码就是这样做的),或者如果您使用自己的_startgcc -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-pieDT_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位代码。)

相关问题