1
votes

let's say I run arm-none-eabi-objcopy firmwared.elf -O ihex firmware.hex

Assume the binary was generated with the following linker script:

ENTRY(Reset_Handler)

MEMORY
{
  FLASH (RX) : ORIGIN = 0x08020000, LENGTH = 896K
  SRAM (RWX) : ORIGIN = 0x20000000, LENGTH = 512K
  BKPSRAM (RW) : ORIGIN = 0x40024000, LENGTH = 4K
}

_estack = 0x20080000;

SECTIONS
{
  .isr_vector :
  {
    . = ALIGN(4);
    _isr_vector = .;
    KEEP(*(.isr_vector))
    . = ALIGN(4);
  } > FLASH

  .firmware_header_vector :
  {
    . = ALIGN(4);
    KEEP(*(.firmware_header_vector))
    . = ALIGN(4);
  } > FLASH

  .text :
  {
    . = ALIGN(4);
    _stext = .;
    *(.Reset_Handler)
    *(.text)
    *(.text*)
    *(.rodata)
    *(.rodata*)
    *(.glue_7)
    *(.glue_7t)
    KEEP(*(.init))
    KEEP(*(.fini))
    . = ALIGN(4);
    _etext = .;

  } > FLASH

  .ARM.extab :
  {
    . = ALIGN(4);
    *(.ARM.extab)
    *(.gnu.linkonce.armextab.*)
    . = ALIGN(4);
  } > FLASH

  .exidx :
  {
    . = ALIGN(4);
    PROVIDE(__exidx_start = .);
    *(.ARM.exidx*)
    . = ALIGN(4);
    PROVIDE(__exidx_end = .);
  } > FLASH

  .preinit_array :
  {
    PROVIDE(__preinit_array_start = .);
    KEEP(*(.preinit_array*))
    PROVIDE(__preinit_array_end = .);
  } > FLASH

  .init_array :
  {
    PROVIDE(__init_array_start = .);
    KEEP(*(SORT(.init_array.*)))
    KEEP(*(.init_array*))
    PROVIDE(__init_array_end = .);
  } > FLASH

  .fini_array :
  {
    PROVIDE(__fini_array_start = .);
    KEEP(*(.fini_array*))
    KEEP(*(SORT(.fini_array.*)))
    PROVIDE(__fini_array_end = .);
  } > FLASH

  _sidata = .;
  .data_x : AT(_sidata) /* LMA address is _sidata (in FLASH) */
  {
    . = ALIGN(4);
    _sdata = .; /* data section VMA address */
    *(.data*)
    . = ALIGN(4);
    _edata = .;
  } > SRAM

  .firmware_header (_sidata + SIZEOF(.data_x)):
  {
    . = ALIGN(4);
    KEEP(*(.firmware_header))
    . = ALIGN(4);
  } > FLASH

  .eth (NOLOAD) :
  {
    . = ALIGN(4);
    KEEP(*(.RxDecripSection))
    KEEP(*(.TxDescripSection))
    KEEP(*(.RxarraySection))
    KEEP(*(.TxarraySection))
    . = ALIGN(4);
  } > SRAM

  .bss :
  {
    . = ALIGN(4);
    _sbss = .;

    PROVIDE(__bss_start__ = _sbss);
    *(.bss)
    *(.bss*)
    *(COMMON)
    . = ALIGN(4);
    _ebss = .;

    PROVIDE(__bss_end__ = _ebss);
  } > SRAM

  PROVIDE(end = .);

  .heap (NOLOAD) :
  {
    . = ALIGN(4);
    PROVIDE(__heap_start__ = .);
    KEEP(*(.heap))
    . = ALIGN(4);
    PROVIDE(__heap_end__ = .);
  } > SRAM

  .reserved_for_stack (NOLOAD) :
  {
    . = ALIGN(4);
    PROVIDE(__reserved_for_stack_start__ = .);
    KEEP(*(.reserved_for_stack))
    . = ALIGN(4);
    PROVIDE(__reserved_for_stack_end__ = .);
  } > SRAM

  .battery_backed_sram (NOLOAD) :
  {
    . = ALIGN(4);
    KEEP(*(.battery_backed_sram))
    . = ALIGN(4);
  } > BKPSRAM

  /DISCARD/ :
  {
    *(.ARM.attributes)
  }
}

