我正在进行一项大学项目,编写适用于Atmel SAM7S256微控制器的软件。这比我之前使用的其他MCU更加深入,因为这次需要了解链接脚本和汇编语言的知识。
为了完全理解如何从头开始启动SAM7/ARM项目,我一直在仔细研究SAM7S芯片的示例项目。一个值得注意的例子是Miro Samek的“使用GNU构建裸机ARM系统”教程,可以在这里找到(此问题中的代码来自该教程)。我还花了很多时间阅读来自sourceware.org的链接器和汇编器文档。
我非常高兴我大部分都理解了上述教程中提供的链接器脚本。只有一个涉及位置计数器的事情让我感到困惑。下面是该链接器脚本:
在示例中(例如在.ramvect、.fastcode和.stack部分中),存在符号定义,如
注意:.实际上是从当前包含对象的开头开始的字节偏移量。通常这是SECTIONS语句,其起始地址为0,因此可以将.用作绝对地址。但是,如果在部分描述中使用.,则它将引用该部分的开头处的字节偏移量,而不是绝对地址。
因此,在这些符号分配期间,位置计数器值应该是相应部分VMAs的偏移量。因此,这些“_start”符号应该全部设置为0x0。这将破坏程序。
很明显我漏掉了什么。我想这可能只是将位置计数器的值分配给符号(在一个节内)默认使用ABSOLUTE()。但我无法找到任何清晰的解释来确认这一点。如果有人能澄清这个问题,提前感谢。
为了完全理解如何从头开始启动SAM7/ARM项目,我一直在仔细研究SAM7S芯片的示例项目。一个值得注意的例子是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,否则程序将无法工作。但是文档linked here似乎表明应该发生这种情况。以下是文档中的引用:注意:.实际上是从当前包含对象的开头开始的字节偏移量。通常这是SECTIONS语句,其起始地址为0,因此可以将.用作绝对地址。但是,如果在部分描述中使用.,则它将引用该部分的开头处的字节偏移量,而不是绝对地址。
因此,在这些符号分配期间,位置计数器值应该是相应部分VMAs的偏移量。因此,这些“_start”符号应该全部设置为0x0。这将破坏程序。
很明显我漏掉了什么。我想这可能只是将位置计数器的值分配给符号(在一个节内)默认使用ABSOLUTE()。但我无法找到任何清晰的解释来确认这一点。如果有人能澄清这个问题,提前感谢。
0x00200040
开始并在0x0020029C
结束。 .data 由于为空,从0x0020029C
开始并结束。 .bss 从0x0020029C
开始并在0x00200438
结束。 所以一切看起来都很好。 - Adam Goodwin.section .fastcode
,而应改为使用.section .fastcode,"ax",%progbits
。因为如果您不添加标志,您的代码只有在某些情况下才会被包括进来(如果你很幸运)。 - user1985657