GNU链接器:ELF有一个具有RWX权限的LOAD段。嵌入式ARM项目

问题描述 投票:0回答:2

我已经更新了我的arm-none-eabi GCC和相关工具,并重建了我开发的嵌入式项目。

$ arm-none-eabi-ld --version
GNU ld (GNU Binutils) 2.39

突然,我收到警告

/usr/lib/gcc/arm-none-eabi/12.1.0/../../../../arm-none-eabi/bin/ld: warning: my_elf_file.elf has a LOAD segment with RWX permissions

这个警告似乎是新引入的。我最近没有更改源/链接描述文件。 (编辑:我检查了使用先前版本创建的旧 ELF 文件。它在链接过程中没有打印警告,但有相同的问题)。我为 STM32F407 微控制器进行开发。 我的链接器脚本中的内存配置如下:

MEMORY
{
    FLASH (xr)  : ORIGIN = 0x08000000, LENGTH = 512K
    RAM (xrw)   : ORIGIN = 0x20000000, LENGTH = 128K    
    CCM (rw)    : ORIGIN = 0x10000000, LENGTH = 64K
}

查看链接的 ELF 我发现:

$ readelf -l my_elf_file.elf

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

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x010000 0x08000000 0x08000000 0x2220c 0x2220c RWE 0x10000
  LOAD           0x040000 0x10000000 0x0802220c 0x003e8 0x00d30 RW  0x10000
  LOAD           0x050000 0x20000000 0x080225f4 0x00c1c 0x00c1c RW  0x10000
  LOAD           0x000c20 0x20000c20 0x08023210 0x00000 0x01e70 RW  0x10000
  LOAD           0x002a90 0x20002a90 0x08023210 0x00000 0x08580 RW  0x10000

 Section to Segment mapping:
  Segment Sections...
   00     .vectors .text .ARM .flashcrc 
   01     .ccmdata .ccmbss 
   02     .data 
   03     .bss 
   04     .heap_stack 

确实,第一段被标记为 RWE。它包含 .vectors、.text、.ARM 和 .flashcrc 部分。

.vectors 部分和.text 部分包含向量表和程序代码。我在链接描述文件中添加了另一个名为 .flashcrc

的部分
    .flashcrc : ALIGN(4)
    {
        KEEP(*(.flashcrc))
        KEEP(*(.flashcrc.*))
        . = ALIGN(4);
    } >FLASH =0xFF

我在源代码中使用此部分来放置一个 const 结构,其中包含 CRC 校验和。这些校验和稍后由一个单独的 python 脚本计算并修补到 ELF 中。如果该结构位于其自己的部分中,则更容易在 ELF 中找到该结构。

删除此部分或简单地将其重新定位到 RAM,如下所示:

    .flashcrc : ALIGN(4)
    {
        KEEP(*(.flashcrc))
        KEEP(*(.flashcrc.*))
        . = ALIGN(4);
    } >RAM AT >FLASH =0xFF

从段中删除了“W”标志,警告消失了。

我不明白为什么 ELF 文件包含位于 FLASH 中的部分的“可写”标志,该标志在链接描述文件中标记为不可写。我尝试在 MEMORY 定义中使用

(xr!w)
,但它没有改变任何东西。

如何说服链接器该段不可写以消除此警告? 为什么链接器脚本的 MEMORY 部分中给出的标志没有任何影响?


奇怪的是,包含函数指针常量数组的 .vectors 部分不会发生这种情况。所以这部分与 .flashcrc 部分基本相同。


编辑2: 今天我又找到了一些时间来玩。

.flashcrc
部分中的结构在代码中(全局)定义如下:

volatile const struct flash_crcs __attribute__((section(".flashcrc"))) crcs_in_flash = {
    .start_magic = 0xA8BE53F9UL,
    .crc_section_ccm_data = 0UL,
    .crc_section_text = 0UL,
    .crc_section_data = 0UL,
    .crc_section_vectors = 0UL,
    .end_magic = 0xFFA582FFUL,
};

链接后将 crc 值修补到 ELF 中。 我必须使结构变得不稳定。如果它不是易失性的,编译器会优化对结构的访问,因为从它的角度来看,元素都是 0,因为它不知道这些元素在链接后已被修补。

事实证明,如果删除

volatile
关键字,警告就会消失。由于某种原因,易失性关键字欺骗编译器/链接器认为这应该是可写的。

是否有另一种方法可以防止编译器优化对此结构的访问但不使用 volatile?

ld binutils arm-none-eabi-gcc
2个回答
1
投票

我想作者已经找到了解决方案,但我希望我的回答对其他人有帮助。

当我们将 ARM GCC 工具链从 10 更新到 12 时,我们的团队遇到了同样的问题。正如 本文 中的 可执行段警告 一节中所指出的,如果您有数据和代码驻留,新的链接器会向您发出警告在同一内存区域(又称段)。

存储数据的存储器必须是可写的(W),并且代码存储器必须是可执行的(X)。两者都必须可读 (R)。因此,存储数据和代码部分的段是 RWX,这使您的程序容易受到缓冲区溢出等攻击。

我注意到,如果您不在段上强制执行属性,它会使用您分配给它的部分设置的属性。

那篇文章中提出的解决方案对我来说不起作用。有效的方法是将内存分成链接描述文件(lscript.ld)中的代码和数据段。不要像这样将所有部分放在同一段(由 MEMORY 命令定义)中:

/* ps7_ddr_0 is RWX because we omit the attributes and we 
   put both .data and .text there */
MEMORY
{
   ps7_ddr_0 : ORIGIN = 0x100000, LENGTH = 0x3FF00000
}

SECTIONS
{
.text : {
   KEEP (*(.vectors))
   *(.boot)
   *(.text)
   *(.text.*)
} > ps7_ddr_0

.data : {
   __data_start = .;
   *(.data)
   *(.data.*)
   __data_end = .;
} > ps7_ddr_0

_end = .;
}

我们将内存分为两个不同的段,一个用于数据部分可写,另一个用于代码部分可执行:

MEMORY
{
   ps7_ddr_0_code (rx) : ORIGIN = 0x00100000, LENGTH = 0x1FF80000
   ps7_ddr_0_data (rw) : ORIGIN = 0x20080000, LENGTH = 0x1FF80000
}

然后,每个SECTION都要根据自己的需要进入段:

SECTIONS
{
.text : {
   KEEP (*(.vectors))
   *(.boot)
   *(.text)
   *(.text.*)
} > ps7_ddr_0_code

.data : {
   __data_start = .;
   *(.data)
   *(.data.*)
   __data_end = .;
} > ps7_ddr_0_data

_end = .;
}

好处是链接器会告诉您是否将一个节放入具有不同要求的段中。


0
投票

我刚刚遇到了同样的问题,只是代码不同。 ST 官方推荐了一种解决方案,您只需为仅进入闪存的每个部分添加“(READONLY)”,例如改变

 .preinit_array :
 {
   …
 } >FLASH

 .preinit_array (READONLY) :
 {
   …
 } >FLASH

这对我有用。

参考:https://wiki.st.com/stm32mcu/wiki/STM32CubeIDE:STM32CubeIDE_errata_1.15.0 第 3.1 节中的问题 #169316(一般问题)。

© www.soinside.com 2019 - 2024. All rights reserved.