Are you writing main()
or _start
?
If you're writing main
, it's a normal function with its args in rdi
, rsi
, following the normal calling convention. See x86 tag wiki for links to the x86-64 ABI.
If you're writing _start
, then data is on the stack, as documented in process startup section of the ABI: [rsp] = argc
, and above that an array of pointers, char *arg[]
starting at rsp+8
. It's an actual array right there on the stack, not a pointer to an array like main
gets.
rbp
is meaningless unless you initialize it. It has whatever the caller left in it.
Your code fragment is silly, too: you never initialize rbp. You should assume it holds garbage on process entry. Only rsp
is guaranteed to be useful.
lea
is just a shift & add instruction that uses effective-address syntax / encoding. mov
is the mnemonic for load / store.
;; your code with comments, also assuming that RBP was initialized
bits 64
lea r8, [rbp-0x10] ; r8 = rbp-0x10
mov r9, [r8] ; should have just done mov r9, [rbp-0x10]
mov r10, [r9+0x10]
jmp r10 ; jump to argv[2]???
Did you put machine code bytes in argv[2]
? Jumping to a string is not normally useful.
Of course, since rbp
isn't initialized, it's not actually accessing argv[2]
.
Working example
single-step this in a debugger if you want to see what's going on.
; get argc and argv from the stack, for x86-64 SysV ABI
global _start
_start:
mov ecx, [rsp] ; load argc (assuming it's smaller than 2^32)
cmp ecx, 3
jb .argc_below_3
; argv[0] is at rsp+8
mov rsi, [rsp+8 + 8*2] ; argv[2] (the 3rd element)
movzx eax, byte [rsi] ; first char of argv[2]
; if you stop here in a debugger, you can see the character from the second arg.
; fall through and exit
.argc_below_3:
xor edi, edi
mov eax, 231 ; exit_group(0)
syscall