assemblyarmstm32gnu-assemblerstm32cubeide

Local variable in .c file overwrites memory address previously aquired in ARM GNU assembly file


I have implemented a basic FIFO data structure with its related functions/subroutines in a FIFO.s file and want to use them in the main function inside a main.c file;

// FIFO.s
.syntax unified
.cpu    cortex-m4
.fpu    softvfp
.thumb

.equ FIFO_CAPACITY_POINTER_SIZE, 4
.equ FIFO_LENGTH_POINTER_SIZE,   4
.equ FIFO_PUT_POINTER_SIZE,      4
.equ FIFO_GET_POINTER_SIZE,      4
.equ FIFO_DATA_SIZE,             4
.equ FIFO_CAPACITY,              10

.equ FIFO_Size, FIFO_CAPACITY_POINTER_SIZE + FIFO_LENGTH_POINTER_SIZE + \
                FIFO_PUT_POINTER_SIZE      + FIFO_GET_POINTER_SIZE    + \
                (FIFO_CAPACITY * FIFO_DATA_SIZE)

.section .data
FIFO:
FIFO_CAPACITY_POINTER: .space FIFO_CAPACITY_POINTER_SIZE
FIFO_LENGTH_POINTER:   .space FIFO_LENGTH_POINTER_SIZE
FIFO_PUT_POINTER:      .space FIFO_PUT_POINTER_SIZE
FIFO_GET_POINTER:      .space FIFO_GET_POINTER_SIZE
FIFO_DATA:             .space (FIFO_CAPACITY * FIFO_DATA_SIZE)

.section .text
.globl FIFO_Init
.globl FIFO_Put
.globl FIFO_Get

FIFO_Init:
    LDR R6, =FIFO_CAPACITY
    LDR R7, =FIFO_CAPACITY_POINTER
    STR R6, [R7]

    MOV R6, #0
    LDR R7, =FIFO_LENGTH_POINTER
    STR R6, [R7]

    LDR R6, =FIFO_DATA
    LDR R7, =FIFO_PUT_POINTER
    STR R6, [R7]
    LDR R7, =FIFO_GET_POINTER
    STR R6, [R7]
    // Return.
    BX LR
////////////////////////////////////////////////////////////////////////////////
FIFO_Put:
    /*
        If Length >= Capacity then FIFO is full and we return, doing nothing.
    */
    LDR   R7, =FIFO_LENGTH_POINTER
    LDR   R6, [R7]
    LDR   R5, =FIFO_CAPACITY_POINTER
    LDR   R5, [R5]
    CMP   R6, R5
    IT    GE
    BXGE  LR
    /*
        Add 1 to Length.
    */
    ADD R6, #1
    STR R6, [R7]
    /*
        Store the value passed to us in R0 where FIFO_PUT_POINTER is pointing.
    */
    LDR R7, =FIFO_PUT_POINTER
    LDR R6, [R7]
    STR R0, [R6]
    /*
        If FIFO_PUT_POINTER is going out of range then reset it to FIFO_DATA;
        Else increment it by FIFO_DATA_SIZE.
    */
    ADD   R6, #FIFO_DATA_SIZE
    LDR   R5, =(FIFO + FIFO_Size)
    CMP   R6, R5
    IT    GE
    LDRGE R6, =FIFO_DATA
    STR   R6, [R7]
    /*
        Return.
    */
    BX LR
////////////////////////////////////////////////////////////////////////////////
FIFO_Get:
    /*
        If Length = 0 then FIFO is empty and we return, doing nothing.
    */
    LDR   R7, =FIFO_LENGTH_POINTER
    LDR   R6, [R7]
    CMP   R6, #0
    IT    LE
    BXLE  LR
    /*
        Subtract 1 from Length.
    */
    SUB R6, #1
    STR R6, [R7]
    /*
        Put the return value pointed at by FIFO_GET_PTR in R0.
    */
    LDR R7, =FIFO_GET_POINTER
    LDR R6, [R7]
    LDR R0, [R6]
    /*
        If FIFO_GET_POINTER is going out of range then reset it to FIFO_DATA;
        Else increment it by FIFO_DATA_SIZE.
    */
    ADD   R6, #FIFO_DATA_SIZE
    LDR   R5, =(FIFO + FIFO_Size)
    CMP   R6, R5
    IT    GE
    LDRGE R6, =FIFO_DATA
    STR   R6, [R7]
    /*
        Return.
    */
    BX LR
