0

So I want to assemble this very simple code:

extern printf

Segment .data
string: 
    db "foo", 0

Segment .text
extern printf


global main
main:

    push rbp
    xor rax, rax
    mov rdi, string

    call printf 
    pop rbp
    ret

but I wanted to make it a PIE so I changed

mov rdi, string

to

lea rdi, [rel+string]

but after doing that I get the following errors from ld/gcc:

relocation R_X86_64_PC32 against symbol `printf@GLIBC_2.2.5' can not be used when making a PIE object; recompile with -fPIC /usr/bin/ld: final link failed: bad value

I'm compiling and assembling this with: nasm -f elf64 bar.s -o bar.o; gcc -fPIC bar.o -o bar

Why am I getting this error? It compiles just fine with the -no-pie flag

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Trey
  • 474
  • 2
  • 9
  • 32
  • 1
    LD doesn't automatically create PLT stubs and rewrite your call instructions when linking an ELF shared object (including PIE executables). You need `call printf@plt` or `call [rel printf@GOTPCREL]`. See [How can I call printf normally in assembly without @PLT but just call printf with -l option in gcc with standard library,](//stackoverflow.com/q/56404855) for the opposite question. Also see [What does @plt mean here?](//stackoverflow.com/q/5469274) – Peter Cordes Jun 15 '19 at 15:28
  • 1
    Oops, `call [rel printf wrt ..got]` is the NASM syntax for gcc `-fno-plt` style code-gen that uses early-binding for dynamic linker symbols, and `call printf wrt ..plt` is NASM for explicit PLT style. `call printf@plt` is GAS syntax. – Peter Cordes Jun 15 '19 at 15:36
  • Ciro's answer on [How to print a number in assembly NASM?](//stackoverflow.com/a/32853546) explains exactly what you need to call printf from a PIE or non-PIE executable in NASM. – Peter Cordes Jun 15 '19 at 15:37
  • 1
    Also found a better duplicate which goes into the details of PLT vs. GOTPCREL and PIE vs. non-PIE. [Can't call C standard library function on 64-bit Linux from assembly (yasm) code](//stackoverflow.com/q/52126328) – Peter Cordes Jun 15 '19 at 15:46
  • @PeterCordes forgive my ignorance but what does *wrt* stand for? Also GOTPCREL is, I assume, the GOT right? – Trey Jun 15 '19 at 17:00
  • WRT = With Respect To. GOTPCREL is a PC-relative way of addressing the GOT. – Peter Cordes Jun 15 '19 at 17:03
  • 1
    Updated my answer on the linked duplicate with a bit more detail on those. – Peter Cordes Jun 15 '19 at 17:10
  • @PeterCordes Thanks Peter. Here's what happened now, your version which uses `jmp WRT ..puts` worked but it segfaulted after showing the message. I couldn't assemble the one which uses **..GOTPCREL** because apparently the symbol is undefined. – Trey Jun 15 '19 at 17:30
  • 1
    You want `call` instead of `jmp` because you're not making a tailcall. – Peter Cordes Jun 15 '19 at 17:59
  • Apparently `..GOTPCREL` only works in YASM, not NASM. In NASM, use `call [rel printf wrt ..got]`. (You only need the `rel` keyword if you leave out `default rel`, but yeah, `..got` instead of `..gotpcrel`) I'll update my YASM answer for that after lunch. – Peter Cordes Jun 15 '19 at 18:06
  • @PeterCordes it worked! just one more thing, I changed **call** to **jmp** just to see what happens and it worked as well, so can I use them interchangeably? – Trey Jun 15 '19 at 18:42
  • 1
    `call` pushes a return address. `jmp` doesn't. That's the only difference, but it's a pretty key difference!! Use `jmp foo` to a new function instead of `call foo` / `ret`. If you need to do *anything* between the call and ret (like `pop rbp`), you can't just replace call/ret with jmp. (You'd have to make other changes like doing the `pop rbp` first to make an optimized tailcall). Plus you have `int main`, not `void foo`. I updated my answer on the linked duplicate to work for NASM as well, and to take out the tailcall optimization from the example because it's a totally separate issue. – Peter Cordes Jun 15 '19 at 18:56

0 Answers0