GNU Binutils: the ELF Swiss Army Knife | Interrupt

great post.

I have some comments that I thought I’d publish in a previous post (Tracking Firmware Code Size | Interrupt). But I think they’re probably more relevant to the topics of this article.

The question concerns the vma and lma that can be expressed in the linker script with the keyword AT.

let’s take the following linker script:

/* Entry Point */
ENTRY(main)

/* Specify the memory areas */
MEMORY
{
  FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 128K
  RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 32K
}

/* Define output sections */
SECTIONS
{
  /* The program code and other data goes into FLASH */
  .text :
  {
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
    *(.eh_frame)

    KEEP (*(.init))
    KEEP (*(.fini))

    . = ALIGN(4);
    _etext = .;        /* define a global symbols at end of code */
  } >FLASH

  /* Constant data goes into FLASH */
  .rodata :
  {
    . = ALIGN(4);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
  } >FLASH

  .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
  .ARM : {
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
  } >FLASH

  /* Initialized data sections goes into RAM, load LMA copy after code */
  .data : 
  {
    . = ALIGN(4);
    *(.data*)          /* .data* sections */

    . = ALIGN(4);
  } >RAM AT> FLASH
  
  /* Uninitialized data section */
  .bss :
  {
    . = ALIGN(4);
    *(.bss*)
    *(COMMON)

    . = ALIGN(4);
  } >RAM

  /* Remove information from the standard libraries */
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }

  .ARM.attributes 0 : { *(.ARM.attributes) }  
}

I’d like you to put the attention on the .data and .bss sections:

  .data : 
  {
    . = ALIGN(4);
    *(.data*)          /* .data* sections */

    . = ALIGN(4);
  } >RAM AT> FLASH
  
  /* Uninitialized data section */
  .bss :
  {
    . = ALIGN(4);
    *(.bss*)
    *(COMMON)

    . = ALIGN(4);
  } >RAM

I try to explain it in words: we are saying that .data has vma in RAM but lma in FLASH, instead .bss I expect it to have vma and lma identical and in RAM.

this is explained in 3.1 Basic Linker Script Concepts and in 3.6.8.2 Output Section LMA

I consider a very simple source file:

static int g_my_global_bss;
static int g_my_global_data = 37;
const int g_my_global_rodata = 45;
int main(void)
{
    g_my_global_bss = g_my_global_data + g_my_global_rodata;
    return 0;
}

I build the elf with this makefile:

all: main.elf

CFLAGS = \
	-std=gnu11 \
	-mcpu=cortex-m4 \
	-mthumb \
	-specs=nano.specs \
	-O0 \
	-Wall \
	-ffunction-sections \
	-fdata-sections \
	-c \
	-Werror

LDFLGS =  \
	-mcpu=cortex-m4 \
	-mthumb \
	-specs=nano.specs \
	-Wl,--gc-sections \
	-Wl,--print-memory-usage

LD_SCRIPT = STM32F410RBTx_FLASH.ld

CC = arm-none-eabi-gcc

main.elf: main.o
	$(CC) $(LDFLGS) -T$(LD_SCRIPT) -Wl,--cref,-Map=$(@:.elf=.map) -o $@ $^

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

.PHONY: clean

clean:
	rm *.o *.elf *.map

linker tells me:

Memory region         Used Size  Region Size  %age Used
	   FLASH:          64 B       128 KB      0.05%
	     RAM:           8 B        32 KB      0.02%

and size tells me:

max@jarvis:~/Dropbox/test_mem$ arm-none-eabi-size -G main.elf
      text       data        bss      total filename
	60          4          4         68 main.elf

then I extract the information of vma and lma from the elf using objdump.

max@jarvis:~/Dropbox/test_mem$ arm-none-eabi-objdump -h main.elf

main.elf:     file format elf32-littlearm

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         0000003c  08000000  08000000  00010000  2**2
		  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .rodata       00000000  0800003c  0800003c  00020004  2**0
		  CONTENTS, ALLOC, LOAD, DATA
  2 .data         00000004  20000000  0800003c  00020000  2**2
		  CONTENTS, ALLOC, LOAD, DATA
  3 .bss          00000004  20000004  08000040  00020004  2**2
		  ALLOC
  4 .ARM.attributes 0000002a  00000000  00000000  00020004  2**0
		  CONTENTS, READONLY
  5 .comment      00000079  00000000  00000000  0002002e  2**0
		  CONTENTS, READONLY

again I highlight .data and .bss (and also .text for comparison)

Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         0000003c  08000000  08000000  00010000  2**2
  2 .data         00000004  20000000  0800003c  00020000  2**2
  3 .bss          00000004  20000004  08000040  00020004  2**2
  • .text has vma and lma coinciding and in FLASH. I expected this
  • .data has vma in RAM and lma in FLASH. I expected this
  • .bss has vma in RAM and lma in FLASH as .data. I wasn’t expecting that.

why this unexpected behavior? What am I missing? Is this a bug?

best regards
Max