////////////////////////////////////////////////////////////////////////////////
.align
.end

This data structure is stored in memory from 0x20000000 to 0x20000038.

My main.c file looks like this:

// main.c
#include <stdint.h>

void FIFO_Init(void);
void FIFO_Put(uint32_t data);
uint32_t FIFO_Get(void);

int main(void)
{
    FIFO_Init();

    FIFO_Put(1);
    FIFO_Put(2);
    FIFO_Put(3);
    FIFO_Put(4);
    FIFO_Put(5);
    FIFO_Put(6);
    FIFO_Put(7);
    FIFO_Put(8);
    FIFO_Put(9);
    FIFO_Put(10);

    // Will not be added to FIFO!
    FIFO_Put(11);
    FIFO_Put(12);

    uint32_t sum = FIFO_Get(); // Error: FIFO_PUT_POINTER overwritten by "sum".
    
    sum += FIFO_Get();         // Error: FIFO_PUT_POINTER overwritten by "sum".
    sum += FIFO_Get();         // Error: FIFO_PUT_POINTER overwritten by "sum".
    sum += FIFO_Get();         // Error: FIFO_PUT_POINTER overwritten by "sum".
    sum += FIFO_Get();         // Error: FIFO_PUT_POINTER overwritten by "sum".
    sum += FIFO_Get();         // Error: FIFO_PUT_POINTER overwritten by "sum".
    sum += FIFO_Get();         // Error: FIFO_PUT_POINTER overwritten by "sum".
    sum += FIFO_Get();         // Error: FIFO_PUT_POINTER overwritten by "sum".
    sum += FIFO_Get();         // Error: FIFO_PUT_POINTER overwritten by "sum".
    sum += FIFO_Get();         // Error: FIFO_PUT_POINTER overwritten by "sum".

    FIFO_Put(sum); // Error: FIFO_PUT_POINTER has invalid address 0x00000037.

    return 0;
}

Build commands that STM32CubeIDE version 1.19.0 uses (the Assembler and Linker seem to be version 2.42.0):

08:24:55 **** Build of configuration Debug for project 22_WorkingWithDataStructures ****
make -j4 all 
arm-none-eabi-gcc -mcpu=cortex-m4 -g3 -DDEBUG -c -x assembler-with-cpp -MMD -MP -MF"Startup/startup_stm32f303retx.d" -MT"Startup/startup_stm32f303retx.o" --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -o "Startup/startup_stm32f303retx.o" "../Startup/startup_stm32f303retx.s"
arm-none-eabi-gcc -mcpu=cortex-m4 -g3 -DDEBUG -c -x assembler-with-cpp -MMD -MP -MF"Src/FIFO.d" -MT"Src/FIFO.o" --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -o "Src/FIFO.o" "../Src/FIFO.s"
arm-none-eabi-gcc "../Src/main.c" -mcpu=cortex-m4 -std=gnu11 -g3 -DDEBUG -DSTM32 -DSTM32F3 -DSTM32F303RETx -c -I../Inc -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -fcyclomatic-complexity -MMD -MP -MF"Src/main.d" -MT"Src/main.o" --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -o "Src/main.o"
arm-none-eabi-gcc "../Src/syscalls.c" -mcpu=cortex-m4 -std=gnu11 -g3 -DDEBUG -DSTM32 -DSTM32F3 -DSTM32F303RETx -c -I../Inc -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -fcyclomatic-complexity -MMD -MP -MF"Src/syscalls.d" -MT"Src/syscalls.o" --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -o "Src/syscalls.o"
arm-none-eabi-gcc "../Src/sysmem.c" -mcpu=cortex-m4 -std=gnu11 -g3 -DDEBUG -DSTM32 -DSTM32F3 -DSTM32F303RETx -c -I../Inc -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -fcyclomatic-complexity -MMD -MP -MF"Src/sysmem.d" -MT"Src/sysmem.o" --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -o "Src/sysmem.o"
arm-none-eabi-gcc -mcpu=cortex-m4 -g3 -DDEBUG -c -x assembler-with-cpp -MMD -MP -MF"Inc/STM32F303RE.d" -MT"Inc/STM32F303RE.o" --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -o "Inc/STM32F303RE.o" "../Inc/STM32F303RE.s"
arm-none-eabi-gcc -o "22_WorkingWithDataStructures.elf" @"objects.list"   -mcpu=cortex-m4 -T"X:\XXXXXXXXXX\22_WorkingWithDataStructures\STM32F303RETX_FLASH.ld" --specs=nosys.specs -Wl,-Map="22_WorkingWithDataStructures.map" -Wl,--gc-sections -static --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -Wl,--start-group -lc -lm -Wl,--end-group
Finished building target: 22_WorkingWithDataStructures.elf
 
