2
votes

Here's the context for my question:

  • I use a ARM7 architecture (ARM720T TDMI...)
  • Compile using GCC of codesourcedy (arm-none-eabi ver 4.5.2)
  • I'm a newbie with gcc and ARM architecture but have worked in embeded for 5 years ;-)

In my project, a C function is called from an assembly file before the stack is initialized. Because the stack is not initialized, the function must not use the stack.

Is it possible with some "pragma like" command to force gcc to not use the stack with that particular function?

Additional information : The goal of my work is to convert a project previously compiled with ARMASM to gcc. So in ARMASM, calling this C function before the stack initialization was working. Maybe that the definitive answer will be that it is not possible to do this in gcc...

Below there is an extract of the ELF listing for the C function called in the assembly code (as you can see I tried a always_inline, but when called from assembly this doesn't seem to be enough):

000049d0 <CInit_Init>:
__attribute__((always_inline)) extern void CInit_Init(void) {
49d0:   e52db004    push    {fp}        ; (str fp, [sp, #-4]!)
49d4:   e28db000    add fp, sp, #0

__attribute__((always_inline)) void COM1_Init(void);

__attribute__((always_inline)) extern inline void COM1_Init_I(void) {
// Skip if already enabled
if (TEST_BIT_CLR(HwrSysControl1, HwSysControl1UartEnable)) {
49d8:   e59f3038    ldr r3, [pc, #56]   ; 4a18 <CInit_Init+0x48>
49dc:   e5933000    ldr r3, [r3]
49e0:   e2033c01    and r3, r3, #256    ; 0x100
49e4:   e3530000    cmp r3, #0
49e8:   1a000007    bne 4a0c <CInit_Init+0x3c>
    HwrUart1Control = (
49ec:   e59f3028    ldr r3, [pc, #40]   ; 4a1c <CInit_Init+0x4c>
49f0:   e59f2028    ldr r2, [pc, #40]   ; 4a20 <CInit_Init+0x50>
49f4:   e5832000    str r2, [r3]
            HwUartControlDataLength8|
            HwUartControlFifoEnable|
            HwUartControlRate115200);
    BIT_SET(HwrSysControl1, HwSysControl1UartEnable);
49f8:   e59f3018    ldr r3, [pc, #24]   ; 4a18 <CInit_Init+0x48>
49fc:   e59f2014    ldr r2, [pc, #20]   ; 4a18 <CInit_Init+0x48>
4a00:   e5922000    ldr r2, [r2]
4a04:   e3822c01    orr r2, r2, #256    ; 0x100
4a08:   e5832000    str r2, [r3]

COM1_Init_I();
}
4a0c:   e28bd000    add sp, fp, #0
4a10:   e49db004    pop {fp}        ; (ldr fp, [sp], #4)
4a14:   e12fff1e    bx  lr
4a18:   80000100    .word   0x80000100
4a1c:   800004c0    .word   0x800004c0
4a20:   00070001    .word   0x00070001
2
If you are doing the call from assembly you could easily set up a very temporary stack of just - say 32-128 bytes by pointing the stack register to some static char[] or similar.Christoffer Bubach

2 Answers

3
votes

just set up the stack. Otherwise if you dont want a stack have no local variables and have little enough code that you dont run out of registers, dont call any functions from within this function, etc. If you really need variables then use global variables. The compiler CANNOT generate your code without using the stack if you cause the code to run out of registers and globals. No compiler switch will invent storage, and I wouldnt trust a compiler that has some non-stack non-standard trick it might try to use..

Simple, no local variables, doesnt even need an intermediate register:

unsigned int fun ( unsigned int a, unsigned int b )
{
    return(a+b);
}

no stack:

00000000 <fun>:
   0:   e0810000    add r0, r1, r0
   4:   e12fff1e    bx  lr

you can always change the stack after you call your initial C code, it takes one to a few instructions to set the stack before your C call, just set the stack pointer. You dont need all the stack pointers, just one. either set the stack pointer or write the function in assembler not C. You should be initializing the stack before a com port anyway. It is one instruction, two word locations and you are calling C so the cost is negligible.

.globl _start
_start:
    b reset
reset:
        ldr sp,=0x20008000
        bl more_fun
        b .

.globl fun
fun:
    bx lr

.globl fun_out
fun_out:
    bx lr



unsigned int fun ( unsigned int , unsigned int );
void fun_out ( unsigned int, unsigned int, unsigned int, unsigned int );
unsigned int more_fun ( unsigned int a, unsigned int b, unsigned int c )
{
    unsigned int d;
    d = fun(a,b);
    fun_out(a,b+c,b,a+c);
}

on instruction, two words and you can call C

   4:   e59fd00c    ldr sp, [pc, #12]   ; 18 <fun_out+0x4>
   8:   eb000003    bl  1c <more_fun>

...

  18:   20008000    andcs   r8, r0, r0

0000001c <more_fun>:
  1c:   e92d4070    push    {r4, r5, r6, lr}
  20:   e1a05002    mov r5, r2
  24:   e1a06000    mov r6, r0
  28:   e1a04001    mov r4, r1
  2c:   ebfffff7    bl  10 <fun>
  30:   e1a00006    mov r0, r6
  34:   e0853006    add r3, r5, r6
  38:   e0851004    add r1, r5, r4
  3c:   e1a02004    mov r2, r4
  40:   ebfffff3    bl  14 <fun_out>
  44:   e8bd8070    pop {r4, r5, r6, pc}
2
votes

You have about two options:

  1. Set up the stack and forget about it. You have to set the stack pointer in assembly anyway, as you can't do that in C (unless the compiler supports some non-standard extensions allowing direct access to registers).
  2. Comment out the original C function whose disassembly you presented above, copy the disassembly code, replace push/pop/bx with str/ldr/b as appropriate, (you will need to allocating a global variable to save/restore those non-volatile registers (fp?)), fix up other things if necessary and recompile it in an assembly module. EDIT: On a second thought, instead of taking the disassembly, translate that function into assembly using the -S switch, e.g. gcc -c cfile.c -S -o asmfile.s. That will probably save you some work.

I'd rather have a valid stack immediately.