linkerarmmallocstm32codesourcery

STM32 Large binary created when using malloc


SOLVED. SEE THE SOLUTION SECTION BELOW.

I've got a problem where my build environment is outputting a large binary file, and I'm hoping that someone can help get me moving again.

I'm using an STM32F105 processor, Eclipse, FreeRTOS, and CodeSourcery compiler to try to get some AHRS evaluation code running on this device. I have much of the code running, but I ran into a problem when implementing the section of the eval code which uses malloc to allocate memory. I had to add some code for _sbrk to get it to compile, and now my binary went from 35K to almost 400MB. I think that this is a linker problem, as the .out file (before objcopy) is about the same size. Even the .s files output from the objdump look pretty comparable.

Here are some (hopefully) relevant bits and pieces:

MEMORY
{
  RAM      (RWX) : ORIGIN = 0x20000000, LENGTH = 20K
  FLASH    (RX)  : ORIGIN = 0x08000000, LENGTH = 128K
}

_estack = ORIGIN(RAM)+LENGTH(RAM);

SECTIONS
{
  .text ORIGIN(FLASH):
  {
    *(.isr_vector)
    *(.text)
    *(.text.*)
    *(.rodata)
    _sidata = .;
  }

  .data ORIGIN(RAM):
  AT (_sidata) 
  {
    _sdata = . ;
    *(.data)
    _edata = . ;
  }

  .bss (_edata) (NOLOAD):
  {
    _sbss = .;
    *(.bss)
    *(.bss.*)
    *(COMMON)
    _ebss = . ;
    . = ALIGN(4);
   _end = .;
  }
}


/* end of allocated ram _end */
PROVIDE( _HEAP_START = _end );

/* end of the heap -> align 4 byte */ 
PROVIDE ( _HEAP_END = ALIGN(ORIGIN(RAM) + LENGTH(RAM) - 4 ,4) );

The makefile:

BOOT = boot
RTOS = FreeRTOSSource
FREERTOS = $(RTOS)/port.c $(RTOS)/tasks.c $(RTOS)/croutine.c $(RTOS)/heap_2.c $(RTOS)/list.c $(RTOS)/queue.c 
APP_SOURCE = app_source/sysmon.c app_source/hscan.c app_source/gps.c app_source/mems.c app_source/gpio.c app_source/mainstates.c app_source/leds.c app_source/database.c
INEMO_LIB = inemo/mems/LSM303DLH.c inemo/mems/L3GD20.c

OBJS = main.o \
      $(BOOT)/core_cm3.o \
      $(BOOT)/system_stm32f10x.o \
      $(BOOT)/stm32f10x_rcc.o \
      $(BOOT)/stm32f10x_gpio.o \
      $(BOOT)/stm32f10x_can.o \
      $(BOOT)/stm32f10x_iwdg.o \
      $(BOOT)/stm32f10x_i2c.o \
      $(BOOT)/startup.o \
      $(BOOT)/mx_gpio.o \
      $(FREERTOS:%.c=%.o) \
      $(INEMO_LIB:%.c=%.o) \
      $(APP_SOURCE:%.c=%.o)

CFLAGS = -O0 -gdwarf-2 -mcpu=cortex-m3 -mthumb -fno-common -I$(BOOT) -std=gnu99 -c -mfloat-abi=soft -Wall -g
LFLAGS  = -mthumb -mcpu=cortex-m3 -Tscripts/stm32f103.ld -nostartfiles -lgcc -lm -lc -mfloat-abi=soft -Wall -g -O0
CPFLAGS = -O binary 

TARGET = arm-none-eabi
#TARGET = arm-elf

CC = $(TARGET)-gcc

LD = $(TARGET)-gcc
CP = $(TARGET)-objcopy
OD = $(TARGET)-objdump

all: version $(OBJS) link

$(BOOT)/startup.o:
    $(CC) $(CFLAGS) $(BOOT)/startup_stm32f10x_cl.s -lm -lc -lnosys -o $@

%.o: %.c
    $(CC) $(CFLAGS) $< -o $@

version:
    $(CC) --version

link: $(OBJS)
    $(LD) -o main.out $(OBJS) $(LFLAGS) 
    $(CP) $(CPFLAGS) main.out main.bin
    $(OD) -D -h main.out > main.S

clean:
    rm -rf $(OBJS) main.bin main.out main.S

The addition of this code plus a call to malloc causes the binary to grow to almost 400MB:

#include <sys/types.h>

extern unsigned int _HEAP_START;
caddr_t * _sbrk(int incr) {

      static unsigned char *heap = NULL;
      unsigned char *prev_heap;
      if (heap == NULL) {
          heap = (unsigned char *)_HEAP_START;
      }

      prev_heap = heap;
      heap += incr;
      return (caddr_t) prev_heap;
}

Any thoughts on how to get moving again? Thanks for any help you can provide!

THE SOLUTION

With the comments of Notlikethat, I saw that another section of code was getting created in the build process, but the linker script didn't have a section with the same name. The linker decided to put this section in RAM, when it should have put it in FLASH. Since it spanned RAM and FLASH, the bin file filled the area between them, causing the large binary. Adding the following line to the linker script (in the FLASH section), allowed the code to build again at a normal size.

*(.rodata.str1.4)

The new complete linker script looks like this:

MEMORY
{
  RAM      (RWX) : ORIGIN = 0x20000000, LENGTH = 20K
  FLASH    (RX)  : ORIGIN = 0x08000000, LENGTH = 128K
}

_estack = ORIGIN(RAM)+LENGTH(RAM);

SECTIONS
{
  .text ORIGIN(FLASH):
  {
    *(.isr_vector)
    *(.text)
    *(.text.*)
    *(.rodata)
    *(.rodata.str1.4)
    _sidata = .;
  }

  .data ORIGIN(RAM):
  AT (_sidata) 
  {
    _sdata = . ;
    *(.data)
    _edata = . ;
  }

  .bss (_edata) (NOLOAD):
  {
    _sbss = .;
    *(.bss)
    *(.bss.*)
    *(COMMON)
    _ebss = . ;
    . = ALIGN(4);
   _end = .;
  }
}


/* end of allocated ram _end */
PROVIDE( _HEAP_START = _end );

/* end of the heap -> align 4 byte */ 
PROVIDE ( _HEAP_END = ALIGN(ORIGIN(RAM) + LENGTH(RAM) - 4 ,4) );

Thanks for the help!


Solution

  • It looks like you have something inserting itself into a RAM section in a way the linker script doesn't catch. You can use objdump or similar on the final ELF to inspect the symbol table and check for anything suspicious, e.g. building some trivial code with this linker script gives:

    $ arm-none-eabi-objdump -t a.out
    
    a.out:     file format elf32-littlearm
    
    SYMBOL TABLE:
    20000000 l    d  .note.gnu.build-id 00000000 .note.gnu.build-id
    08000000 l    d  .text  00000000 .text
    20000000 l    d  .data  00000000 .data
    20000004 l    d  .bss   00000000 .bss
    00000000 l    d  .comment   00000000 .comment
    00000000 l    d  .ARM.attributes    00000000 .ARM.attributes
    00000000 l    df *ABS*  00000000 test.c
    00000000 l    df *ABS*  00000000 sum.c
    00000000 l    df *ABS*  00000000 
    20000000 g     O .data  00000004 j
    080000c0 g       .text  00000000 _sidata
    20000004 g       .bss   00000000 _sbss
    08000038 g     F .text  0000003c sum
    20000000 g       .data  00000000 _sdata
    080000bc g     O .text  00000004 k
    20000008 g       .bss   00000000 _ebss
    20000004 g     O .bss   00000004 i
    08000000 g     F .text  00000038 main
    08000074 g     F .text  00000048 sum2
    20005000 g       *ABS*  00000000 _estack
    20000004 g       .data  00000000 _edata
    20000008 g       .bss   00000000 _end
    

    In this case, there are a few symbols with RAM addresses, but what causes this final binary to blow up to ~400MB in this case is the .note.gnu.build-id entry. Checking the section headers reveals why:

    $ arm-none-eabi-objdump -h a.out
    
    a.out:     file format elf32-littlearm
    
    Sections:
    Idx Name          Size      VMA       LMA       File off  Algn
      0 .note.gnu.build-id 00000024  20000000  20000000  00030000  2**2
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      1 .text         00000074  08000000  08000000  00010000  2**2
                      CONTENTS, ALLOC, LOAD, READONLY, CODE
      2 .data         00000004  20000000  08000074  00020000  2**2
                      CONTENTS, ALLOC, LOAD, DATA
      3 .bss          00000004  20000004  08000078  00020004  2**2
                      ALLOC
      4 .comment      00000036  00000000  00000000  00030024  2**0
                      CONTENTS, READONLY
      5 .ARM.attributes 00000033  00000000  00000000  0003005a  2**0
                      CONTENTS, READONLY
    

    The .data and .bss sections have RAM virtual addresses (VMA), but load addresses still in flash (LMA). The note section on the other hand has a RAM load address as well, so converting the ELF to a raw binary causes it to placed ~400MB after the other sections in the image.

    From the extra details provided, it sounds like library functions are generating their own .rodata sections which don't match anything in the script, thus get allocated fairly arbitrarily by the linker's heuristics. I'd try adding *(.rodata.*) to the .text section to catch those.