Search code examples
linkerarmgnuobjcopysrecord

Where do addresses in S-Record files come from?


I am developing a freestanding application for an ARM Cortex-M microcontroller and while researching the structure of an S-Record file I found that I have some kind of misunderstanding in how the addresses are represented in the S-Record format.

I have a variable defined in my source code like so:

uint32_t g_ip_address = IP_ADDRESS(10, 1, 0, 56); // in LE: 0x3800010A

When I run objdump I see that the variable ends up in the .data section at address 0x1ffe01c4:

$ arm-none-eabi-objdump -t application.elf | grep g_ip_address
1ffe01c4 g     O .data  00000004 g_ip_address

This makes sense, given that the memory section of my linker script looks like this and .data is going to RAM:

MEMORY
{
  FLASH (rx)         : ORIGIN = 0x00000000, LENGTH = 0x0200000  /*   2M */
  RAM (rwx)          : ORIGIN = 0x1FFE0000, LENGTH = 0x00A0000  /* 640K */   
}

However, when I check through the srec file, I'm finding that the address for the record is not 0x1FFE0000. It's 0x0005F570, which seems to put it in the FLASH section (spaces added for clarity).

S315 0005F570 00000000 3800010A 000010180000000014

Is there an implicit offset encoded in a different record entry? How does objcopy get this new address? If this value is being encoded into a function in some way (some pre-main initialization of variables perhaps)?

Ultimately, my goal is to be able to parse the srec file and patch the IP address value to create a new srec file. Is the idiomatic way of doing something like this simply to create a struct that hardcodes some leading magic number sequence that can be detected in the file?


Solution

  • flash.s

    .cpu cortex-m0
    .thumb
    
    .word 0x00002000
    .word reset
    
    .thumb_func
    reset:
        b reset
        
    .data
    .word 0x11223344
    
    .bss
    .word 0x00000000
    .word 0x00000000
    

    flash.ld

    MEMORY
    {
        rom : ORIGIN = 0x08000000, LENGTH = 0x1000
        ram : ORIGIN = 0x20000000, LENGTH = 0x1000
    }
    SECTIONS
    {
        .text   : { *(.text*)   } > rom
        .bss    : { *(.bss*)    } > ram AT > rom
        .data   : { *(.data*)   } > ram AT > rom
    }
    

    build it

    arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m0 flash.s -o flash.o
    arm-none-eabi-ld -nostdlib -nostartfiles -T flash.ld flash.o -o so.elf
    arm-none-eabi-objdump -D so.elf > so.list
    arm-none-eabi-objcopy --srec-forceS3 so.elf -O srec so.srec
    arm-none-eabi-objcopy -O binary so.elf so.bin
    

    cat so.list

    08000000 <reset-0x8>:
     8000000:   00002000    andeq   r2, r0, r0
     8000004:   08000009    stmdaeq r0, {r0, r3}
    
    08000008 <reset>:
     8000008:   e7fe        b.n 8000008 <reset>
    
    Disassembly of section .bss:
    
    20000000 <.bss>:
        ...
    
    Disassembly of section .data:
    
    20000008 <.data>:
    20000008:   11223344            ; <UNDEFINED> instruction: 0x11223344
    

    cat so.srec

    S00A0000736F2E7372656338
    S30F080000000020000009000008FEE7D2
    S3090800000A443322113A
    S70508000000F2
    

    arm-none-eabi-readelf -l so.elf

    Elf file type is EXEC (Executable file)
    Entry point 0x8000000
    There are 3 program headers, starting at offset 52
    
    Program Headers:
      Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
      LOAD           0x000094 0x08000000 0x08000000 0x0000a 0x0000a R E 0x2
      LOAD           0x000000 0x20000000 0x0800000a 0x00000 0x00008 RW  0x1
      LOAD           0x00009e 0x20000008 0x0800000a 0x00004 0x00004 RW  0x1
    
     Section to Segment mapping:
      Segment Sections...
       00     .text 
       01     .bss 
       02     .data 
    

    hexdump -C so.bin

    00000000  00 20 00 00 09 00 00 08  fe e7 44 33 22 11        |. ........D3".|
    0000000e
    

    bss is not normally exposed as is, you complicate your linker script to add beginning and end points so you can then zero that range in your bootstrap. For .data you can clearly see what is going on with the standard binutils tools.

    You have not provided enough of your code (and linker script), nor a minimal example that demonstrates the problem, so this is about as far as this can go.