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這樣的方式
選項二: 修改連接描述【GCC Arm 12.2編譯提示 LOAD segment with RWX permissions 警告】對于存在問題的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

經驗總結擴展閱讀