GCC Arm 12.2编译提示 LOAD segment with RWX permissions 警告

使用GCC Arm工具链开发的项目, 在升级到 arm-gnu-toolchain-12.2 之后, 编译出现警告
arm-gnu-toolchain-12.2.mpacbti-bet1-x86_64-arm-none-eabi/bin/../lib/gcc/arm-none-eabi/12.2.0/../../../../arm-none-eabi/bin/ld: warning: Build/app.elf has a LOAD segment with RWX permissions关于 LOAD segment with RWX permissions 警告这是 Binutils 2.39 引入的一个新的安全类型的警告, GCC在升级版本时会带着新版本的 Binutils 一起发布. 如果要消除这个警告, 要么修改ld文件, 要么屏蔽掉它.
说明这篇文章里有比较详细的说明https://www.redhat.com/en/blog/linkers-warnings-about-executable-stacks-and-segments
The executable segment warnings
当程序载入内存时会分段载入, 一些属于可执行的代码,一些属于数据, 可读或者可读可写, 可能还有一些用于其它特殊用途. 每一段内存都会区分可读、可写和可执行这三个属性, 如果一个内存段同时具有这三种属性, 则存在受到攻击的可能性, 因此在这种情况下链接器将产生以下警告
warning: <file> has a LOAD segment with RWX permissions这个警告表示elf文件中存在一个或多个存在安全问题的段, 可以通过运行readelf程序进行查看
readelf -lW <file>注意: 在readelf的输出中, 段的可执行标志被标记为E而不是X, 三个属性的标识为RWE而不是RWX. 警告出现的常见原因是使用自定义连接脚本进行链接, 该脚本未将代码和数据分成不同的段, 所以最好的解决办法是更新连接脚本. readelf命令将显示每个段包含哪些部分, 可以通过这些信息计算出连接器映射需要如何更新, 才能将代码部分和可写的数据部分分开.
消除 LOAD segment with RWX permissions 警告选项一: 使用 --no-warn-rwx-segments 屏蔽

  • 如果连接使用的是ld, 可以用--no-warn-rwx-segments选项
  • 如果连接使用的是gcc, 直接用会提示无法识别的选项, 需要用-Wl,--no-warn-rwx-segments这样的方式
选项二: 修改连接描述对于存在问题的elf, 可以通过这个命令查看文件结构, 注意后面的Flg部分, RWE分别表示Read,Write,Execute.
$ readelf -lW app.elf Elf file type is EXEC (Executable file)Entry point 0x15e1There are 3 program headers, starting at offset 52Program Headers:TypeOffsetVirtAddrPhysAddrFileSiz MemSizFlg AlignLOAD0x010000 0x00000000 0x00000000 0x026f4 0x026f4 RWE 0x10000LOAD0x020000 0x20000000 0x000026f4 0x00088 0x00334 RW0x10000LOAD0x000334 0x20000334 0x0000277c 0x00000 0x00004 RW0x10000 Section to Segment mapping:Segment Sections...00.isr_vector .text .rodata .init_array .fini_array01.data .bss02._user_heap_stack其中LOAD0x010000 0x08000000 0x08000000 0x03ffc 0x03ffc RWE 0x10000就是存在问题的segment, 如果要消除这个警告, 可以将ld文件中的 .init_array 和 .fini_array 这部分注释掉, 代码如下. 这部分是 startup 文件中 __libc_init_array使用的, 如果不需要可以直接删除, 对应的编译参数也可以加上-nostartfiles.
.preinit_array:{PROVIDE_HIDDEN (__preinit_array_start = .);KEEP (*(.preinit_array*))PROVIDE_HIDDEN (__preinit_array_end = .);} >FLASH.init_array :{PROVIDE_HIDDEN (__init_array_start = .);KEEP (*(SORT(.init_array.*)))KEEP (*(.init_array*))PROVIDE_HIDDEN (__init_array_end = .);} >FLASH.fini_array :{PROVIDE_HIDDEN (__fini_array_start = .);KEEP (*(SORT(.fini_array.*)))KEEP (*(.fini_array*))PROVIDE_HIDDEN (__fini_array_end = .);} >FLASH这样编译完之后的结果如下, 第一个segment中, Flg变成了R E就没问题了.
$ readelf -lW app.elf Elf file type is EXEC (Executable file)Entry point 0x1549There are 3 program headers, starting at offset 52Program Headers:TypeOffsetVirtAddrPhysAddrFileSiz MemSizFlg AlignLOAD0x010000 0x00000000 0x00000000 0x02654 0x02654 R E 0x10000LOAD0x020000 0x20000000 0x00002654 0x00088 0x00318 RW0x10000LOAD0x000318 0x20000318 0x000026dc 0x00000 0x00300 RW0x10000 Section to Segment mapping:Segment Sections...00.isr_vector .text .rodata01.data .bss02._user_heap_stack上面这种修改并不是通用的, 对于需要使用libc的应用而言并不可行.
实际上, 对于Cortex M系列的MCU而言, elf中第一个segment对应的实际上是烧录到flash中的部分(可执行), 第二个segment对应的才是运行时可读写的内存部分(数据), 第一个segment在通过flash启动正常运行时并不存在修改的可能性.
因此结论是可以通过选项一, 简单地将警告屏蔽掉
参考
  • https://github.com/raspberrypi/pico-sdk/issues/1029
  • https://stackoverflow.com/questions/73429929/gnu-linker-elf-has-a-load-segment-with-rwx-permissions-embedded-arm-project
  • https://github.com/OP-TEE/optee_os/issues/5471
【GCC Arm 12.2编译提示 LOAD segment with RWX permissions 警告】

推荐阅读