arm-none-eabi-size  22_WorkingWithDataStructures.elf 
arm-none-eabi-objdump -h -S 22_WorkingWithDataStructures.elf  > "22_WorkingWithDataStructures.list"
   text    data     bss     dec     hex filename
   1104      56    1568    2728     aa8 22_WorkingWithDataStructures.elf
Finished building: default.size.stdout
 
Finished building: 22_WorkingWithDataStructures.list
 

08:25:57 Build Finished. 0 errors, 0 warnings. (took 1m:2s.223ms)

The value of local variable number (24) will be stored in memory address 0x20000008 which was reserved in the FIFO.s file to hold the Put_Ptr value of the FIFO data structure! In other words, number and Put_ptr share the same memory location!

I've checked and debugged all my functions/subroutines and they don't have any problems (it's not FIFO_Get that's doing the overwriting).

If I declare number in the .data section of the FIFO.s file and import it to my main.c file then everything works! But when I declare it as a local variable in the main function of my main.c file, it doesn't work.

Why is this happening and how can it be fixed?

Edit: As @AndreyTurkin (my thanks to him) has said in the comments, The problem was that I was using register R7 in my subroutines in FIFO.s so I changed them to only work with registers R0-R3 and there seems to be no problems.

New version of FIFO.s:

.syntax unified
.cpu    cortex-m4
.fpu    softvfp
.thumb

.equ FIFO_CAPACITY_POINTER_SIZE, 4
.equ FIFO_LENGTH_POINTER_SIZE,   4
.equ FIFO_PUT_POINTER_SIZE,      4
.equ FIFO_GET_POINTER_SIZE,      4
.equ FIFO_DATA_SIZE,             4
.equ FIFO_CAPACITY,              10

.equ FIFO_SIZE, FIFO_CAPACITY_POINTER_SIZE + FIFO_LENGTH_POINTER_SIZE + \
                FIFO_PUT_POINTER_SIZE      + FIFO_GET_POINTER_SIZE    + \
                (FIFO_CAPACITY * FIFO_DATA_SIZE)

.section .data
FIFO:
FIFO_CAPACITY_POINTER: .space FIFO_CAPACITY_POINTER_SIZE
FIFO_LENGTH_POINTER:   .space FIFO_LENGTH_POINTER_SIZE
FIFO_PUT_POINTER:      .space FIFO_PUT_POINTER_SIZE
FIFO_GET_POINTER:      .space FIFO_GET_POINTER_SIZE
FIFO_DATA:             .space (FIFO_CAPACITY * FIFO_DATA_SIZE)

.section .text
.globl FIFO_Init
.globl FIFO_Put
.globl FIFO_Get
.globl FIFO_Capacity
.globl FIFO_Length
.globl FIFO_Size

FIFO_Init:
    /*
        FIFO_CAPACITY_POINTER will hold the value "FIFO_CAPACITY".
    */
    LDR R0, =FIFO_CAPACITY
    LDR R1, =FIFO_CAPACITY_POINTER
    STR R0, [R1], #FIFO_CAPACITY_POINTER_SIZE
    /*
        FIFO_LENGTH_POINTER will hold the value 0.
    */
    MOV R0, #0
    STR R0, [R1], #FIFO_LENGTH_POINTER_SIZE
    /*
        Both FIFO_PUT_POINTER and FIFO_GET_POINTER will hold the starting
        address of "FIFO_DATA".
    */
    LDR R0, =FIFO_DATA
    STR R0, [R1], #FIFO_PUT_POINTER_SIZE
    STR R0, [R1]
    /*
        Return.
    */
    BX LR
