我正在从事一个大学项目,我正在从头开始为 Atmel SAM7S256 微控制器编写软件。这比我以前使用过的其他 MCU 更深入,因为这次需要了解链接器脚本和汇编语言。
我一直在仔细研究 SAM7S 芯片的示例项目,以便完全了解如何从头开始启动 SAM7/ARM 项目。一个值得注意的例子是 Miro Samek 的“使用 GNU 构建裸机 ARM 系统”教程,可在此处找到(此问题中的代码来自哪里)。我还花了很多时间阅读 sourceware.org 上的链接器和汇编器文档。
我很高兴我在很大程度上理解了以下链接器脚本。只有一件事涉及位置计数器,这对我来说没有意义。以下是上述教程提供的链接器脚本:
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_vectors)
MEMORY { /* memory map of AT91SAM7S64 */
ROM (rx) : ORIGIN = 0x00100000, LENGTH = 64k
RAM (rwx) : ORIGIN = 0x00200000, LENGTH = 16k
}
/* The sizes of the stacks used by the application. NOTE: you need to adjust */
C_STACK_SIZE = 512;
IRQ_STACK_SIZE = 0;
FIQ_STACK_SIZE = 0;
SVC_STACK_SIZE = 0;
ABT_STACK_SIZE = 0;
UND_STACK_SIZE = 0;
/* The size of the heap used by the application. NOTE: you need to adjust */
HEAP_SIZE = 0;
SECTIONS {
.reset : {
*startup.o (.text) /* startup code (ARM vectors and reset handler) */
. = ALIGN(0x4);
} >ROM
.ramvect : { /* used for vectors remapped to RAM */
__ram_start = .;
. = 0x40;
} >RAM
.fastcode : {
__fastcode_load = LOADADDR (.fastcode);
__fastcode_start = .;
*(.glue_7t) *(.glue_7)
*isr.o (.text.*)
*(.text.fastcode)
*(.text.Blinky_dispatch)
/* add other modules here ... */
. = ALIGN (4);
__fastcode_end = .;
} >RAM AT>ROM
.text : {
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
*(.glue_7) /* glue arm to thumb (NOTE: placed already in .fastcode) */
*(.glue_7t)/* glue thumb to arm (NOTE: placed already in .fastcode) */
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .; /* global symbol at end of code */
} >ROM
.preinit_array : {
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(SORT(.preinit_array.*)))
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} >ROM
.init_array : {
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
} >ROM
.fini_array : {
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(.fini_array*))
KEEP (*(SORT(.fini_array.*)))
PROVIDE_HIDDEN (__fini_array_end = .);
} >ROM
.data : {
__data_load = LOADADDR (.data);
__data_start = .;
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(4);
_edata = .;
} >RAM AT>ROM
.bss : {
__bss_start__ = . ;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = .;
} >RAM
PROVIDE ( end = _ebss );
PROVIDE ( _end = _ebss );
PROVIDE ( __end__ = _ebss );
.heap : {
__heap_start__ = . ;
. = . + HEAP_SIZE;
. = ALIGN(4);
__heap_end__ = . ;
} >RAM
.stack : {
__stack_start__ = . ;
. += IRQ_STACK_SIZE;
. = ALIGN (4);
__irq_stack_top__ = . ;
. += FIQ_STACK_SIZE;
. = ALIGN (4);
__fiq_stack_top__ = . ;
. += SVC_STACK_SIZE;
. = ALIGN (4);
__svc_stack_top__ = . ;
. += ABT_STACK_SIZE;
. = ALIGN (4);
__abt_stack_top__ = . ;
. += UND_STACK_SIZE;
. = ALIGN (4);
__und_stack_top__ = . ;
. += C_STACK_SIZE;
. = ALIGN (4);
__c_stack_top__ = . ;
__stack_end__ = .;
} >RAM
/* Remove information from the standard libraries */
/DISCARD/ : {
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
}
在整个示例中(例如在 .ramvect、.fastcode 和 .stack 部分中)都有符号定义,例如__ram_start = .;
. 启动汇编代码和初始化 C 代码使用这些地址来初始化 MCU RAM 中的正确位置。
我有一个问题理解是这些符号定义如何导致分配正确的值。这确实发生了,脚本是正确的,我只是不明白如何。
按照我的理解,当您在一个部分中使用位置计数器时,它只包含与该部分本身的虚拟内存地址 (VMA) 的相对偏移量。
因此,例如,在该行__ram_start = .;
中,我希望 __ram_start 被分配一个值 0x0 - 因为它在 .ramvect 部分的最开头被分配了位置计数器的值。但是,为了使初始化代码正常工作(确实如此),必须将 __ram_start 分配为 0x00200000(RAM 开头的地址)。
我原以为这只会按预期工作,如果该行改为__ram_start = ABSOLUTE(.);
或__ram_start = ADDR(.ramvect);
.
__fastcode_start
和也是如此__stack_start__
。它们不能都被定义为地址 0x0,否则程序将无法运行。但是此处链接的文档似乎表明这就是应该发生的事情。这是文档中的引用:
笔记: 。实际上是指从当前包含对象开始的字节偏移量。通常这是 SECTIONS 语句,其起始地址为 0,因此 . 可以用作绝对地址。如果 。但是,在节描述中使用,它指的是从该节开始的字节偏移量,而不是绝对地址。
因此,在这些符号分配期间的位置计数器值应该从相应的部分 VMA 偏移。所以那些“_start”符号应该都设置为0x0。这会破坏程序。
所以很明显我错过了一些东西。我想这可能只是将位置计数器值分配给一个符号(在一个部分内)导致 ABSOLUTE() 默认使用。但我无法在任何地方找到明确的解释来证实这一点。
如果有人可以解决这个问题,请提前感谢。