This results in a readelf output of:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .isr_vector       PROGBITS        08020000 010000 0001f8 00  WA  0   0  4
  [ 2] .firmware_header_ PROGBITS        080201f8 0101f8 000004 00  WA  0   0  4
  [ 3] .text             PROGBITS        08020200 010200 021b44 00  AX  0   0 64
  [ 4] .ARM.extab        PROGBITS        08041d44 042728 000000 00   W  0   0  1
  [ 5] .exidx            ARM_EXIDX       08041d44 031d44 000008 00  AL  3   0  4
  [ 6] .init_array       INIT_ARRAY      08041d4c 031d4c 000008 04  WA  0   0  4
  [ 7] .fini_array       FINI_ARRAY      08041d54 031d54 000004 04  WA  0   0  4
  [ 8] .data_x           PROGBITS        20000000 040000 0009c8 00  WA  0   0  8
  [ 9] .firmware_header  PROGBITS        08042720 042720 000008 00  WA  0   0  4
  [10] .eth              NOBITS          200009c8 0509c8 0030a0 00  WA  0   0  4
  [11] .bss              NOBITS          20003a68 0509c8 045da4 00  WA  0   0  4
  [12] .heap             PROGBITS        2004980c 042728 000000 00   W  0   0  1
  [13] .reserved_for_sta PROGBITS        2004980c 042728 000000 00   W  0   0  1
  [14] .battery_backed_s NOBITS          40024000 044000 00000c 00  WA  0   0  4
  [15] .comment          PROGBITS        00000000 042728 000075 01  MS  0   0  1
  [16] .debug_frame      PROGBITS        00000000 0427a0 00144c 00      0   0  4
  [17] .stab             PROGBITS        00000000 043bec 000084 0c     18   0  4
  [18] .stabstr          STRTAB          00000000 043c70 000117 00      0   0  1
  [19] .symtab           SYMTAB          00000000 043d88 009b00 10     20 1787  4
  [20] .strtab           STRTAB          00000000 04d888 0042bb 00      0   0  1
  [21] .shstrtab         STRTAB          00000000 051b43 0000e6 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  y (purecode), p (processor specific)

There are no section groups in this file.

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x010000 0x08020000 0x08020000 0x21d58 0x21d58 RWE 0x10000
  LOAD           0x040000 0x20000000 0x08041d58 0x009c8 0x009c8 RW  0x10000
  LOAD           0x042720 0x08042720 0x08042720 0x00008 0x00008 RW  0x10000
  LOAD           0x0509c8 0x200009c8 0x08042720 0x00000 0x48e44 RW  0x10000
  LOAD           0x044000 0x40024000 0x40024000 0x00000 0x0000c RW  0x10000

 Section to Segment mapping:
  Segment Sections...
   00     .isr_vector .firmware_header_vector .text .exidx .init_array .fini_array 
   01     .data_x 
   02     .firmware_header 
   03     .eth .bss 
   04     .battery_backed_sram 

How does objcopy know not to insert sections such as .bss in to the output image? I know that it computes this on the fly, and I'm assuming this mechanism is driven through the section to segment mapping, but I cannot find any explanation as to how it actually performs the segment to section mapping. The elf file stores no information about which segments are flash, and yet somehow objcopy knows that it should not copy .bss into the output file. How?

2
Since it is producing a binary image, it mostly goes from the program headers. Those sections that don't get loaded into any segment will end up being ignored. Sections that are fixed zeroes (NOBITS) will likely not end up in the binary either.Chris Dodd
So that makes sense, but does that mean it has to iterate through both sections and program headers and then "join" that information to say what which will get loaded? Because going by program headers, there is no linkage to specifically what sections are covered by a given program header. As I understand, the only way to get that information would be to use the (files offset + FileSiz) and then look for blocks that occur in that range to know what sections are in that program header. IS this correct?chris12892
Further more, I am confused because .heap is marked with PROGBITS but it isn't loaded. I guess what I'm really asking for really is a simple algorithm which could be used to decide if a section is loaded or not, preferably the same way objcopy does the same.chris12892
Oh actually I just noticed that heap has a size of 0. That could be itchris12892
More or less the same as stackoverflow.com/questions/56501470/…, although I don't think that got a satisfactory answer.Clifford

2 Answers

0
votes

I have no idea if this is the real way to do this, but this is how I ended up solving this:

  # For each program header, get the sections contained by it
  # For each section, calculate the LMA the section will reside at 
  # Do NOT load a section if...
  #   Section type is SHT_NULL or NOBITS
  #   Section size = 0
  #   The LMA is outside of the isr_vector -> header region

Note that in my case, I had a section capping off the end of the image. This made it very obvious as to exactly where the image ended.

0
votes

The 'A' flag in the Flg column of the readelf output, which ELF calls SHF_ALLOC, indicates a section that 'occupies memory during process execution'.

SHF_ALLOC : The section occupies memory during process execution. Some control sections do not reside in the memory image of an object file; this attribute is off for those sections.

http://refspecs.linuxbase.org/elf/elf.pdf

Typically this applies to both program and data memory, and in a 'normal' OS environment the OS loads the SHF_ALLOC sections to the indicated addresses (ignoring or using for other purposes the non-SHF_ALLOC sections as appropriate) and everything's ready to go.

In an embedded environment with ROM program memory, only SHF_ALLOC sections mapping to addresses in ROM need to be output.

(This question illustrates a case in which going by address alone without taking SHF_ALLOC into account is not enough.)

However:

Depending on how the linker produced the ELF file, initialised data sections may or may not require additional handling.

Ideally the linker produces two sections related to initialised data: one in RAM (the runtime data area), and one in ROM (the initialisation contents for the RAM data). Startup code references the addresses of these sections and copies the init data to the RAM area. In this case simply outputting SHF_ALLOC sections in the ROM address range will automatically produce correct results.

If the linker instead produces a single section as if for a normal OS, then when generating the ROM image the data section needs to be identified and emitted at an address in ROM (and the startup code needs to be able to find that address).

Most decent toolchains take the former approach if configured correctly, but I've certainly worked with the latter as well.