我们在 Cortex M4 MCU 上遇到了一个奇怪的问题。如果在正确的部分使用 ST-LINK 加载二进制文件,则编译后的固件可以工作,但使用 C-Lion 和 OpenOcd,FW 加载操作还会加载 FLASH 中的 ELF 标头并损坏引导加载程序使用的标志区域。
MCU Flash 大小为 512 kB - 总共 256 页(每页 2kB),布局如下:
+-------- 0x0800 0000 - FLASH_BASE -------------------------+
| Bootloader partition |
| |
+-------- FLAGS_ADDR 0x0800 5000 --------------------------+
| 1 page Flags page |
+-------- APP_ADDR 0x0800 5800 -----------------------------+
| |
| (APP_NUMPAGES) Application partition |
| |
+-------- BAK_ADDR -----------------------------------------+
| |
| (APP_NUMPAGES) Application backup |
| |
+-------- NEW_ADDR -----------------------------------------+
| |
| (APP_NUMPAGES) Update partition |
| |
+-----------------------------------------------------------+
App FW必须从
0x0800 8500
开始加载,用ST-LINK加载.bin是正确的,但是在C-Lion中加载FW,代码是从0x0800 8500
开始正确加载的,但也错误地加载了ELF 文件加载于 0x0800 5000
。
使用readelf分析ELF文件,显示以下信息:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .isr_vector PROGBITS 08005800 000800 000194 00 A 0 0 1
[ 2] .build_info PROGBITS 08005994 000994 0000a4 00 A 0 0 4
[ 3] .text PROGBITS 08005a40 000a40 01f698 00 AX 0 0 16
[ 4] .rodata PROGBITS 080250d8 0200d8 002540 00 A 0 0 4
[ 5] .ARM.extab PROGBITS 08027618 024398 000000 00 W 0 0 1
[ 6] .ARM ARM_EXIDX 08027618 022618 000008 00 AL 3 0 4
[ 7] .preinit_array PREINIT_ARRAY 08027620 024398 000000 04 WA 0 0 1
[ 8] .init_array INIT_ARRAY 08027620 022620 000038 04 WA 0 0 4
[ 9] .fini_array FINI_ARRAY 08027658 022658 000008 04 WA 0 0 4
[10] .data PROGBITS 20000000 023000 001398 00 WA 0 0 8
[11] .bss NOBITS 20001398 024398 00f740 00 WA 0 0 8
[12] ._user_heap_stack NOBITS 20010ad8 024398 002000 00 WA 0 0 1
[13] .ARM.attributes ARM_ATTRIBUTES 00000000 024398 000030 00 0 0 1
[14] .comment PROGBITS 00000000 0243c8 000039 01 MS 0 0 1
[15] .debug_info PROGBITS 00000000 024401 1f27e9 00 0 0 1
[16] .debug_abbrev PROGBITS 00000000 216bea 020d3e 00 0 0 1
[17] .debug_aranges PROGBITS 00000000 237928 00cc90 00 0 0 8
[18] .debug_rnglists PROGBITS 00000000 2445b8 009c3c 00 0 0 1
[19] .debug_line PROGBITS 00000000 24e1f4 06ef95 00 0 0 1
[20] .debug_str PROGBITS 00000000 2bd189 1d4157 01 MS 0 0 1
[21] .debug_frame PROGBITS 00000000 4912e0 03a014 00 0 0 4
[22] .debug_line_str PROGBITS 00000000 4cb2f4 0001bc 01 MS 0 0 1
[23] .debug_loclists PROGBITS 00000000 4cb4b0 001e9b 00 0 0 1
[24] .symtab SYMTAB 00000000 4cd34c 00f260 10 25 2637 4
[25] .strtab STRTAB 00000000 4dc5ac 01f90f 00 0 0 1
[26] .shstrtab STRTAB 00000000 4fbebb 00012b 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
D (mbind), y (purecode), p (processor specific)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x08005000 0x08005000 0x22660 0x22660 RWE 0x1000
LOAD 0x023000 0x20000000 0x08027660 0x01398 0x01398 RW 0x1000
LOAD 0x000398 0x20001398 0x20001398 0x00000 0x11740 RW 0x1000
Section to Segment mapping:
Segment Sections...
00 .isr_vector .build_info .text .rodata .ARM .init_array .fini_array
01 .data
02 .bss ._user_heap_stack
There is no dynamic section in this file.
看起来链接器尝试将代码对齐到 4kB 而不是 2kB。 有办法强制链接器将代码对齐到2kB吗?
使用相同的链接描述文件为 CortexM7 处理器编译固件,具有 2MB 闪存和不同的闪存分区,在这种情况下,闪存分区与 4kB 对齐兼容。
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .isr_vector PROGBITS 08040000 001000 0001f8 00 A 0 0 1
[ 2] .build_info PROGBITS 080401f8 0011f8 000098 00 A 0 0 4
[ 3] .text PROGBITS 08040290 001290 0939d8 00 AX 0 0 16
[ 4] .rodata PROGBITS 080d3c68 094c68 025f18 00 A 0 0 8
[ 5] .ARM.extab PROGBITS 080f9b80 0beea0 000000 00 W 0 0 1
[ 6] .ARM ARM_EXIDX 080f9b80 0bab80 000008 00 AL 3 0 4
[ 7] .preinit_array PREINIT_ARRAY 080f9b88 0beea0 000000 04 WA 0 0 1
[ 8] .init_array INIT_ARRAY 080f9b88 0bab88 000070 04 WA 0 0 4
[ 9] .fini_array FINI_ARRAY 080f9bf8 0babf8 000008 04 WA 0 0 4
[10] .data PROGBITS 20000000 0bb000 003ea0 00 WA 0 0 8
[11] .dma NOBITS 20078000 0bf000 0030a0 00 WA 0 0 4
[12] .bss NOBITS 20003ea0 0beea0 03fca0 00 WA 0 0 8
[13] ._user_heap_stack NOBITS 20043b40 0beea0 004800 00 WA 0 0 1
[14] .ARM.attributes ARM_ATTRIBUTES 00000000 0beea0 00002e 00 0 0 1
[15] .comment PROGBITS 00000000 0beece 0000a9 01 MS 0 0 1
[16] .debug_info PROGBITS 00000000 0bef77 b262bd 00 0 0 1
[17] .debug_abbrev PROGBITS 00000000 be5234 063c2c 00 0 0 1
[18] .debug_aranges PROGBITS 00000000 c48e60 02be48 00 0 0 8
[19] .debug_rnglists PROGBITS 00000000 c74ca8 025d1f 00 0 0 1
[20] .debug_line PROGBITS 00000000 c9a9c7 168d5a 00 0 0 1
[21] .debug_str PROGBITS 00000000 e03721 1d9a755 01 MS 0 0 1
[22] .debug_frame PROGBITS 00000000 2b9de78 0ca8ac 00 0 0 4
[23] .debug_line_str PROGBITS 00000000 2c68724 0001c9 01 MS 0 0 1
[24] .debug_loclists PROGBITS 00000000 2c688ed 05e172 00 0 0 1
[25] .debug_loc PROGBITS 00000000 2cc6a5f 0002dd 00 0 0 1
[26] .debug_ranges PROGBITS 00000000 2cc6d3c 000030 00 0 0 1
[27] .symtab SYMTAB 00000000 2cc6d6c 05bbf0 10 28 15989 4
[28] .strtab STRTAB 00000000 2d2295c 240e77 00 0 0 1
[29] .shstrtab STRTAB 00000000 2f637d3 000149 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
D (mbind), y (purecode), p (processor specific)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x001000 0x08040000 0x08040000 0xb9c00 0xb9c00 RWE 0x1000
LOAD 0x0bb000 0x20000000 0x080f9c00 0x03ea0 0x03ea0 RW 0x1000
LOAD 0x000ea0 0x20003ea0 0x20003ea0 0x00000 0x444a0 RW 0x1000
LOAD 0x000000 0x20078000 0x20078000 0x00000 0x030a0 RW 0x1000
Section to Segment mapping:
Segment Sections...
00 .isr_vector .build_info .text .rodata .ARM .init_array .fini_array
01 .data
02 .bss ._user_heap_stack
03 .dma
There is no dynamic section in this file.
The linker script is the following, with a different definition for Cortex M4 or Cortex M7:
INCLUDE "hw_linkerdefs.ld"
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = _LINKER_StartStack; /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = _LINKER_Min_Heap_Size; /* required amount of heap */
_Min_Stack_Size = _LINKER_Min_Stack_Size; /* required amount of stack */
_heap_upper_limit = _estack - _LINKER_Min_Stack_Size;
_dmaAreaStart = _LINKER_DMA_AREASTART;
/* Specify the memory areas */
/* ensure consistency to SCB->VTOR defined in system_stm32l4xx.c */
/* IMAGETYPE is replaced by cmake into either APP or BOOT*/
MEMORY
{
RAM (xrw) : ORIGIN = _LINKER_RAM_BASEADDR, LENGTH = _LINKER_RAM_SIZE
FLASH (rx) : ORIGIN = _LINKER_APP_FLASHSTART, LENGTH = _LINKER_APP_FLASHSIZE
FLAGSAREA (r) : ORIGIN = FLAGS_ADDR, LENGTH = 1024
DMARAM (rw) : ORIGIN = _LINKER_DMA_AREASTART, LENGTH = _LINKER_DMA_AREASIZE
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(8);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(8);
} >FLASH
/* This is the area reserved for build info */
.build_info :
{
. = ALIGN(8);
KEEP(*(.build_info_index))
*(.build_info_area)
. = ALIGN(8);
} > FLASH
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(8);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(8);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data goes into FLASH */
.rodata :
{
. = ALIGN(8);
KEEP(*(*.uxTopUsedPriority))
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(8);
} >FLASH
.ARM.extab :
{
. = ALIGN(8);
*(.ARM.extab* .gnu.linkonce.armextab.*)
. = ALIGN(8);
} >FLASH
.ARM : {
. = ALIGN(8);
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
. = ALIGN(8);
} >FLASH
.preinit_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(8);
} >FLASH
.init_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(8);
} >FLASH
.fini_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
. = ALIGN(8);
} >FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(8);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(8);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
/* This is only useful for the bootloader build in case it includes the default flags area */
.bootloader_default_flags :
{
KEEP(*(.bootloader_default_flags))
} > FLAGSAREA
/* this rule places the .dma buffers into the DMA area of RAM */
.dma (NOLOAD):
{
*(.dma)
} > DMARAM
/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >RAM
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
. = ALIGN(8);
PROVIDE ( _end_static_memory = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >RAM
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
}
代码是用
xpack-arm-none-eabi-gcc-13.3.1-1.1
工具链编译的。
我找到了解决方案! 我的问题是重复的:使用 OpenOcd 刷新 ELF 文件会导致 ELF 标头写入 Flash 搜索解决方案我发现了这个:如何更改 ELF 中代码段的对齐方式。
解决方案是将最小页面大小减小到 2kB,添加链接器选项:
-z max-page-size=2048
。
之后 ELF 格式正确,C-Lion/OpenOCD 可以正确加载代码。 改变后的ELF为:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .isr_vector PROGBITS 08005800 000800 000198 00 A 0 0 1
[ 2] .build_info PROGBITS 08005998 000998 0000a8 00 A 0 0 4
[ 3] .text PROGBITS 08005a40 000a40 01f258 00 AX 0 0 16
[ 4] .rodata PROGBITS 08024c98 01fc98 002420 00 A 0 0 4
[ 5] .ARM.extab PROGBITS 080270b8 023b28 000000 00 W 0 0 1
[ 6] .ARM ARM_EXIDX 080270b8 0220b8 000008 00 AL 3 0 4
[ 7] .preinit_array PREINIT_ARRAY 080270c0 023b28 000000 04 WA 0 0 1
[ 8] .init_array INIT_ARRAY 080270c0 0220c0 000038 04 WA 0 0 4
[ 9] .fini_array FINI_ARRAY 080270f8 0220f8 000008 04 WA 0 0 4
[10] .data PROGBITS 20000000 022800 001328 00 WA 0 0 8
[11] .bss NOBITS 20001328 023b28 00f63c 00 WA 0 0 8
[12] ._user_heap_stack NOBITS 20010964 023b28 002004 00 WA 0 0 1
[13] .ARM.attributes ARM_ATTRIBUTES 00000000 023b28 000030 00 0 0 1
[14] .comment PROGBITS 00000000 023b58 000039 01 MS 0 0 1
[15] .debug_info PROGBITS 00000000 023b91 1f1974 00 0 0 1
[16] .debug_abbrev PROGBITS 00000000 215505 020d17 00 0 0 1
[17] .debug_aranges PROGBITS 00000000 236220 00cc58 00 0 0 8
[18] .debug_rnglists PROGBITS 00000000 242e78 009c0f 00 0 0 1
[19] .debug_line PROGBITS 00000000 24ca87 06ece9 00 0 0 1
[20] .debug_str PROGBITS 00000000 2bb770 1cc606 01 MS 0 0 1
[21] .debug_frame PROGBITS 00000000 487d78 039f0c 00 0 0 4
[22] .debug_line_str PROGBITS 00000000 4c1c84 0001bc 01 MS 0 0 1
[23] .debug_loclists PROGBITS 00000000 4c1e40 001e9b 00 0 0 1
[24] .symtab SYMTAB 00000000 4c3cdc 00ef20 10 25 2604 4
[25] .strtab STRTAB 00000000 4d2bfc 01d74e 00 0 0 1
[26] .shstrtab STRTAB 00000000 4f034a 00012b 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
D (mbind), y (purecode), p (processor specific)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000800 0x08005800 0x08005800 0x21900 0x21900 RWE 0x800
LOAD 0x022800 0x20000000 0x08027100 0x01328 0x01328 RW 0x800
LOAD 0x000328 0x20001328 0x20001328 0x00000 0x11640 RW 0x800
Section to Segment mapping:
Segment Sections...
00 .isr_vector .build_info .text .rodata .ARM .init_array .fini_array
01 .data
02 .bss ._user_heap_stack
There is no dynamic section in this file.
There are no relocations in this file.