I assume there's supposed to be padding after e_type and e_version? Otherwise all the 16 bit and 32 bit fields are unaligned.
Newer versions of the standard define e_type as uint16_t and e_version as uint32_t. Makes sense, I guess. This code is pretty old, I don't even remember where I've got this layout from.
This can be handy for elf-in-ROM on small machines, or memory-mapped on big machines.
Even stripped ELF files contain a lot more stuff, so if you are optimizing for size, it may not be the best idea to use ELF for anything.
Another issue is that for RAM code you don't really get a good info. ELF is not designed to be converted into binary, it was designed to be loaded as is. So even with this code you get output like this if you have RAM functions:
program 6884 bytes from file offset 65536 into memory address 4194304 (0x00400000)
program 4 bytes from file offset 72420 into memory address 4201188 (0x00401ae4)
program 4 bytes from file offset 72424 into memory address 4201192 (0x00401ae8)
program 112 bytes from file offset 131072 into memory address 541065216 (0x20400000)
0x00400000 in this case is the flash offset and 0x20400000 is SRAM. As you can see, the last allocated section is in the RAM, but the actual location of the data is in the flash.
The way objcopy handles this is it takes all the parts within the section (as defined by SECTIONS part of the linker script) and concatenates them together without filling the gaps. So in this case to simulate objcopy you would ignore the address and copy the data sequentially.
The only time objcopy fills the gaps is when there are multiple sections that contain loadable data. Those are the sections defined by the MEMORY part of the linker script. To extract that info add this:
typedef struct
{
uint32_t p_type;
uint32_t p_offset;
uint32_t p_vaddr;
uint32_t p_paddr;
uint32_t p_filesz;
uint32_t p_memsz;
uint32_t p_flags;
uint32_t p_align;
} elf_phdr_t;
..........
elf_phdr_t *phdr = (elf_phdr_t *)&data[hdr->e_phoff];
for (int i = 0; i < hdr->e_phnum; i++)
{
printf("--- %d\n", i);
printf("p_type : 0x%08x\n", phdr[i].p_type);
printf("p_offset : 0x%08x\n", phdr[i].p_offset);
printf("p_vaddr : 0x%08x\n", phdr[i].p_vaddr);
printf("p_paddr : 0x%08x\n", phdr[i].p_paddr);
printf("p_filesz : 0x%08x\n", phdr[i].p_filesz);
printf("p_memsz : 0x%08x\n", phdr[i].p_memsz);
printf("p_flags : 0x%08x\n", phdr[i].p_flags);
printf("p_align : 0x%08x\n", phdr[i].p_align);
}
Although it looks like it may be relying on the physical address from the program headers as well to convert things:
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x010000 0x00400000 0x00400000 0x01aec 0x01aec R E 0x10000
LOAD 0x020000 0x20400000 0x00401aec 0x00070 0x081ac RWE 0x10000
Section to Segment mapping:
Segment Sections...
00 .text .init .fini
01 .data .bss
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 1] .text PROGBITS 00400000 010000 001ae4 00 AX 0 0 64
[ 2] .init PROGBITS 00401ae4 011ae4 000004 00 AX 0 0 4
[ 3] .fini PROGBITS 00401ae8 011ae8 000004 00 AX 0 0 4
[ 4] .data PROGBITS 20400000 020000 000070 00 WAX 0 0 4
Here 0x00401aec is the address of the RAM functions (part of the .data segment) in the flash. If you just go by "Section Headers", then you will have a huge gap. But "Program Headers" alone do not have enough information.