了解GNU链接脚本的位置计数器(Understanding the Location Counter

2019-07-05 00:11发布

我的工作,我正在写软件的从地上爬起来的爱特梅尔微控制器SAM7S256一所大学的项目。 这是更深入地比其他MCU我合作过,如连接器脚本语言和汇编语言的知识是必要的这一次。

我一直都仔细检查了SAM7S芯片示例项目,以充分了解如何从头开始一个SAM7 / ARM项目。 一个明显的例子是米罗萨梅克的“建筑裸机ARM系统的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部分)存在诸如符号定义__ram_start = .; 。 这些地址,以初始化在MCU的RAM正确的位置使用的启动汇编代码和初始化C代码。

我有一个问题的理解,是这些符号的定义是如何导致被分配了正确的价值观。 这确实发生了,剧本是正确的,我只是不明白怎么样。

我的理解是,当你使用一节内的位置计数器的方式,它仅包含一个相对从部分本身的虚拟内存地址(VMA)的偏移量。

因此,例如,在线路__ram_start = .; ,我希望__ram_start被分配值为0x0 - 因为它是在.ramvect节一开始就指定位置计数器的值。 然而,对于初始化代码才能正常工作(其它),__ram_start一定是指定为0​​x00200000(地址为RAM的开头)。

我本来以为这只会工作打算,如果行是不是__ram_start = ABSOLUTE(.);__ram_start = ADDR(.ramvect);

这同样适用于__fastcode_start__stack_start__ 。 他们不能全部得到定义为地址0x0,否则程序将无法正常工作。 但文档链接在这里似乎表明,这就是应该发生。 下面是从文档报价:

注意: 。 实际上指的是字节从当前包含对象的起始位置的偏移。 通常,这是该科的语句,其起始地址为0,因此。 可作为绝对地址。 如果。 使用段说明内部然而,它指的是字节从该部分,而不是绝对地址的起始偏移。

所以在这些符号分配位置的计数器值应该是从相应的部分的VMA偏移。 因此,这些“_start”符号应该得到设置为0x0。 这将打破计划。

所以,很显然,我失去了一些东西。 我想这可以简单地是分配位置计数器值的符号(一个段内)在无水结果()正在使用的默认值。 但我一直没能在别处找到一个明确的解释,即证明了这一点。

在此先感谢如果有人可以清除这件事。

Answer 1:

我想我可能已经找到了答案,以我自己的问题。 我不知道我是对的,但它是第一个解释,我已经能够想到的,其实是有道理的。 是什么让我重新思考的事情是文档的这个页面 。 特别是这句话:

地址和符号可以是相对的部分,或绝对的。 A节相对符号是可重定位的。 如果请求使用`-r”选项重定位输出,进一步的链接操作可改变部分相对符号的值。 在另一方面,绝对符号将保留在整个任何进一步的链接操作相同的值。

而这句话:

您可以使用内置的功能绝对给力的表现是绝对的,当它本来是相对的。 例如,为了创建一个绝对符号设置为输出部的端部的地址.data

  SECTIONS { .data : { *(.data) _edata = ABSOLUTE(.); } } 

如果ABSOLUTE没有使用, _edata将相对.data部分。

我以前读过他们,但是这一次我看见他们从一个新的视角。

所以我觉得我的误解是认为一个符号,分配相关的字节偏移地址时,被简单地设置的,虽然基址信息丢失偏移值。

这是根据从我原来的问题这句话:

注意: 。 实际上指的是字节从当前包含对象的起始位置的偏移。 通常,这是该科的语句,其起始地址为0,因此。 可作为绝对地址。 如果。 使用段说明内部然而,它指的是字节从该部分,而不是绝对地址的起始偏移。

相反,我现在明白要发生的是,基址信息也不会丢失。 符号不是简单地得到分配的基地址的偏移值。 符号仍然将最终解析到一个绝对地址,但只有当没有机会的基地址可以改变。

那么,我认为,像__stack_start__ = . ; __stack_start__ = . ; 应必须被改变到__stack_start__ = ABSOLUTE(.) ; ,它不工作,我现在认为这是不必要的。 更重要的是,我从这个回应,你可以重新链接ELF文件的第一次报价明白了吗?

所以,如果我用__stack_start__ = ABSOLUTE(.) ; ,跑了链接脚本创建ELF可执行文件,然后试图重新链接它和移动.STACK部分其他地方,则__stack_start__符号仍会被指向从第一个链接相同的绝对地址,因而是不正确的。

这可能是难以遵循,但我已经为铰接,我可以写它。 我怀疑我已经接近了正确的想法,但我仍然需要有人究竟是谁知道这个东西来证实或否认这一点。



Answer 2:

该部分的位置由存储区中的大括号(后测定>RAM AT>ROM )。 所以执行的地址是在RAM在0x00200000和以下,但加载地址是在ROM(闪存)在0x00100000。 启动代码必须复制.fastcode从它的负载输出部分,其执行的地址,这就是符号是。

请注意,这些不需要在地址0,因为AT91SAM7S重新映射RAM或ROM地址0通常它启动了映射的ROM,并启动代码转换,为RAM。



文章来源: Understanding the Location Counter of GNU Linker Scripts