GCC for ARM -- ELF 输出文件段错位

标签 gcc arm embedded linker-scripts

编辑添加:我现在已将此交叉发布到 GNU ARM Embedded Toolchain网站,因为我相当确定这是一个链接器错误。

另外,我注意到当第一个程序段适合 ELF 文件的第一页时(即它在其页内的起始偏移量 >= ELF header 中的字节数)似乎会发生这种情况。在这种情况下,段错误地向下扩展到文件的开头。这可以解释为什么如果起始地址的页内偏移量从 0x80 减少到 0x40,问题就会消失。

我正在为 ARM Cortex M0 实现一个独立的操作系统,但我的链接器有一个奇怪的问题。这是我的源文件 OS.c ,精简下来说明问题:

int EntryPoint (void) { return 99 ; }

这是我的链接器脚本文件 OS.ld ,只需将所有代码分配给从 0x10080 开始的区域:
MEMORY
  {
  NVM (rx) : ORIGIN = 0x10080, LENGTH = 0x1000
  }

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

我编译并链接它:
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

现在当我用 readelf OS.elf -l 列出程序段时,我得到:
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

据此,唯一的程序段开始于偏移量0x000000在 ELF 输出文件中,这很疯狂:该区域包含与操作系统无关的 ELF header 信息。而物理起始地址是0x00010000 ,这在我的硬件中不存在。

但奇怪的是,如果我更改 0x10080 的两个实例至 0x10040在链接器脚本文件中,它有效!我得到:
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

现在程序段位于文件中的正确位置,长度为 0x0000c而不是 0x0008c .不幸地址0x00010040我的硬件中也不存在,所以这不是解决方案。

这是 GCC ARM 编译器中的错误吗?使用 --version 运行它给出:
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]

最佳答案

你所看到的可能不是你所期望的,但仍然是正确的,恕我直言。

ELF 是为 System V 创建的。一个支持虚拟内存和 mmap() 的操作系统。 (将文件内容映射到内存的系统调用)。

您正在查看 ELF 程序头( 而不是 节头,见下文)。程序头是一个(具有虚拟内存能力)操作系统的 ELF 加载器的信息,关于它应该在哪里mmap()将(完整的)ELF 文件放入它作为进程镜像准备的虚拟内存中。然后,该操作系统将在某处分配一个(或多个)页面,调用该(虚拟)0x10000(对于该进程),映射文件并跳转到 0x10080(入口点)。

对于您的第二个示例,当您指定(虚拟)起始地址 时,这将不起作用。之前 ELF 文件头的结尾(ELF 头 + 程序头 + 节头),所以它不能只是将文件映射到页面边界,这使得操作系统更复杂(甚至不可能)做到这一点 mmap()诡计。

对于您的裸机操作系统(很可能不支持虚拟内存,至少在启动时不支持),ELF 程序头信息可能完全无关。

您可能更应该查看部分标题。它们描述物理内存。

关于GCC for ARM -- ELF 输出文件段错位,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54151670/

相关文章:

gcc - 如何删除特定的 ELF 部分,而不删除其他符号?

c - 为什么 GCC 在实现整数除法时使用乘以一个奇怪的数字?

assembly - 使用 gdb 比较汇编跟踪

c++ - 引用静态对象 - 在 ARM proc 上触发对齐陷阱

c - 就 C 中的速度和空间消耗而言,静态与全局

c# - 在 Linux 嵌入式单声道程序中,数组数据未正确编码为从 c# 到 c 的结构

c - 无法编译C程序

gcc - 关于 ARM 内联装配的不同 clobber 描述的混淆

linux - 嵌入式设备上带有 ramfs 的内核 panic : No filesystem could mount root

c++ - 有人有用 C++ 包装函数的例子吗?