微控制器的首次 gcc 链接器脚本编译但不运行

First time gcc linker script for microcontroller compiles but doesn't run

本文关键字:编译 脚本 运行 链接 控制器 gcc      更新时间:2023-10-16

我通过 atmel studio 为SAMD51J19微控制器创建了我的第一个 gcc 链接器脚本,它的编译很棒,没有问题,但是当加载到设备中时会发生奇怪的古怪错误。

有时它是硬故障,有时它没有,有时中断没有发送到处理程序,有时是。有时函数在运行时没有任何事情发生,有时则没有。调试器到处都是,旧的股票链接器代码没有任何这些问题,我不知道为什么会发生这种情况。

大多数只是从原始文件中重复使用。我所做的主要是在区域之间添加一些间距或更好的对齐方式,并移动一些不是强制性放置在那里的部分。例如,只读数据位于 rom bank 0 的末尾而不是中间。所以我真的不明白为什么会发生这种情况

下面是我的链接器脚本

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
SEARCH_DIR(.)
/*
* NVM = 2 Banks (512KB) (352KB usable) (160KB reserved for SEEPROM)
* 1 Bank = 32 blocks (256K) and 16 Regions
*    Bank 1 (Main) = 32 blocks (256K) and 16 Regions
*    Bank 2 (Aux)  = 12 Blocks (96KB) and 6 Regions
* 1 Region = 2 Blocks (16KB) (Protection Alignment)
* 1 Block = 16 Pages (8KB) (Division Alignment)
* 1 Page = 32 QWords (512B) (Section Alignment)
* 1 QWord = 16 Bytes (Sub-Section Alignment)
* 1 DWord = 8 Bytes
* 1 Word  = 4 Bytes (Default Alignment)
*
* Alignments:
* 4      Bytes: New piece
* 16     Bytes: New Subsection
* 512    Bytes: New Section
* 8,192  Bytes: New division
* 16,384 Bytes: New protection region
*/
/* Memory Spaces Definitions */
MEMORY
{
rom0     (rx)  : ORIGIN = 0x00000000, LENGTH = 0x0003F800 /*Rom Bank 0: Main Bank (For Code)*/
lnl      (rx)  : ORIGIN = 0x0003F800, LENGTH = 0x00000800 /*Load-N-Lock 0-Wait State Speed: 2KB*/
rom1     (rx)  : ORIGIN = 0x00040000, LENGTH = 0x00018000 /*Rom Bank 1: Aux Bank (For Data)*/
qspi     (rx)  : ORIGIN = 0x04000000, LENGTH = 0x01000000 /*External NVM: (Additional Aux Data on seperate chip)*/
ram      (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00030000 /*Ram (Default RAM)*/
seeprom  (rx)  : ORIGIN = 0x44000000, LENGTH = 0x00010000 /*SEEPROM: Runtime Permanent Data*/
bkupram  (rwx) : ORIGIN = 0x47000000, LENGTH = 0x00002000 /*Backup Ram: Program Data*/
}
/* The stack size used by the application*/
STACK_SIZE = 0xC000;
/* Section Definitions */
SECTIONS
{
/*
Program Code
Gameplan for .text output section
No support for exceptions or unwinding tables
Vectors go at start
Program Code is placed on the next QWord
Thumb/Arm Glue code is placed on the next QWord
Read-Only Data is placed in it's own page
Relocate Data is placed in it's own page
*/
.text :
{
/*Start at top*/
. = 0x00000000;
_stext = .;
_sfixed = .;
/*Vector Table*/
/*Place Vectors at start of rom0 as required*/
KEEP(*(.vectors .vectors.*))
/*Program Code*/
. = ALIGN(16); /*New Subsection*/
*(.text .text.* .gnu.linkonce.t.*)
/*C & C++ Special Generated Code, part of main program code*/
/*C++ Setup Code*/
. = ALIGN(4); /*New piece*/
KEEP(*(.init))
. = ALIGN(4); /*New piece*/
__preinit_array_start = .;
KEEP (*(.preinit_array))
__preinit_array_end = .;
. = ALIGN(4); /*New piece*/
__init_array_start = .;
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
__init_array_end = .;
/*C++ Constructor Code*/
. = ALIGN(4); /*New piece*/
KEEP (*crtbegin.o(.ctors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*crtend.o(.ctors))
/*C++ Teardown Code*/
. = ALIGN(4); /*New piece*/
KEEP(*(.fini))
. = ALIGN(4); /*New piece*/
__fini_array_start = .;
KEEP (*(.fini_array))
KEEP (*(SORT(.fini_array.*)))
__fini_array_end = .;
/*C++ Deconstructor Code*/
. = ALIGN(4); /*New piece*/
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*crtend.o(.dtors))
/*Thumb <==> ARM Code Translation Glue*/
. = ALIGN(16); /*New Subsection*/
*(.glue_7t) *(.glue_7)
/*Read-Only/Constant Data, placing in a new page*/
. = ALIGN(512); /*New Page*/
*(.rodata .rodata* .gnu.linkonce.r.*)
/*This is for relocating data, place it in it's own page*/
. = ALIGN(512); /*New Page*/
_placeRelocate = .;
_efixed = .;
_etext = .;
} > rom0 /*These of course go in the main rom bank*/
/*
Rom 0 Custom Code
Gameplan for .rom0 output section
This is sort of a workarea for program-specific usage unrelated to generated
code. It needs to go into it's own block.
*/
.rom0 (NOLOAD):
{
. = ALIGN(8192); /*New block*/
_srom0 = .;
*(.rom0*)
_erom0 = .;
} > rom0
/*
Load-N-Lock 0-Wait State Speed
Gameplan for .lnl output section
LNL is placed in it's own reserved memory area of a specific size in rom0. Space is
immensely cramped and every byte counts. No alignment will be done.
*/
.lnl (NOLOAD):
{
_slnl = .;
*(.lnl*)
_elnl = .;
} > lnl /*Goes into it's own specific area*/
/*
Rom 1 Custom Code
Gameplan for .rom1 output section
Rom Bank 1 is more relaxed given the entire bank is for program usage.
This needs to be placed after the first page. The first page is reserved
for program-specific header data and work area.
*/
.rom1 (NOLOAD):
{
. = 512; /*2nd page from start*/
_srom1 = .;
*(.rom1*)
_erom1 = .;
} > rom1 /*Goes into rom bank 1*/
/*
QSPI Custom Code
Gameplan for .qspi output section
QSPI is similar to ROM1 in that it's just an area that can be used
for custom usage or work area.
*/
.qspi (NOLOAD):
{
. = ALIGN(4); /*New piece*/
_sqspi = .;
*(.qspi*)
_eqspi = .;
} > qspi /*Goes into QSPI*/
/*
Uninitialized Data
Gameplan for .bss output section
Uninitialized data goes into main RAM at the start
*/
.bss (NOLOAD) :
{
. = ALIGN(4); /*New piece*/
_sbss = . ;
_szero = .;
*(.bss .bss.*) /*Place BSS Data at start of RAM*/
. = ALIGN(4); /*New piece*/
*(COMMON) /*Place common data after*/
_ebss = . ;
_ezero = .;
/*Relocate data comes after bss*/
. = ALIGN(4); /*New piece*/
_relocateStart = .;
} > ram
/*
Relocate data
Gameplan for .relocate output section
This needs to be placed at rom0 in it's own defined section. It will
be copied to the end of bss.
*/
.relocate : AT (_placeRelocate)
{
. = ALIGN(4); /*New piece*/
_srelocate = .;
*(.data .data.*);
. = ALIGN(4); /*New Piece*/
*(.ramfunc .ramfunc.*);
_erelocate = .;
} > ram /*Place in RAM*/
/*
Stack data
Gameplan for .stack output section
Placed at end of RAM an empty area large enough for the stack
*/
.stack (NOLOAD):
{
. = ALIGN(8); /*New larger piece*/
_sstack = .; /*Begin Stack*/
. = . + STACK_SIZE; /*Insert large blank area for stack*/
. = ALIGN(8); /*New larger piece*/
_estack = .; /*End stack placement*/
} > ram
/*
SEEPROM Data
Gameplan for .seeprom output section
The SEEPROM section is like rom1 just byte-writable rather than
block/page eraseable/writable. Unlike ROM1 however SEEPROM can start
at the beginning.
*/
.seeprom (NOLOAD):
{
. = ALIGN(4); /*New piece*/
_seeprom = .;
*(.seeprom*)
_eseeprom = .;
} > seeprom /*Goes into seeprom area*/
/*
Custom Backup RAM
Gameplan for .bkupram output section
Like ROM1 and SEEPROM, this is another potential usage area.
*/
.bkupram (NOLOAD):
{
. = ALIGN(4); /*New piece*/
_sbkupram = .;
*(.bkupram*)
_ebkupram = .;
} > bkupram /*Goes into bkupram area*/
. = ALIGN(4); /*New piece*/
_end = . ; /*End of everything*/
}

以及 ide 附带的原始文件

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
SEARCH_DIR(.)
/* Memory Spaces Definitions */
MEMORY
{
rom0     (rx)  : ORIGIN = 0x00000000, LENGTH = 0x0003F800
lnl      (rx)  : ORIGIN = 0x0003F800, LENGTH = 0x00000800 /*Load-N-Lock 0-Wait State Caching: 2KB*/
rom1     (rx)  : ORIGIN = 0x00040000, LENGTH = 0x00018000
qspi     (rx)  : ORIGIN = 0x04000000, LENGTH = 0x01000000
ram      (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00030000
seeprom  (rx)  : ORIGIN = 0x44000000, LENGTH = 0x00010000
bkupram  (rwx) : ORIGIN = 0x47000000, LENGTH = 0x00002000
}
/* The stack size used by the application. NOTE: you need to adjust according to your application. */
STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0xC000;
/* Section Definitions */
SECTIONS
{
.text :
{
. = ALIGN(4);
_sfixed = .;
KEEP(*(.vectors .vectors.*))
*(.text .text.* .gnu.linkonce.t.*)
*(.glue_7t) *(.glue_7)
*(.rodata .rodata* .gnu.linkonce.r.*)
*(.ARM.extab* .gnu.linkonce.armextab.*)
/* Support C constructors, and C destructors in both user code
and the C library. This also provides support for C++ code. */
. = ALIGN(4);
KEEP(*(.init))
. = ALIGN(4);
__preinit_array_start = .;
KEEP (*(.preinit_array))
__preinit_array_end = .;
. = ALIGN(4);
__init_array_start = .;
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
__init_array_end = .;
. = ALIGN(4);
KEEP (*crtbegin.o(.ctors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*crtend.o(.ctors))
. = ALIGN(4);
KEEP(*(.fini))
. = ALIGN(4);
__fini_array_start = .;
KEEP (*(.fini_array))
KEEP (*(SORT(.fini_array.*)))
__fini_array_end = .;
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*crtend.o(.dtors))
. = ALIGN(4);
_efixed = .;            /* End of text section */
} > rom0
/* .ARM.exidx is sorted, so has to go in its own output section.  */
PROVIDE_HIDDEN (__exidx_start = .);
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > rom0
PROVIDE_HIDDEN (__exidx_end = .);
. = ALIGN(4);
_etext = .;
.lnl :
{
/*. = ALIGN(4);*/
__lnl_start__ = .;
*(.lnl*)
__lnl_end__ = .;
} > lnl
.rom1 (NOLOAD):
{
. = ALIGN(8);
_rom1 = .;
*(.rom1 .rom1.*);
. = ALIGN(8);
_rom1 = .;
} > rom1
.seeprom (NOLOAD):
{
. = ALIGN(8);
_seeprom = .;
*(.seeprom .seeprom.*);
. = ALIGN(8);
_eseeprom = .;
} > seeprom
.relocate : AT (_etext)
{
. = ALIGN(4);
_srelocate = .;
*(.ramfunc .ramfunc.*);
*(.data .data.*);
. = ALIGN(4);
_erelocate = .;
} > ram
.bkupram (NOLOAD):
{
. = ALIGN(8);
_sbkupram = .;
*(.bkupram .bkupram.*);
. = ALIGN(8);
_ebkupram = .;
} > bkupram
.qspi (NOLOAD):
{
. = ALIGN(8);
_sqspi = .;
*(.qspi .qspi.*);
. = ALIGN(8);
_eqspi = .;
} > qspi
/* .bss section which is used for uninitialized data */
.bss (NOLOAD) :
{
. = ALIGN(4);
_sbss = . ;
_szero = .;
*(.bss .bss.*)
*(COMMON)
. = ALIGN(4);
_ebss = . ;
_ezero = .;
} > ram
/* stack section */
.stack (NOLOAD):
{
. = ALIGN(8);
_sstack = .;
. = . + STACK_SIZE;
. = ALIGN(8);
_estack = .;
} > ram
. = ALIGN(4);
_end = . ;
}

经过大量实验、试验和错误,事实证明,ram 部分似乎必须按该顺序"重新定位"然后"bss"然后"堆叠"。

我试图做的是将"bss"放在"重新定位"上方,这导致一切都不起作用。所以这就是答案,但任何人都可以解释为什么"bss"必须高于"搬迁"。我这样做是因为我希望将静态成员放在顶部,并且我知道重新定位适用于非静态成员。