Using RIP-relative addressing in OSX x64 assembly

2019-05-07 07:01发布

问题:

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?

回答1:

The problem is that the original mov rdi, msg loaded the memory address of msg into rdi at assemble time.

When it was changed to mov rdi, [rel msg], this produced code which used the value in msg as the relative address, as seen when debugging:

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x00000a6f6c6c6568

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 of msg at runtime, like so:

lea rdi, [rel msg]