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

My build function has 6 arguments. When I try to access the fourth and fifth arguments, they are wrong, here is my code on the cortex-8a hand

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

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

+4
source share
3 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 and 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 the register r0-r3, as expected. the fifth operand, however, is pushed onto the stack. why the compiler allocates 12 bytes instead of 4 for the operand, which is a mystery ... Perhaps viewing the function makes sense:

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

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 

therefore, the caller simply knows that the operand is the first thing on the stack and does not care about the stack frame created by the caller. so itโ€™s a mystery why in this case the caller allocated 12 bytes instead of 4.

 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 that the compiler actually implements the calling convention can make reading the calling convention more understandable. Or, if you create such examples for a specific function prototype that interests you in the compiler that you are interested in, you do not need to read the agreement, you just make your caller or called party depending on what interests you, in accordance with what the compiler does for itself.

+8
source

Additional arguments are passed on the stack, however , SP points to them when entering the function. In your prolog, you click registers to save, and that changes the SP , so you need to consider it.

r4, r5, r6, r7, r8 and lr are 6 registers, so you need to set the SP offsets to 6 * 4 = 24 bytes. So try the following:

 push {r4-r8,lr} // 6 regs are pushed // SP is decremented by 6*4 = 24 bytes ldr r6, [sp, #(0+24)] // get first stack arg ldr r7, [sp, #(4+24)] // get second stack arg 

If you perform more operations with SP , for example. Allocate space for the stack, you will also have to take this into account.

+6
source

AAPCS standard

http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042f/IHI0042F_aapcs.pdf 5.5 Passing parameters contains a response.

This is not very easy to understand, since it is an algorithm, but the crucial part that answers the question seems to be:

C .5 If the NCRN is less than r4 and the NSAA is SP, the argument is split between the kernel registers and the stack. The first part of the argument is c Based on the main registers, starting from NCRN to and including r3. The rest of the argument is copied onto the stack, starting with the NSAA. NCRN is set to r4, and the NSAA is increased by the argument minus the amount received in the registers. The argument is now highlighted.

Which, as others have said, means 4 registers + stack.

+3
source

All Articles