///////////////////////////////////////////////////////////////////////////////
FIFO_Put:
    /*
        If Length >= Capacity then FIFO is full and we return 0 (failed).
    */
    LDR   R3, =FIFO_CAPACITY_POINTER
    LDR   R1, [R3], #FIFO_CAPACITY_POINTER_SIZE
    LDR   R2, [R3]
    CMP   R2, R1
    ITT   GE
    MOVGE R0, #0
    BXGE  LR
    /*
        Add 1 to Length.
    */
    ADD R2, #1
    STR R2, [R3], #FIFO_LENGTH_POINTER_SIZE
    /*
        Store the value passed to us in R0 where FIFO_PUT_POINTER is pointing.
    */
    LDR R1, [R3]
    STR R0, [R1]
    /*
        If FIFO_PUT_POINTER is going out of range then reset it to FIFO_DATA;
        Else increment it by FIFO_DATA_SIZE.
    */
    ADD   R1, #FIFO_DATA_SIZE
    LDR   R0, =(FIFO + FIFO_SIZE)
    CMP   R1, R0
    IT    GE
    LDRGE R1, =FIFO_DATA
    STR   R1, [R3]
    /*
        Return with "return code" 1.
    */
    MOV R0, #1
    BX  LR
///////////////////////////////////////////////////////////////////////////////
FIFO_Get:
    /*
        If Length = 0 then FIFO is empty and we return, doing nothing.
    */
    LDR  R2, =FIFO_LENGTH_POINTER
    LDR  R1, [R2]
    CMP  R1, #0
    IT   LE
    BXLE LR
    /*
        Subtract 1 from Length.
    */
    SUB R1, #1
    STR R1, [R2], #(FIFO_LENGTH_POINTER_SIZE + FIFO_PUT_POINTER_SIZE)
    /*
        Put return value in R0.
    */
    LDR R1, [R2]
    LDR R0, [R1]
    /*
        If FIFO_GET_POINTER is going out of range then reset it to FIFO_DATA;
        Else increment it by FIFO_DATA_SIZE.
    */
    ADD   R1, #FIFO_DATA_SIZE
    LDR   R3, =(FIFO + FIFO_SIZE)
    CMP   R1, R3
    IT    GE
    LDRGE R1, =FIFO_DATA
    STR   R1, [R2]
    /*
        Return.
    */
    BX LR
///////////////////////////////////////////////////////////////////////////////
FIFO_Capacity:
    LDR R0, =FIFO_CAPACITY
    /*
        Return.
    */
    BX LR
///////////////////////////////////////////////////////////////////////////////
FIFO_Length:
    LDR R0, =FIFO_LENGTH_POINTER
    LDR R0, [R0]
    /*
        Return.
    */
    BX LR
///////////////////////////////////////////////////////////////////////////////
FIFO_Size:
    LDR R0, =FIFO_SIZE
    /*
        Return.
    */
    BX LR
///////////////////////////////////////////////////////////////////////////////
.align
.end

P.S: I'm using STM32CubeIDE version 1.19.0 that uses the ARM GNU Assembler to program a STM32 NUCLEO-F303RE micro-controller (if it's a necessary piece of information for answering this question).


