1

I have written a simple assembly code which I am trying to compile in 64-bit mode. Here is the code:

extern printf

section .rodata
readinfo db `%d\n`, 0

section .text
global main
main:
mov rbp, rsp ; for correct debugging

mov rax, 5
push rax
push readinfo
call printf
add rsp, 8

xor rax, rax
mov rsp, rbp
ret

And here are the instructions I give to nasm and gcc (as I have read on other posts, gcc automatically links the object file with the default c libraries):

nasm -f elf64 -o test.o test.asm -D UNIX
gcc -o test test.o

However, I get the following relocation error:

/usr/bin/x86_64-linux-gnu-ld: test.o: relocation R_X86_64_32S against `.rodata' can not be used when making a PIE object; recompile with -fPIC

/usr/bin/x86_64-linux-gnu-ld: final link failed: Nonrepresentable section on output

collect2: error: ld returned 1 exit status

When I compile with the '-no-pic' option to disable positionally-independent code, it compiles without errors, but after execution I get a segfault with no output. When I recompile the code in 32-bit (replacing 64-bit registers with 32-bit), I get no error. The commands are:

nasm -f elf32 -o test.o test.asm -D UNIX
gcc -o test test.o -m32

My question is: why can't I compile the code with PIC in 64bit mode?

PS: This is not a duplicate of Can't link a shared library from an x86-64 object from assembly because of PIC , since the error is different and the solution found in that post has nothing in relation with my problem. I have edited the error output to specify.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Live It
  • 53
  • 8
  • Possible duplicate of [Can't link a shared library from an x86-64 object from assembly because of PIC](https://stackoverflow.com/q/11916394/608639), [Access .data section in Position Independent Code](https://stackoverflow.com/q/36223182/608639), etc. – jww Jul 18 '18 at 17:20
  • `push readinfo` is not position independent. You could do `lea rax, [rel readinfo]` `push rax`. But forget that. In 64-bit Linux code the first parameters are passed in registers.(not on the stack). You'd also need to change `call printf` to `call printf wrt ..plt` – Michael Petch Jul 18 '18 at 21:22
  • To to the print you want try `xor eax, eax` `lea rdi, [rel readinfo]` `mov esi, 5` `call printf wrt ..plt` . Register AL has to be set to the number of vector registers use which in this case is 0. So I use XOR EAX, EAX to zero RAX. First integer class parameter is in RDI, second in RSI – Michael Petch Jul 18 '18 at 21:26
  • The 64-bit ABI can be found here that discusses the calling convention: https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-1.0.pdf . Note,`printf` is a variadic function (not a fixed number of arguments) and variadic functions are also covered in the ABI. – Michael Petch Jul 18 '18 at 21:31
  • You can find a summary of the calling convention here: https://en.wikipedia.org/wiki/X86_calling_conventions#System_V_AMD64_ABI – Michael Petch Jul 18 '18 at 21:39

1 Answers1

3

The mistake was that I was using the wrong calling convention. In architecture x86_64 the first two arguments are passed in rdi and rsi, respectively, without using the stack. Also, I needed to add "wrt ..plt" to the call. The following code works:

extern printf

section .rodata
readinfo db `%d\n`, 0

section .text
global main
main:
mov rbp, rsp ; for correct debugging

mov rsi, 5
mov rdi, readinfo
xor rax, rax
call printf wrt ..plt

xor rax, rax
mov rsp, rbp
ret

The commands for nasm and gcc haven't changed.

Live It
  • 53
  • 8
  • 1
    `xor eax,eax` is more efficient than `xor rax,rax`: it saves a byte. [What is the best way to set a register to zero in x86 assembly: xor, mov or and?](https://stackoverflow.com/q/33666617). – Peter Cordes Jul 22 '18 at 07:27
  • See also [Can't call C standard library function on 64-bit Linux from assembly (yasm) code](https://stackoverflow.com/q/52126328) for more about `wrt ..plt` or how to inline a call via the GOT entry, NASM syntax for what `gcc -fno-plt` does. – Peter Cordes Jul 25 '23 at 19:32