Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access more than 4 arguments in an ARM assembly function?

Tags:

assembly

arm

In my assembly function, there are 6 arguments. When I try to access the fourth and fifth arguments, they are wrong, here is my code on arm cortex-8a

push {r4-r8,lr}
ldr r6, [sp]
ldr r7, [sp, #4]

I have checked [sp] memory, r4-r8 have wrong value. But, if there are 3 or fewer arguments, [sp] gives correct r4-r8 value. Do I miss something?

like image 593
Tom Avatar asked Feb 25 '13 16:02

Tom


People also ask

How many number of arguments functions can take?

Functions can define default argument values, functions can be called with keyword arguments, and functions can be written to accept any number of arguments.

How many arguments of type int can be passed to AC function using only the registers?

C functions may pass parameters in registers and fixed memory locations. A maximum of 3 parameters may be passed in registers.

Which registers are used for passing arguments to a function?

For 64-bit x86 Windows programs the arguments are passed in registers rcx , rdx , r8 and r9 . And for 64-bit x86 Linux programs the registers rdi , rsi , rcx , rdx , r8 and r9 are used.

Where are arguments stored on the stack?

Stack layout On the x86, each call's argument values and return address are stored on the stack. A callee's arguments are stored at the top of its caller's stack frame, with the first argument at the top, the second argument just below, and so on.


1 Answers

why not just try it?

unsigned int fun ( unsigned int, unsigned int, unsigned int, unsigned int, unsigned int );
unsigned int myfun ( void )
{
return(fun(1,2,3,4,5));
}

assemble then disassemble

> arm-none-eabi-gcc -O2 -c fun.c -o fun.o
> arm-none-eabi-objdump -D fun.o 

assembly output contains

00000000 <myfun>:
   0:   e52de004    push    {lr}        ; (str lr, [sp, #-4]!)
   4:   e3a03005    mov r3, #5
   8:   e24dd00c    sub sp, sp, #12
   c:   e58d3000    str r3, [sp]
  10:   e3a01002    mov r1, #2
  14:   e3a02003    mov r2, #3
  18:   e3a03004    mov r3, #4
  1c:   e3a00001    mov r0, #1
  20:   ebfffffe    bl  0 <fun>
  24:   e28dd00c    add sp, sp, #12
  28:   e49de004    pop {lr}        ; (ldr lr, [sp], #4)
  2c:   e12fff1e    bx  lr

the first four operands are in registers r0-r3 as expected. the fifth operand though is placed on the stack. why the compiler allocates 12 bytes instead of 4 for the operand, that is a mystery...Perhaps seeing the function would make more sense:

unsigned int fun ( unsigned int a, unsigned int b, unsigned int c, unsigned int d, unsigned int e )
{
    return(a+b+c+d-e);
}

assemble and disassemble

arm-none-eabi-gcc -O2 -c fun.c -o fun.o
arm-none-eabi-objdump -D fun.o 

00000000 <fun>:
   0:   e0811000    add r1, r1, r0
   4:   e0812002    add r2, r1, r2
   8:   e59d0000    ldr r0, [sp]
   c:   e0823003    add r3, r2, r3
  10:   e0600003    rsb r0, r0, r3
  14:   e12fff1e    bx  lr

so the callee simply knows the operand is the first thing on the stack and doesnt care about the stack frame created by the caller. so it is a mystery why the caller allocated 12 bytes instead of 4 in this case.

arm-none-eabi-gcc --version
arm-none-eabi-gcc (GCC) 4.7.2
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

seeing the compiler actually implement the calling convention can make the reading of the calling convention itself more understandable. Or if you craft examples like this for the specific function prototype you are interested in on the compiler you are interested in you dont have to read the convention, you just make your caller or callee whichever you are interested in, match what the compiler is doing for itself.

like image 160
old_timer Avatar answered Oct 31 '22 21:10

old_timer