Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCC for ARM -- ELF output file segment misplaced

Edited to add: I have now cross-posted this to the GNU ARM Embedded Toolchain site, as I am fairly certain that it's a linker bug.

Also, I have noticed that it seems to happen when the first program segment fits into the first page in the ELF file (i.e. its starting offset within its page is >= the number of bytes in the ELF header). In this case the segment erroneously gets extended downwards to the beginning of the file. This would explain why the problem disappears if the in-page offset of the start address is reduced from 0x80 to 0x40.


I am implementing a stand-alone OS for ARM Cortex M0, and I have a weird problem with the linker. Here is my source file OS.c, stripped down to illustrate the problem:

int EntryPoint (void) { return 99 ; }

And here is my linker script file OS.ld, simply assigning all code to the region starting at 0x10080:

MEMORY
  {
  NVM (rx) : ORIGIN = 0x10080, LENGTH = 0x1000
  }

SECTIONS
  {
  .text 0x10080 :
    {
    OS.o (.text)
    } > NVM
  }

I compile and link it:

arm-none-eabi-gcc.exe -march=armv6-m -mthumb -c OS.c
arm-none-eabi-gcc.exe -oOS.elf -Xlinker --script=OS.ld OS.o -nostartfiles -nodefaultlibs

And now when I list the program segments with readelf OS.elf -l, I get:

Elf file type is EXEC (Executable file)
Entry point 0x10080
There are 1 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x00010000 0x00010000 0x0008c 0x0008c R E 0x10000

According to this, the one and only program segment starts at offset 0x000000 in the ELF output file, which is crazy: that region contains ELF header info irrelevant to the OS. And the physical start address is 0x00010000, which doesn't exist in my hardware.

But the weird thing is that if I change both instances of 0x10080 to 0x10040 in the linker script file, it works! I get:

Elf file type is EXEC (Executable file)
Entry point 0x10040
There are 1 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x010040 0x00010040 0x00010040 0x0000c 0x0000c R E 0x10000

Now the program segment is in the right place in the file, and has length 0x0000c instead of 0x0008c. Unfortunately address 0x00010040 doesn't exist in my hardware either, so this is not a solution.

Is this a bug in the GCC ARM compiler? Running it with --version gives:

arm-none-eabi-gcc.exe (GNU Tools for Arm Embedded Processors 7-2018-q2-update) 7.3.1 20180622 (release) [ARM/embedded-7-branch revision 261907]
like image 274
TonyK Avatar asked Oct 17 '22 08:10

TonyK


1 Answers

what you see might not be what you expect, but is nevertheless correct, IMHO.

ELF was created for System V. An OS that supports virtual memory and mmap() (a system call to map the contents of a file into memory).

You are looking at the ELF program header (not the section headers, see below). The program header is information to a (virtual memory capable) operation system's ELF loader about where it is supposed to mmap() the (complete) ELF file into virtual memory it prepared as process image. This OS would then just allocate one (or more) page(s) somewhere, call that (virtual) 0x10000 (for that process), map the file and jump to 0x10080 (the entry point).

For your second example, this would not work as you specified the (virtual) start address before the end of the ELF file's header (ELF header + program header + section headers), sot it cannot just map the file to a page boundary, making it more complicated (or even impossible) to the OS to do it's mmap() trick.

For your bare metal OS (that most likely doesn't support virtual memory, at least not on startup), the ELF program header's information is probably completely irrelevant.

You should probably rather look at the section headers, instead. They describe physical memory.

like image 62
mfro Avatar answered Oct 21 '22 09:10

mfro