1

I have an ARM assembly function that is called from a C function.

At some point, I do something like this:

.syntax unified  
.arm
.text
.globl myfunc
.extern printf

myfunc:     
stmdb sp!, {r4-r11}     // save stack from C call

... do stuff ...

// (NOT SHOWN): Load values into r1 and r2 to be printed by format string above 
ldr     r0, =message  // Load format string above
push {lr}   // me attempting to preserve my stack
bl      printf  // actual call to printf
pop {lr}    // me attempting to recover my stack
ldmia sp!, {r4-r11} // recover stack from C call
mov r0, r2     // Move return value into r0
mov pc, lr     //  Return to C
.section data
 message:  
.asciz "Output: %d, %d\n"
.end

This runs sometimes, crashes sometimes, runs a few times then crashes, etc. It actually runs on a quasi bare-metal context, so I can't run a debugger. I'm 99% sure it's a stack -- or alignment? -- thing, as per this Printf Change values in registers, ARM Assembly and this Call C function from Assembly -- the application freezes at "call printf" and I have no idea why.

Can anyone provide some specific ideas for how to get the above chunk of code running, and perhaps general ideas for best practices here? Ideally I'd like to be able to call the same output function multiple times in my assembly file, to debug things as I go.

Thanks in advance!

jww
  • 97,681
  • 90
  • 411
  • 885
user770901
  • 414
  • 1
  • 4
  • 15
  • Stack needs to be 8-byte aligned. just pushing lr, breaks that. push something next to lr to make that push 8-byte aligned (you could also do that in the prologue of function). Why don't you print stack values before and after function call to see where it crashes. Since you are doing bare-metal, you could also handle different CPU modes and print register values like a crash handler for example when CPU goes into ABORT. I did something similar for printf a while ago: https://github.com/auselen/arm-printf – auselen May 23 '14 at 05:55
  • I suggest you do not use `printf` to debug assembler. Write your own polled UART routines. There are also simple routines to convert an *int* to hex characters. `printf` requires lots of machinery to work well. A polled UART routine may not even require stack. Take a look [here for some implementation](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/arch/arm/include/debug). If you go auselen's route, I would suggest getting a *snprintf* implementation and pass the buffer to the polled UART. – artless noise May 23 '14 at 13:50
  • If you stick with what you have, I suggest you put the `lr` save and restore together with the register saves. Take a look at [ARM link and frame pointer](http://stackoverflow.com/questions/15752188/arm-link-register-and-frame-pointer) for some assembler. Your return is then just `ldmia sp!, {r4-r11,pc}`. You should probably update the `fp` as the `printf` probably wants to use it and **maybe** stomping on your stack for some reason. Ie, C code uses the `[fp]` to access local 'C' variables and for other machinery. – artless noise May 23 '14 at 13:59

1 Answers1

0

I could see the following issues in that code:

  1. .align 2 (could be 3 or any higher value) before function entry point (myfunc:)
.align 2 // guarantee that instruction address is 4B aligned
myfunc:
  1. as was mentioned in comments, stack is expected to be 8B aligned. push {lr} breaks that.

  2. message: doesn't need to be in 'data' section. It might be placed in code section behind 'myfunc'. Check linker map that data is actually present & address loaded into r0 is correct.

Since that a bare-metal, check that stack is set properly and enough room is reserved for it.

user3124812
  • 1,861
  • 3
  • 18
  • 39