I was trying to make a basic printf
example in x86-64 assembly code for OSX, here's my first version:
section .data
msg db 'hello', 0Ah
section .text
extern _printf
global _main
_main:
sub rsp, 8
mov rdi, msg
mov rax, 0
call _printf
add rsp, 8
ret
So this code is moving the absolute address of msg
into rdi
for the first argument to _printf
, and gcc then complains about the lack of position-independent code. The binary still works though:
→ nasm -f macho64 new.asm && gcc -m64 -o new new.o && ./new
ld: warning: PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, but used in _main from new.o. To fix this warning, don't compile with -mdynamic-no-pic or link with -Wl,-no_pie
hello
So when I change the code to use RIP-relative addressing, using the [rel ...]
nasm syntax, the warning disappears but the executable now seg faults:
section .data
msg db 'hello', 0Ah
section .text
extern _printf
global _main
_main:
sub rsp, 8
mov rdi, [rel msg]
mov rax, 0
call _printf
add rsp, 8
ret
And when I compile and run it:
→ nasm -f macho64 new.asm && gcc -m64 -o new new.o && ./new
zsh: segmentation fault ./new
Does anyone know what's going wrong?
The problem is that the original
mov rdi, msg
loaded the memory address ofmsg
intordi
at assemble time.When it was changed to
mov rdi, [rel msg]
, this produced code which used the value inmsg
as the relative address, as seen when debugging:Notice how the address contains the bytes from
msg
,0x00000a<olleh>
.The correct solution is to use the
lea
instruction to load the effective RIP-relative address ofmsg
at runtime, like so: