STM32 Большой двоичный файл, созданный при использовании malloc

РЕШЕНО. СМОТРИТЕ РАЗДЕЛ РЕШЕНИЯ НИЖЕ.

У меня проблема, когда моя среда сборки выводит большой двоичный файл, и я надеюсь, что кто-то поможет мне снова двигаться.

Я использую процессор STM32F105, компилятор Eclipse, FreeRTOS и CodeSourcery, чтобы попытаться запустить на этом устройстве некоторый оценочный код AHRS. У меня работает большая часть кода, но я столкнулся с проблемой при реализации раздела кода eval, который использует malloc для выделения памяти. Мне пришлось добавить код для _sbrk, чтобы он скомпилировался, и теперь мой двоичный файл увеличился с 35 КБ до почти 400 МБ. Я думаю, что это проблема компоновщика, так как файл .out (до objcopy) примерно того же размера. Даже файлы .s, выводимые из objdump, выглядят довольно сопоставимыми.

Вот некоторые (надеюсь) относящиеся к делу фрагменты:

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) );

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

Добавление этого кода плюс вызов malloc приводит к увеличению двоичного файла почти до 400 МБ:

#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;
}

Есть мысли о том, как снова двигаться? Спасибо за любую помощь, которую вы можете оказать!

РЕШЕНИЕ

Из комментариев Notlikethat я увидел, что в процессе сборки создается еще один раздел кода, но в сценарии компоновщика не было раздела с таким же именем. Компоновщик решил поместить этот раздел в RAM, тогда как он должен был поместить его во FLASH. Поскольку он занимал RAM и FLASH, файл bin заполнял область между ними, создавая большой двоичный файл. Добавление следующей строки в сценарий компоновщика (в разделе FLASH) позволило снова построить код с нормальным размером.

*(.rodata.str1.4)

Новый полный скрипт компоновщика выглядит так:

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) );

Спасибо за помощь!


person Dan Puccio    schedule 11.02.2015    source источник
comment
Хорошая мысль. Я просмотрел файл .s после процесса сборки и не нашел ничего похожего на это. Список дополнительных переменных в исходном коде невелик, между хорошей (двоичный нормальный размер) и плохой (большой двоичный) сборкой. Есть ли какой-нибудь простой (не зависящий от человеческого глаза) способ проверить это?   -  person Dan Puccio    schedule 12.02.2015
comment
Неужели 400 МБ? Не 400КБ?   -  person TonyK    schedule 12.02.2015
comment
Да, 400 МБ. Довольно большой!   -  person Dan Puccio    schedule 12.02.2015
comment
Я бы написал *(.rodata.str1.4) как *(.rodata.*), чтобы охватить любые другие неожиданные разделы данных только для чтения, которые могут появиться, когда вы поддерживаете код. Я также поинтересовался, не стоит ли включать *(.data.*) сразу после *(.data) для подобного упрочнения. Но, по крайней мере, вы знаете, что ваши инструменты могут иногда создавать разделы с интересными названиями, которые необходимо учитывать в сценарии компоновщика.   -  person RBerteig    schedule 13.02.2015
comment
Отличная идея! Спасибо!   -  person Dan Puccio    schedule 13.02.2015


Ответы (1)


Похоже, что у вас что-то вставляется в раздел RAM таким образом, что скрипт компоновщика не улавливает. Вы можете использовать objdump или подобное в окончательном ELF, чтобы проверить таблицу символов и проверить на наличие чего-либо подозрительного, например создание некоторого тривиального кода с помощью этого скрипта компоновщика дает:

$ 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

В этом случае есть несколько символов с адресами ОЗУ, но то, что заставляет этот последний двоичный файл раздуться до ~ 400 МБ в этом случае, - это запись .note.gnu.build-id. Проверка заголовков разделов показывает, почему:

$ 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

Разделы .data и .bss имеют виртуальные адреса RAM (VMA), но адреса загрузки все еще находятся во флэш-памяти (LMA). С другой стороны, раздел заметок также имеет адрес загрузки ОЗУ, поэтому преобразование ELF в необработанный двоичный файл приводит к тому, что он помещается на ~ 400 МБ после других разделов изображения.

Судя по предоставленным дополнительным сведениям, кажется, что библиотечные функции генерируют свои собственные разделы .rodata, которые не совпадают ни с чем в скрипте, поэтому распределяются довольно произвольно эвристикой компоновщика. Я бы попробовал добавить *(.rodata.*) в раздел .text, чтобы поймать их.

person Notlikethat    schedule 12.02.2015
comment
Хорошо, я чувствую себя немного скованно, но разве вы не хотите, чтобы j в области памяти .data (RAM)? В ранее работавшей сборке у меня была объявлена ​​и определена глобальная переменная (внутренний CAN-адрес устройства), как и в вашем примере, и которая, похоже, помещается в .data. 2000002c l O .data 00000001 NMEA_Source_Address - person Dan Puccio; 12.02.2015
comment
Ой, извините, оказывается, несмотря на воспроизведение проблемы, мои рассуждения были немного неправильными. Сценарий компоновщика достаточно умен, чтобы обрабатывать перемещаемый раздел .data, но другие примитивы ускользают. - person Notlikethat; 12.02.2015
comment
Ничего себе, Notlikethat, спасибо за вашу помощь в этом. Не могу передать, насколько я это ценю. Я выделил проблему в исходном коде одним вызовом sqrt. Если я это закомментирую, то двоичный файл будет иметь нормальный размер. Если я вставлю его обратно, двоичный файл станет очень большим. Я уверен, что то, что вы сказали, происходит, потому что я изменил адреса в сценариях компоновщика, и размер встроенного двоичного файла изменился, поэтому что-то охватывает ОЗУ и ФЛЭШ-память. При неудачной сборке появляется новый раздел под названием .rodata.str1.4. Похоже, что это идет в ОЗУ. - person Dan Puccio; 12.02.2015
comment
Думаю, я начинаю понимать ваш комментарий. Я не понимаю, как создается этот раздел (поскольку этого имени нет в сценарии компоновщика), а также как его исправить. - person Dan Puccio; 12.02.2015
comment
Отличная работа - наверное, стоит добавить эти детали к вопросу для потомков. Я думаю, вам не хватает того, что компилятор / ассемблер создает входные разделы, а компоновщик объединяет их в выходные разделы. Если в сценарии компоновщика не указано, где разместить конкретный раздел ввода, он более или менее просто помещается в любое место, где работает (см. Документы, на которые я ссылался; нет, я действительно не понимаю этого или ;) - person Notlikethat; 12.02.2015
comment
Я добавил в скрипт компоновщика раздел под названием * (. Rodata.str1.4) в области FLASH, и это, похоже, позволяет создавать код нормального размера. Теперь WDT вызывает сброс устройства, но это отдельная проблема. Спасибо за вашу помощь Notlikethat. Я очень ценю это! - person Dan Puccio; 12.02.2015