1

All is in the title, I have an int** as my function argument and I want to display the int with assembly, so what i've done is:

global bar

section .text:
bar:
  mov  rdx, [rdi]
  mov  rdi, [rdx]
  mov  rdx, [rdi]
  mov  rdi, [rdx]
  call rsi
  ret

My C code:

#include <stdio.h>

void  bar(int **i, void (*f)());  //assembly function prototype

void  foo(int i)
{
  printf("%d\n", i);
}

int   main(void)
{
  int i = 3;
  int *ptr = &i;

  bar(&ptr, &foo);
  return (0);
}

It segfaults and says "invalid address" at foo with lldb, so I think it's because i'm not dereferencing the right way, so I'm stuck because I need to do this for a larger function. Thanks for the help.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Fayeure
  • 1,181
  • 9
  • 21

2 Answers2

4

Let me trace the assembly code:

global bar

section .text:
bar:
  mov  rdx, [rdi] // rdi = &ptr, rdx = *&ptr = ptr
  mov  rdi, [rdx] // rdx = ptr, rdi = *ptr = i
  mov  rdx, [rdi] // rdi = i, rdx = *i = (invalid)
  mov  rdi, [rdx]
  call rsi
  ret

This suggests actually you have to pass int****, not int**, as the first argument because you are doing dereference 4 times.

It will be like this:

#include <stdio.h>

void  bar(int ****i, void (*f)());  //assembly function prototype

void  foo(int i)
{
  printf("%d\n", i);
}

int   main(void)
{
  int i = 3;
  int *ptr = &i;
  int **pptr = &ptr;
  int ***ppptr = &pptr;

  bar(&ppptr, &foo);
  return (0);
}

Also stack pointer should be 16-byte aligned on function call in x86-64, so the assembly function bar should be (for example):

global bar

section .text:
bar:
  mov  rdx, [rdi]
  mov  rdi, [rdx]
  mov  rdx, [rdi]
  mov  rdi, [rdx]
  sub  rsp, 8 // adjust stack pointer
  call rsi
  add  rsp, 8 // restore stack pointer
  ret

The alignment is done before call and 8 byte (return address) is pushed by call, so another 8 byte should be subtracted from the function pointer to retain 16-byte alignment.

If you want to use int** as the first argument, do dereferences (memory accesses) only 2 times.

global bar

section .text:
bar:
  mov  rdx, [rdi]
  mov  rdi, [rdx]
  sub  rsp, 8
  call rsi
  add  rsp, 8
  ret

Another thing you may want to do is

  1. Create stack frame
  2. Store the argument on the stack memory for later use
global bar

section .text:
bar:
  push rbp // create stack frame
  mov  rbp, rsp
  sub  rsp, 16 // create region for local variables (note 16-byte alignment)
  mov  [rbp-8], rdi // save the argument to the memory
  mov  rdx, [rdi]
  mov  rdi, [rdx]
  mov  rdi, [rbp-8] // restore the argument from the memory
  mov  rdx, [rdi]
  mov  rdi, [rdx]
  call rsi
  leave // destruct stack frame
  ret
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
MikeCAT
  • 73,922
  • 11
  • 45
  • 70
  • I writed the code at hand in the post I dont know why I added 2 dereferencements it wasn't in my code my bad I'm a little bit tired x) but I think my problem was the stack pointer but I don't think I understood how it works, if we had only 2 dereferencements would it be sub rsp, 4? – Fayeure Nov 06 '20 at 16:38
  • I think he want his assembly code match the prototype `void bar(int **i, void (*f)())` – Jabberwocky Nov 06 '20 at 16:40
  • 2
    @Fayeure No because the dereferences don't affect the stack pointer. – MikeCAT Nov 06 '20 at 16:41
  • @Fayeure: https://stackoverflow.com/questions/49391001/why-does-the-x86-64-amd64-system-v-abi-mandate-a-16-byte-stack-alignment?noredirect=1&lq=1 may help – Nate Eldredge Nov 06 '20 at 16:42
2

I think you might want this:

bar:
   mov   rax, [rdi]
   mov   edi, [rax]
   call  rsi
   ret

which would match the void bar(int **i, void (*f)()) prototype

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
  • I wrote it badly in the post but in my program it was like that and it segfaulted – Fayeure Nov 06 '20 at 16:52
  • 1
    In addition to removing the extra dereference, bar needs to align the stack before calling rsi. Subtract 8 from rsp before the call and add 8 after the call. – prl Nov 07 '20 at 01:50