【问题标题】:What is the difference between "load address" and "relocation address"?“加载地址”和“重定位地址”有什么区别?
【发布时间】:2015-03-26 05:50:31
【问题描述】:

关于ldAT (...) 指令,this source 声明:

AT ( ldadr ) The expression ldadr that follows the AT keyword specifies the load address of the section. The default (if you do not use the AT keyword) is to make the load address the same as the relocation address. This feature is designed to make it easy to build a ROM image.

我一直在搜索,但没有找到“加载地址”和“重定位地址”的明确定义。

我了解,当目标文件链接在一起时,代码会在跳转地址中“重新定位”,等等。被重写以指向组合机器代码中的正确偏移量。那么,“重定位地址”是结果目标代码中某个部分开始的偏移量吗?如果是这样,一个部分的“加载地址”怎么可能不同?

如果这两个地址不同,对链接器的输出有何影响?

【问题讨论】:

    标签: gcc linker ld


    【解决方案1】:

    差异至关重要。重定位地址添加到节中的所有重定位。因此,如果它与加载地址不同,则此部分将无法正常工作——部分内的所有重定位都将被解析为错误值。

    那么为什么我们需要这样的技术呢?没有那么多应用程序,但假设 (from here) 你的架构上确实有 0x1000 处的极快内存

    那么你可能需要两个部分来获得重定位地址0x1000:

    .text0 0x1000 : AT (0x4000) { o1/*.o(.text) }
    __load_start_text0 = LOADADDR (.text0);
    __load_stop_text0 = LOADADDR (.text0) + SIZEOF (.text0);
    .text1 0x1000 : AT (0x4000 + SIZEOF (.text0)) { o2/*.o(.text) }
    __load_start_text1 = LOADADDR (.text1);
    __load_stop_text1 = LOADADDR (.text1) + SIZEOF (.text1);
    . = 0x1000 + MAX (SIZEOF (.text0), SIZEOF (.text1));
    

    现在在运行时继续,当您需要 text1 时,自行管理它,以便从其实际加载地址复制到正确的地址:

    extern char __load_start_text1, __load_stop_text1;
    memcpy ((char *) 0x1000, &__load_start_text1,
          &__load_stop_text1 - &__load_start_text1);
    

    然后使用它,因为它被加载在这里自然。这种技术称为叠加。

    我认为,例子很清楚。

    【讨论】:

    • 康斯坦丁,谢谢你的例子。我想明白了:链接代码中的所有跳转、调用等都被计算为好像代码将在“重定位地址”处加载,但在运行时,它实际上是在“加载地址”处加载的?那么“加载地址”实际上是代码出现的目标文件中的偏移量吗?或者目标文件(ELF 等)是否包含指示加载程序在运行时将代码放在内存中的何处?
    • 为了澄清我的问题,一旦链接器决定了一个部分的“加载地址”,这对输出二进制文件有什么影响?
    • 是的,如果你的目标是 UNIX 系统skyfree.org/linux/references/ELF_Format.pdf,它是 ELF 格式的一部分@
    • 好的,我现在看到了...对于 ELF 文件中的每个段,都有一个“程序头”,both 它在文件中的偏移量和 它的加载地址。查看带有readelfvmlinux Linux 内核二进制文件,我可以看到它有3 个段,每个段有10 多个段。这是否意味着引导加载程序将读取那些 ELF 标头并在指定的“加载地址”加载每个段?
    • 其实没用os内核试过。我想,内核不使用标准加载程序,并且引导加载程序可能会忽略 ELF 标头中的某些内容或处理其他内容。我认为尝试查看它们在哪里更简单。
    【解决方案2】:

    经过一番搜索,我找到了更好的答案:

    每个 ... 输出部分都有两个地址。第一个是 VMA,或 虚拟内存地址。这是该部分将具有的地址 输出文件运行。第二个是LMA,或加载内存地址。 这是该部分将被加载的地址。在大多数情况下 这两个地址将相同。他们可能是什么时候的一个例子 不同的是当一个数据段被加载到 ROM 中,然后被复制 当程序启动时进入 RAM(这种技术通常用于 在基于 ROM 的系统中初始化全局变量)。在这种情况下 ROM 地址是 LMA,RAM 地址是 VMA。

    引自:https://www.sourceware.org/binutils/docs/ld/Basic-Script-Concepts.html#Basic-Script-Concepts

    这意味着在像这样的部分声明中:

    section [address] [(type)] :
      [AT(lma)]
      [ALIGN(section_align) | ALIGN_WITH_INPUT]
      [SUBALIGN(subsection_align)]
      [constraint]
      {
        output-section-command
        output-section-command
        …
      } [>region] [AT>lma_region] [:phdr :phdr …] [=fillexp]
    

    [address]is the VMA[AT(lma)]is the LMA

    【讨论】:

    • 非常感谢! +1
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-02-19
    • 1970-01-01
    • 2016-10-05
    • 1970-01-01
    • 2020-03-12
    • 2022-01-24
    • 1970-01-01
    相关资源
    最近更新 更多