I have the following code
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
I am compiling it to an executable as:
nasm -f elf64 hello.asm
gcc hello.o
The resulting executable correctly prints Hello followed by a newline.
But I get the following warning message while linking.
$ 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
I don't understand what it means. I want to understand what it means and what I have to change to get rid of this message.
Use a RIP-relative LEA to silence the warning:
lea rsi, [rel msg].Or
default relsolea rsi, [msg]is treated as[rel msg]How to load address of function or label into register
mov rsi, msgwith a 64-bit absolute address is the worst way; only use it if your executable will be larger than 2GiB (e.g. with huge arrays) so the normal way can't reach.Modern Linux distros configure GCC to make PIEs (Position Independent Executables) by default so they can benefit from ASLR (load at a random base address). This includes passing
-pietoldwhen linking a.o. See 32-bit absolute addresses no longer allowed in x86-64 Linux? for more.Using absolute addresses in a PIE requires runtime fixups (done by the dynamic linker
ld.so) after the kernel picks an address to load/map your executable. In the.textsection (or probably other read-only sections), these are called "text relocations",DT_TEXTREL, and require anmprotectsystem call to temporarily make the page read+write.If you make a "static PIE" (
gcc -static-pie),_startneeds to apply the relocations (and gcc's static-PIE CRT startup code does so), or they won't be done at all if you use your own_start(gcc -static-pie -nostdlib) - How are relocations supposed to work in static PIE binaries?Using 32-bit absolute addresses like
movzx edx, byte [msg + rcx](with the addressing mode being[rcx+disp32]) isn't even possible in 64-bit PIE executables or shared libraries, because they need to be relocatable anywhere in virtual address-space, not just the low 2 GiB: 32-bit absolute addresses no longer allowed in x86-64 Linux?Applying a text relocation dirties the whole page of memory so it's not backed by the executable on disk (like a MAP_PRIVATE file mapping that you modify, it becomes basically an anonymous page that could only be paged out to swap space). And it takes up space for metadata to apply it.
When compilers use 64-bit absolute addresse, e.g. for static pointer variables like a global
int *const p = &a;(example on Godbolt) or an array of pointers, they put them in a special section so they're all grouped together (.section .data.rel.ro.local,"aw"for read-only,.data.rel.localfor read-write), so hopefully only one dirtied page, leaving.rodataand.textclean so it can be shared between processes running the same executable or mapping the same library.Those compiler-generated absolute addresses are in pages that start out read+write (not
.textor .rodata) so you don't get aDT_TEXTRELwarning. The same mechanism of applying relocations during startup still happens, but without themprotectcall first. The.data.rel.ro.localsection is a subsection of.dataso it starts out read+write. I think it gets made read-only after applying relocations, withmprotect(PROT_READ).GCC does avoid absolute addresses when inventing jump tables for
switchby using 32-bit relative offsets: GCC Jump Table initialization code generating movsxd and add?This warning exists to help compiler developers and people writing asm by hand find places where they've accidentally used absolute addresses in a PIE or shared library (without putting them in a special section).
Or users of compilers who build some files with
-fno-pieand then linked them into a PIE or shared library, although that will more usually fail entirely with an error likerelocation R_X86_64_32S against `.data' can not be used when making a shared object; recompile with -fPIC. (On most modern distros, GCC is configured with-fPIEas the default, but that didn't used to be the case, or you might have used-fno-piefor some files.DT_TEXTRELalso applies to shared libraries.)You normally don't want 64-bit absolute addresses as part of your machine code in the first place, only as data.
mov rsi, msgis not a good way to do things in 64-bit code unless you're making a huge executable that's larger than 2GiB, so the label is more than +-2GiB away from the instruction (so RIP-relative couldn't reach, like-mcmodel=large) In a traditional non-PIE executable you'd wantmov esi, msg(32-bit absolute), but the best we can do in position-independent code islea rsi, [rel msg](RIP + rel32).See How to load address of function or label into register
(RIP-relative addressing doesn't exist in 32-bit mode, so avoiding text relocations in 32-bit code is less trivial and has a performance cost. For beginners especially I'd recommend just using
-no-piewhen linking for 32-bit code. That also works for 64-bit code if you want to keep using inefficient but "simple" stuff likemov rsi, msgand silence the warning.)