2

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.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Arin Chaudhuri
  • 382
  • 4
  • 14
  • Probably `mov rsi, msg` should be `lea rsi, [rel msg]`. (You can omit the `rel` here if you use a `default rel` directive.) – ecm Jul 10 '23 at 07:49
  • Yes, that fixes that problem. Thanks. – Arin Chaudhuri Jul 10 '23 at 11:28
  • Near duplicate of [How to load address of function or label into register](https://stackoverflow.com/q/57212012) - my answer does briefly mention runtime fixups in the .text section as one of the downsides of `movabs` aka `mov r64, imm64`. – Peter Cordes Jul 10 '23 at 12:37

1 Answers1

2

Use a RIP-relative LEA to silence the warning: lea rsi, [rel msg].
Or default rel so lea rsi, [msg] is treated as [rel msg]
How to load address of function or label into register
mov rsi, msg with 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 -pie to ld when 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 .text section (or probably other read-only sections), these are called "text relocations", DT_TEXTREL, and require an mprotect system call to temporarily make the page read+write.

If you make a "static PIE" (gcc -static-pie), _start needs 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.local for read-write), so hopefully only one dirtied page, leaving .rodata and .text clean 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 .text or .rodata) so you don't get a DT_TEXTREL warning. The same mechanism of applying relocations during startup still happens, but without the mprotect call first. The .data.rel.ro.local section is a subsection of .data so it starts out read+write. I think it gets made read-only after applying relocations, with mprotect(PROT_READ).

GCC does avoid absolute addresses when inventing jump tables for switch by 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-pie and then linked them into a PIE or shared library, although that will more usually fail entirely with an error like relocation 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 -fPIE as the default, but that didn't used to be the case, or you might have used -fno-pie for some files. DT_TEXTREL also 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, msg is 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 want mov esi, msg (32-bit absolute), but the best we can do in position-independent code is lea 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-pie when linking for 32-bit code. That also works for 64-bit code if you want to keep using inefficient but "simple" stuff like mov rsi, msg and silence the warning.)

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • Thanks. I am seeing a similar error when I try to use puts (which I declared as extern). I have also start with default rel. What am I missing here? /usr/bin/ld: hello.o: warning: relocation against `puts@@GLIBC_2.2.5' in read-only section `.text' /usr/bin/ld: hello.o: relocation R_X86_64_PC32 against symbol `puts@@GLIBC_2.2.5' can not be used when making a PIE object; recompile with -fPIE /usr/bin/ld: final link failed: bad value collect2: error: ld returned 1 exit status – Arin Chaudhuri Jul 10 '23 at 18:45
  • @ArinChaudhuri: [Can't call C standard library function on 64-bit Linux from assembly (yasm) code](https://stackoverflow.com/q/52126328) – Peter Cordes Jul 10 '23 at 20:24
  • "Using absolute addresses in a PIE requires runtime fixups (done by CRT startup code) " I think this phrasing is a bit confusing because it's the `--no-dynamic-linker` option passed to `ld` due to `--static-pie` that counts, not being a PIE in itself. In absence of a dynamic loader there's nothing that perform the relocation for the program and the CRT itself must do it. – Margaret Bloom Jul 10 '23 at 21:53
  • @MargaretBloom: Oh right, it's the dynamic linker itself that applies fixups in a non-static PIE. Thanks. – Peter Cordes Jul 11 '23 at 01:18