Solution

  • As @AndreyTurkin has said in the comments (my thanks to him), in my subroutines in FIFO.s I used and modified register R7, which according to ARM's AAPCS ABI, is callee-preserved and is used in the main function of main.c as a Frame Pointer. That caused the memory corruption problem.

    I changed my subroutines in FIFO.s to only use registers R0 through R3, and there seems to be no more problems.

    New version of FIFO.s:

    .syntax unified
    .cpu    cortex-m4
    .fpu    softvfp
    .thumb
    
    .equ FIFO_CAPACITY_POINTER_SIZE, 4
    .equ FIFO_LENGTH_POINTER_SIZE,   4
    .equ FIFO_PUT_POINTER_SIZE,      4
    .equ FIFO_GET_POINTER_SIZE,      4
    .equ FIFO_DATA_SIZE,             4
    .equ FIFO_CAPACITY,              10
    
    .equ FIFO_SIZE, FIFO_CAPACITY_POINTER_SIZE + FIFO_LENGTH_POINTER_SIZE + \
                    FIFO_PUT_POINTER_SIZE      + FIFO_GET_POINTER_SIZE    + \
                    (FIFO_CAPACITY * FIFO_DATA_SIZE)
    
    .section .data
    FIFO:
    FIFO_CAPACITY_POINTER: .space FIFO_CAPACITY_POINTER_SIZE
    FIFO_LENGTH_POINTER:   .space FIFO_LENGTH_POINTER_SIZE
    FIFO_PUT_POINTER:      .space FIFO_PUT_POINTER_SIZE
    FIFO_GET_POINTER:      .space FIFO_GET_POINTER_SIZE
    FIFO_DATA:             .space (FIFO_CAPACITY * FIFO_DATA_SIZE)
    
    .section .text
    .globl FIFO_Init
    .globl FIFO_Put
    .globl FIFO_Get
    .globl FIFO_Capacity
    .globl FIFO_Length
    .globl FIFO_Size
    
    FIFO_Init:
        /*
            FIFO_CAPACITY_POINTER will hold the value "FIFO_CAPACITY".
        */
        LDR R0, =FIFO_CAPACITY
        LDR R1, =FIFO_CAPACITY_POINTER
        STR R0, [R1], #FIFO_CAPACITY_POINTER_SIZE
        /*
            FIFO_LENGTH_POINTER will hold the value 0.
        */
        MOV R0, #0
        STR R0, [R1], #FIFO_LENGTH_POINTER_SIZE
        /*
            Both FIFO_PUT_POINTER and FIFO_GET_POINTER will hold the starting
            address of "FIFO_DATA".
        */
        LDR R0, =FIFO_DATA
        STR R0, [R1], #FIFO_PUT_POINTER_SIZE
        STR R0, [R1]
        /*
            Return.
        */
        BX LR
    ///////////////////////////////////////////////////////////////////////////////
    FIFO_Put:
        /*
            If Length >= Capacity then FIFO is full and we return 0 (failed).
        */
        LDR   R3, =FIFO_CAPACITY_POINTER
        LDR   R1, [R3], #FIFO_CAPACITY_POINTER_SIZE
        LDR   R2, [R3]
        CMP   R2, R1
        ITT   GE
        MOVGE R0, #0
        BXGE  LR
        /*
            Add 1 to Length.
        */
        ADD R2, #1
        STR R2, [R3], #FIFO_LENGTH_POINTER_SIZE
        /*
            Store the value passed to us in R0 where FIFO_PUT_POINTER is pointing.
        */
        LDR R1, [R3]
        STR R0, [R1]
        /*
            If FIFO_PUT_POINTER is going out of range then reset it to FIFO_DATA;
            Else increment it by FIFO_DATA_SIZE.
        */
        ADD   R1, #FIFO_DATA_SIZE
        LDR   R0, =(FIFO + FIFO_SIZE)
        CMP   R1, R0
        IT    GE
        LDRGE R1, =FIFO_DATA
        STR   R1, [R3]
        /*
            Return with "return code" 1.
        */
        MOV R0, #1
        BX  LR
    ///////////////////////////////////////////////////////////////////////////////
    FIFO_Get:
        /*
            If Length = 0 then FIFO is empty and we return, doing nothing.
        */
        LDR  R2, =FIFO_LENGTH_POINTER
        LDR  R1, [R2]
        CMP  R1, #0
        IT   LE
        BXLE LR
        /*
            Subtract 1 from Length.
        */
        SUB R1, #1
        STR R1, [R2], #(FIFO_LENGTH_POINTER_SIZE + FIFO_PUT_POINTER_SIZE)
        /*
            Put return value in R0.
        */
        LDR R1, [R2]
        LDR R0, [R1]
        /*
            If FIFO_GET_POINTER is going out of range then reset it to FIFO_DATA;
            Else increment it by FIFO_DATA_SIZE.
        */
        ADD   R1, #FIFO_DATA_SIZE
        LDR   R3, =(FIFO + FIFO_SIZE)
        CMP   R1, R3
        IT    GE
        LDRGE R1, =FIFO_DATA
        STR   R1, [R2]
        /*
            Return.
        */
        BX LR
    ///////////////////////////////////////////////////////////////////////////////
    FIFO_Capacity:
        LDR R0, =FIFO_CAPACITY
        /*
            Return.
        */
        BX LR
    ///////////////////////////////////////////////////////////////////////////////
    FIFO_Length:
        LDR R0, =FIFO_LENGTH_POINTER
        LDR R0, [R0]
        /*
            Return.
        */
        BX LR
    ///////////////////////////////////////////////////////////////////////////////
    FIFO_Size:
        LDR R0, =FIFO_SIZE
        /*
            Return.
        */
        BX LR
    ///////////////////////////////////////////////////////////////////////////////
    .align
    .end