Linker 了解GNU链接器脚本的位置计数器

Linker 了解GNU链接器脚本的位置计数器,linker,embedded,arm,gnu,linker-scripts,Linker,Embedded,Arm,Gnu,Linker Scripts,我正在做一个大学项目,我正在为Atmel SAM7S256微控制器从头开始编写软件。这比我以前使用过的其他MCU更深入,因为这一次需要了解链接器脚本和汇编语言 为了完全理解如何从头开始SAM7/ARM项目,我一直在仔细研究SAM7S芯片的示例项目。一个值得注意的例子是Miro Samek的“使用GNU构建裸机手臂系统”教程(这个问题中的代码来自哪里)。我还花了很多时间阅读sourceware.org上的链接器和汇编程序文档 我很高兴我大部分都理解了下面的链接器脚本。只有一件事涉及到位置计数器,这

我正在做一个大学项目,我正在为Atmel SAM7S256微控制器从头开始编写软件。这比我以前使用过的其他MCU更深入,因为这一次需要了解链接器脚本和汇编语言

为了完全理解如何从头开始SAM7/ARM项目,我一直在仔细研究SAM7S芯片的示例项目。一个值得注意的例子是Miro Samek的“使用GNU构建裸机手臂系统”教程(这个问题中的代码来自哪里)。我还花了很多时间阅读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部分中),有一些符号定义,例如
\uu ram\u start=。启动汇编代码和初始化C代码使用这些地址来初始化MCU RAM中的正确位置

我在理解这些符号定义时遇到的一个问题是,这些符号定义如何产生正确的赋值。这确实发生了,脚本是正确的,我只是不明白怎么做

按照我的理解,当您在一个节中使用位置计数器时,它只包含与该节本身的虚拟内存地址(VMA)的相对偏移量

例如,在
\uu ram\u start=,我希望uu ram_start被分配一个值0x0-因为它在.ramvect节的最开始被分配了位置计数器的值。但是,要使初始化代码正常工作(确实如此),uu ram_start必须被分配为0x00200000(ram开头的地址)

我本以为只有当行改为
\uu ram\u start=ABSOLUTE(.)时,这才会像预期的那样工作
\uu ram\u start=ADDR(.ramvect)

这同样适用于
\uuu快速代码\u开始
\uu堆栈\uu开始
。它们不能都被定义为地址0x0,否则程序将无法工作。但文件似乎表明这是应该发生的事情。以下是文档中的引用:

注:。实际上是指从当前包含对象的起始处开始的字节偏移量。通常这是SECTIONS语句,其起始地址为0,因此。可以用作绝对地址。如果。在节描述中使用,但它指的是从该节开始的字节偏移量,而不是绝对地址

因此,这些符号分配期间的位置计数器值应为与相应部分VMA的偏移量。所以这些“_start”符号都应该设置为0x0。这会破坏程序

所以很明显我错过了什么。我想可能只是将位置计数器值指定给符号(在节中)会导致默认情况下使用ABSOLUTE()。但我还没有找到一个明确的解释来证实这一点


如果有人可以清除此内容,请提前感谢。

节的位置由右括号后面的内存区域决定(
>ramat>ROM
)。因此,执行地址在0x00200000及其后的RAM中,但加载地址在0x00100000的ROM(闪存)中。启动代码必须将
.fastcode
输出部分从其加载复制到其执行地址,这就是符号的用途


请注意,这些不需要位于地址0,因为AT91SAM7S将RAM或ROM重新映射到地址0。通常它是用ROM映射启动的,启动代码会将其切换到RAM。

我想我可能已经找到了我自己问题的答案。我不确定我是否正确,但这是我能想到的第一个解释,实际上是有意义的。让我重新思考的是。特别是这句话:

地址和符号可以是段相对的,也可以是绝对的。一节 相对符号是可重定位的。如果您请求可重定位输出 使用“-r”选项,进一步的链接操作可能会更改该值 一个相对符号的部分。另一方面,一个绝对的符号 将在任何进一步的链接操作中保持相同的值

这句话:

可以使用内置函数ABSOLUTE强制表达式 绝对的,否则它是相对的。例如,创建 设置为输出段末尾地址的绝对符号
.data

 SECTIONS
   {
     .data : { *(.data) _edata = ABSOLUTE(.); }
   }
如果未使用
绝对值
数据将与
数据相关
节

我以前读过,但这次我从一个新的角度看

所以我认为我的误解是,当分配一个相对字节偏移量地址时,一个符号被简单地设置为偏移量的值,而基址信息丢失

这是基于我最初问题的引述:

注:。实际上是指从 当前包含的对象。通常情况下,这是一段声明, 其起始地址为0,因此。可以用作绝对地址。 如果。在节描述中使用,但它指的是 从该节开始的字节偏移量,而不是绝对地址

相反,我现在理解的是,基址信息没有丢失。该符号并不是简单地从基址获得偏移量的值。符号最终仍将解析为绝对地址,但前提是其基址不可能更改

所以我认为像
\uu堆栈
.ramvect : {                        /* used for vectors remapped to RAM */
    __ram_start = .;
    . = 0x40;
} >RAM