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

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

问题描述

我正在从事一个大学项目,我正在从头开始为 Atmel 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 项目.一个值得注意的例子是 Miro Samek 的使用 GNU 构建裸机 ARM 系统"教程发现 here(这个问题中的代码来自哪里).我还花了很多时间阅读 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、.fastcode 和 .stack 部分),都有诸如 __ram_start = .; 之类的符号定义.这些地址由启动汇编代码和初始化 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 部分.但是,要使初始化代码正常工作(确实如此),必须将 __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);.

__fastcode_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.

因此,这些符号分配期间的位置计数器值应该是相对于相应部分 VMA 的偏移量.所以那些_start"符号应该all被设置为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.

很明显我错过了一些东西.我想这可能只是将位置计数器值分配给一个符号(在一个部分内)导致默认使用 ABSOLUTE() .但我一直无法在任何地方找到明确的解释来证实这一点.

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:

地址和符号可以是相对的,也可以是绝对的.一节相对符号是可重定位的.如果您请求可重定位输出使用‘-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 来强制表达式为当它是相对的时是绝对的.例如,要创建设置为输出部分末尾地址的绝对符号.data:

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(.); }
   }

如果没有使用 ABSOLUTE_edata 将相对于 .data部分.

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.

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

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:

注意:.实际上是指从开头的字节偏移量当前包含对象.通常这是 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.

相反,我现在了解到正在发生的是基地址信息没有丢失.符号不会简单地被分配到基地址的偏移值.该符号最终仍会解析为绝对地址,但前提是其基地址不可能更改.

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天全站免登陆