0

I am trying to analyse the objdump of an ARM Binary.

I see that the number of local variables in my case is 2 integers, each of size 4 Bytes. The function does not have parameters. I expect the function to decrement the stack pointer by 8, however the stack pointer is decremented by 16.

This happens in all functions in my test set.

Can someone please help me understand what is happening? Or point me to some document which I can read to understand.

EDIT: Its a 32 bit ARM 11 Processor, if that helps.

Also, the function parameters are being sent in registers.

From my small test set, I see that the SP is incremented by value 8 more than the size of local variables.

EDIT2:

here is some code.

Objdump

    0000036c <adpcm_coder>:
 36c:   e92d0ff0    push    {r4, r5, r6, r7, r8, r9, sl, fp}
 370:   e24dd010    sub sp, sp, #16   ; <-- Decrement of SP
 374:   e58d0004    str r0, [sp, #4]
 378:   e58d300c    str r3, [sp, #12]
 37c:   e1d330f0    ldrsh   r3, [r3]
 380:   e59d000c    ldr r0, [sp, #12]
 384:   e5d0c002    ldrb    ip, [r0, #2]
 388:   e59f0118    ldr r0, [pc, #280]  ; 4a8 <adpcm_coder+0x13c>
 38c:   e790010c    ldr r0, [r0, ip, lsl #2]
 390:   e3520000    cmp r2, #0
 394:   da00003d    ble 490 <adpcm_coder+0x124>
 398:   e58d1000    str r1, [sp]

Some Output from GDB, that might be helpful.

$ arm-none-eabi-gdb my_ctop_IR.elf

(gdb) target sim
Connected to the simulator.

(gdb) load
Loading section .text, size 0x898 vma 0x0
Loading section .rodata, size 0x200 vma 0x898
Loading section .data, size 0x14e768 vma 0xa98
Start address 0x40
Transfer rate: 10981376 bits in <1 sec.

(gdb) b adpcm_coder
Breakpoint 1 at 0x37c: file adpcm_IR.c, line 106.

(gdb) run
Starting program: /home/gaurav/eclipse-workspace/hostCompiledSimulation/instrument/examples/adpcm/my_ctop_IR.elf 

Breakpoint 1, adpcm_coder (indata=0x14f208, outdata=0x154208 "", len=10240, state=0x14f204) at adpcm_IR.c:106
106   valpred = state->valprev;

(gdb) info scope adpcm_coder
Scope for adpcm_coder:
Symbol indata is a variable with multiple locations, length 4.
Symbol outdata is a variable with multiple locations, length 4.
Symbol len is a variable with multiple locations, length 4.
Symbol state is a variable with multiple locations, length 4.
Symbol valpred_41 is optimized out.
Symbol index_40 is a variable with multiple locations, length 4.
Symbol index_38 is optimized out.
Symbol delta_37 is a variable with multiple locations, length 4.
Symbol step_36 is a variable with multiple locations, length 4.
Symbol step_35 is a variable with multiple locations, length 4.
Symbol valpred_34 is a variable with multiple locations, length 4.
Symbol ivtmp_28 is a variable in register r8, length 4.
Symbol bufferstep is a variable in register r6, length 4.
Symbol outputbuffer is a variable with complex or multiple locations (DWARF2), length 4.
Symbol index is a variable with multiple locations, length 4.
Symbol vpdiff is a variable with multiple locations, length 4.
Symbol valpred is a variable with multiple locations, length 4.
Symbol step is a variable with multiple locations, length 4.
Symbol diff is a variable with multiple locations, length 4.
Symbol delta is a variable with multiple locations, length 4.
Symbol sign is a variable in register r7, length 4.
Symbol outp is a variable with complex or multiple locations (DWARF2), length 4.

I must also point out that the Source Code of the function actually has far more Local Variables, but these seem to have been optimized by the compiler. When I try to print addresses of the variables listed in the output of gdb command info scope, I only see 2 variables with addresses in stack. This is how I come to a conclusion that the function only has 2 local variables.

Regards, Gaurav

gkernel
  • 147
  • 2
  • 10
  • 1
    There's a number of potential compiler-related possibilities - function inlining, certain compiler options (debugging, stack protectors, etc.), or just plain funky code generation. Showing the full disassembly of the smallest possible function that demonstrates this would help. – Notlikethat Aug 12 '14 at 17:18
  • This question has been asked and answered MANY times, please look around SO. Even though it is a duplicate we cant really help unless you post the high level source and the code generated, otherwise we would have to close this as "primarily opinion based" since all we can do is guess. – old_timer Aug 12 '14 at 17:31
  • I guess gcc version? It could be llvm or ARMcc or something else? An unknown compiler outputs something with unknown options to unknown code. [ask] – artless noise Aug 12 '14 at 18:42
  • Well, seeing the code I would have though it was fairly self-explanatory - hint: what's in r0, r3, and r1 at that point? What happens if you need to call another function before you've finished with those values? [Not all locals are on the stack, and not everything on the stack is a local](http://en.wikipedia.org/wiki/Register_spill#Spilling). – Notlikethat Aug 13 '14 at 21:28

2 Answers2

3

In addition to your two variables, the functions (if they are not leaf functions) will also need to save the return address somewhere, so they can return. In addition, the normal ARM ABI requires that the stack always be 8-byte aligned. So you've got a total of 12 bytes that needs to be stored on the stack, which will be rounded up to a multiple of 8 for alignment.

Leaf functions don't need any stack space as they don't need to save the return address and can keep the local vars in scratch registers (if there are 4 or fewer locals).

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • It may be 8-byte aligned in 64 bit ARM, mine is 32 bit and I will edit my question with that information. I can see sp being decremented by 12 in another function which has only 1 local variable. – gkernel Aug 12 '14 at 16:31
  • 3
    @gkernel Nope, the [32-bit AAPCS](http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042e/IHI0042E_aapcs.pdf) mandates 8-byte SP alignment at a public interface (it's 16 bytes for 64-bit). – Notlikethat Aug 12 '14 at 17:10
  • It is 8-byte aligned on ARM32 so ldrd (which expects 8-byte alignment) can be used on stack-variables. – Nico Erfurth Aug 12 '14 at 17:11
  • 8 bit alignment because the busses these days are 64 bits wide even though they are 32 bit processors. – old_timer Aug 12 '14 at 17:28
  • I think it was due to a ~bug in vararg definitions. – auselen Aug 12 '14 at 17:50
0

Actually, your 2 integers are probably not part of the 16 bytes used in the stack. ARM is a RISC processor, a load/store architecture. This means it does never work directly in the memory. instead of that, it uses internal registers, in which it loads values from the memory, computes thoses values, then store it in the memory.

If your two integer only exists in your function, it is useless to put it in the memory. A register will do the work, and you avoid load/store operations.

So, what is in your stack ? The only way to know is to take a look at the assembly. Fortunately, ARM assembly is pretty easy to read. But let take a guess : if your function calls an other one, you will have to preserve you return address (initially in lr register), you will have to load the function address (it usually can't be called through direct addressing), and you will have to save the registers the function you call may modify.

It's hard to have an accurate guess, it really depends on your function. Provide us the disassembly of your function, and we will be able to tell you exactly what happens.

Jacen
  • 346
  • 2
  • 12