了解GNU链接脚本的位置计数器 [英] Understanding the Location Counter of GNU Linker Scripts

查看:311
本文介绍了了解GNU链接脚本的位置计数器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm working on a university project where I'm writing software for an Atmel SAM7S256 microcontroller from the ground up. This is more in depth than other MCUs I've worked with before, as a knowledge of linker scripts and assembly language is necessary this time around.

我一直很仔细审查了SAM7S芯片示例项目,以充分了解如何从头开始一个SAM7 / ARM项目。一个明显的例子是米罗萨梅克的建设裸机ARM系统的GNU教程中这里(凡在这个问题上的code是)。我也花了很多时间阅读sourceware.org的连接器和汇编文件。

I've been really scrutinizing example projects for the SAM7S chips in order to fully understand how to start a SAM7/ARM project from scratch. A notable example is Miro Samek's "Building Bare-Metal ARM Systems with GNU" tutorial found here (where the code in this question is from). I've also spent a lot of time reading the linker and assembler documentation from sourceware.org.

我很高兴,我知道大部分下面的链接脚本。这里有一个涉及位置计数器没有任何意义,我的事情。下面是设置有上面的教程链接脚本:

I'm quite happy that I understand the following linker script for the most part. There's just one thing involving the location counter that doesn't make sense to me. Below is the linker script provided with the above tutorial:

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,。快code和.STACK部分)有,如 __ ram_start =符号定义; 。这些地址用于启动组装code和初始化C $ C $Ç以初始化MCU的RAM的正确位置。

Throughout the example (such as in the .ramvect, .fastcode and .stack sections) there are symbol definitions such as __ram_start = .;. These addresses are used by the startup assembly code and initialization C code in order to initialize the correct locations in the MCU's RAM.

我有什么问题的理解,是这些符号的定义是如何导致被分配了正确的价值观。发生这种情况时,脚本是正确的,我只是不明白怎么样。

What I have a problem understanding, is how these symbol definitions result in the correct values being assigned. This does happen, the script is correct, I just don't understand how.

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

The way I understand it, when you use the location counter within a section, it only contains a relative offset from the virtual memory address (VMA) of the section itself.

因此​​,例如,在该行 __ ram_start =; ,我希望__ram_start被分配值为0x0 - 因为它被指定的位置计数器的值。在.ramvect部的最开始。然而,对于初始化code键正确地(其它)工作,__ram_start必须得到分配为0x00200000(地址为RAM的开头)。

So for example, in the line __ram_start = .;, I would expect __ram_start to be assigned a value of 0x0 - as it is assigned the value of the location counter at the very beginning of the .ramvect section. However, for the initialization code to work correctly (which it does), __ram_start must be getting assigned as 0x00200000 (the address for the beginning of RAM).

我还以为这只会工作打算,如果该行是不是 __ ram_start = ABSOLUTE(); __ ram_start = ADDR(。 ramvect);

I would have thought this would only work as intended if the line was instead __ram_start = ABSOLUTE(.); or __ram_start = ADDR(.ramvect);.

这同样适用于 __快code_start __ __ stack_start 。他们不能全部得到定义为地址0x0,否则程序是行不通的。但链接的文档这里似乎表明,这就是应该发生。下面是从文档报价:

The same goes for __fastcode_start and __stack_start__. They can't all be getting defined as address 0x0, otherwise the program wouldn't work. But the documentation linked here seems to suggest that that's what should be happening. Here's the quote from the documentation:

请注意:。实际上指的是字节从当前的包含对象的起始位置的偏移。通常情况下,它是SECTIONS语句,其起始地址为0,因此。可作为绝对地址。如果。用于不过在段说明内部,它指的是字节从该部分,而不是绝对地址开始的偏移量。

Note: . actually refers to the byte offset from the start of the current containing object. Normally this is the SECTIONS statement, whose start address is 0, hence . can be used as an absolute address. If . is used inside a section description however, it refers to the byte offset from the start of that section, not an absolute address.

因此​​,在这些符号赋值的位置计数器的值应该是从相应的部分音乐录影带大奖偏移。所以,那些_start符号应的所有的越来越设置为0x0。这将打破计划。

So the location counter values during those symbol assignments should be offsets from the corresponding section VMAs. So those "_start" symbols should all be getting set to 0x0. Which would break the program.

所以,很显然,我失去了一些东西。我想这可能仅仅是该指定位置计数器值符号(段内)的绝对导致()使用默认。但我一直没能在任何地方找到一个明确的解释,即证明了这一点。

So obviously I'm missing something. I suppose it could simply be that assigning the location counter value to a symbol (within a section) results in ABSOLUTE() being used by default. But I haven't been able to find a clear explanation anywhere that confirms this.

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

Thanks in advance if anybody can clear this up.

推荐答案

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

I think I may have figured out the answer to my own question. I'm not sure I'm right, but it's the first explanation I've been able to think of that actually makes sense. What made me rethink things was this page of the documentation. Particularly this quote:

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

Addresses and symbols may be section relative, or absolute. A section relative symbol is relocatable. If you request relocatable output using the `-r' option, a further link operation may change the value of a section relative symbol. On the other hand, an absolute symbol will retain the same value throughout any further link operations.

和这句话:

您可以使用内建函数ABSOLUTE强制一个前pression是
  绝对将本来是相对的。例如,要创建
  绝对符号设置为输出部的端部的地址
  。数据

You can use the builtin function ABSOLUTE to force an expression to be absolute when it would otherwise be relative. For example, to create an absolute symbol set to the address of the end of the output section .data:

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

如果绝对都没有使用, _edata 是相对于。数据
  部分。

If ABSOLUTE were not used, _edata would be relative to the .data section.

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

I had read them before, but this time I saw them from a new perspective.

所以,我认为我的misinter pretation的想法是一个符号,分配相对字节偏移地址时,被简单地设置的,虽然基址信息丢失的偏移值。

So I think my misinterpretation was thinking that a symbol, when assigned a relative byte offset address, is simply set to the value of that offset while the base address information is lost.

这是基于这句话从我原来的问题:

That was based on this quote from my original question:

请注意:。实际上指的是从字节的起始位置的偏移
  当前包含对象。通常,这是该科的说法,
  其起始地址为0,因此。可作为绝对地址。
  如果。用于然而一个段说明里,它指的是
  字节来自部分,而不是绝对地址开始的偏移量。

Note: . actually refers to the byte offset from the start of the current containing object. Normally this is the SECTIONS statement, whose start address is 0, hence . can be used as an absolute address. If . is used inside a section description however, it refers to the byte offset from the start of that section, not an absolute address.

相反,我现在明白了要发生的事情是,该基地的地址信息不丢失。符号并不仅仅会被分配的从基地址偏移值。符号仍然最终将解析为绝对地址,但只有当没有机会的基地址可以改变。

Instead what I now understand to be happening is that the base address information is not lost. The symbol does not simply get assigned the value of the offset from the base address. The symbol will still eventually resolves to an absolute address, but only when there's no chance its base address can change.

那么,我认为,像 __ stack_start__ =。 ; 应更改为 __ stack_start__ = ABSOLUTE(); ,它不工作,我现在认为这是不必要的。更重要的是,我从这个响应第一次报价明白,你可以重新链接的ELF文件?

So where I thought that something like __stack_start__ = . ; should have to be changed to __stack_start__ = ABSOLUTE(.) ;, which does work, I now think it is unnecessary. What's more, I understand from the first quote in this response that you can relink an ELF file?

所以,如果我用 __ stack_start__ = ABSOLUTE(); ,运行链接脚本来创建ELF可执行文件,然后试图重新链接它某处移动.STACK节否则,在 __ __ stack_start 符号仍然会被指向从第一个链接相同的绝对地址,因而是不正确的。

So if I used __stack_start__ = ABSOLUTE(.) ;, ran the linker script to create the ELF executable, then tried to relink it and moved the .stack section somewhere else, the __stack_start__ symbol would still be pointing to the same absolute address from the first link, and thus be incorrect.

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

This is probably hard to follow, but I've written it as articulately as I could. I suspect I've got close to the right idea, but I still need someone who actually knows about this stuff to confirm or deny this.

这篇关于了解GNU链接脚本的位置计数器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