【问题标题】:Access symbols defined in the linker script by application应用程序在链接描述文件中定义的访问符号
【发布时间】:2012-01-13 23:40:58
【问题描述】:

在我的链接描述文件中,我定义了两个符号

define symbol _region_RAM_start__     = 0xC0000000;
define symbol _region_RAM_end__       = 0xC00fffff; 

然后我将它们导出,如下所示

export symbol _region_RAM_start__;
export symbol _region_RAM_end__;

从应用程序代码中,我尝试访问这些符号

extern const unsigned int _region_RAM_start__;
extern const unsigned int _region_RAM_end__;
....
int GetRAMSize()
{
    int size = 0;
    unsigned int address_1 = _region_RAM_start__;
    unsigned int address_2 = _region_RAM_end__;
    size = address_2 - address_1 + 1U;
    return size;
}

现在,我预计返回值为 0x00100000,但是,我得到的只是 0。 因此,当我转向调试器时,我注意到 _region_RAM_start___region_RAM_end__ 的值分别为 0xC0000000 和 0xC00fffff,但 address_1address_2 的值分别为 0。

编译器优化设置为“无”。这一直困扰着我一段时间。我在这里是否缺少一些非常明显的东西(除了“我一开始就不应该这样做”)

解决方案 感谢 n.m.为了答案

  unsigned int address_1 = (unsigned int) (&_region_RAM_start__);

否则address_1address_2 都包含垃圾值(即分别在地址 0xC0000000 和 0xC00fffff 处可用的值,但从这段代码的角度来看是垃圾)

【问题讨论】:

  • 这是一个正确的预期行为。符号是地址的名称。 extern xyz的意思是“Linker先生,我不知道xyz的地址,请帮我解决”。
  • 完美!感谢指针@n.m。所以解决方案是使用 address_1 = (unsigned int) (&_region_RAM_start__);
  • 我正要问:define 语法记录在哪里,但后来我终于看到了 iar 标签。世界上还有其他链接器! :-)

标签: c linker iar


【解决方案1】:

这有点老了,但我还是会回答的……

来自ld manual

从源代码访问链接描述文件定义的变量是 不直观。特别是链接描述文件符号不是 相当于高级语言中的变量声明, 相反,它是一个没有值的符号。

在继续之前,重要的是要注意编译器 经常将源代码中的名字转换成不同的名字 当它们存储在符号表中时。例如,Fortran 编译器通常预先或附加下划线和 C++ 执行广泛的名称修改。因此可能有一个 使用的变量名称之间的差异 源代码和定义的相同变量的名称 在链接描述文件中。例如,在 C 中,链接描述文件变量 可以称为:

extern int foo;

但在链接描述文件中它可能被定义为:

_foo = 1000;

然而,在其余示例中,假定没有名称 转变已经发生。

当一个符号用 C 等高级语言声明时, 发生了两件事。首先是编译器保留 程序内存中有足够的空间来保存 value 符号。第二个是编译器创建一个入口 在包含符号地址的程序符号表中。 即符号表包含内存块的地址 持有符号的值。所以例如下面的C 声明,在文件范围内:

int foo = 1000;

在符号表中创建一个名为“foo”的条目。此条目 保存一个 int 大小的内存块的地址,其中 最初存储数字 1000。

当程序引用符号时,编译器会生成代码 首先访问符号表以找到 符号的内存块,然后代码从中读取值 内存块。所以:

foo = 1;

在符号表中查找符号foo,得到地址 与此符号关联,然后将值 1 写入 那个地址。鉴于:

int * a = & foo;

在符号表中查找符号foo,得到它的地址 然后将此地址复制到关联的内存块中 使用变量“a”。

相比之下,链接器脚本符号声明创建一个条目 在符号表中,但不为它们分配任何内存。因此 它们是没有值的地址。例如链接器 脚本定义:

foo = 1000;

在符号表中创建一个名为 @samp{foo} 的条目,其中包含 内存位置 1000 的地址,但没有存储任何特殊内容 在地址 1000。这意味着您无法访问 value 链接描述文件定义的符号 - 它没有任何价值 - 你可以做的一切 是使用链接描述文件定义符号的地址

因此,当您在源代码中使用链接描述文件定义的符号时 代码,您应该始终获取符号的地址,并且永远不要 尝试使用它的价值。例如假设你想复制 将一段名为 .ROM 的内存的内容放入一个段中 称为 .FLASH 并且链接描述文件包含以下声明:

start_of_ROM   = .ROM;
end_of_ROM     = .ROM + sizeof (.ROM);
start_of_FLASH = .FLASH;

那么执行复制的 C 源代码将是:

extern char start_of_ROM, end_of_ROM, start_of_FLASH;

memcpy (& start_of_FLASH, & start_of_ROM, & end_of_ROM - & start_of_ROM);

注意“&”运算符的使用。他们是正确的。

【讨论】:

  • 如果你从其他来源复制粘贴,你应该引用它。 sourceware.org/binutils/docs/ld/Source-Code-Reference.html
  • 多年来我一直在编写链接器脚本和混合 C/ASM 代码,这让我大吃一惊。当然,链接器符号始终是存储位置而不是解释过的值是有道理的。谢谢。
【解决方案2】:

由于它们是您尝试访问的通用地址符号,而不一定是指向特定类型的指针,因此您不想将它们声明为 unsigned int,而是将它们声明为

extern void _region_RAM_START;

那么 &_region_RAM_START 将具有适当的类型 'void *'。

【讨论】:

    【解决方案3】:

    下面的代码应该可以按预期工作:

    extern const volatile unsigned int _region_RAM_start__;
    
    extern const volatile unsigned int _region_RAM_end__;
    
    ....
    int GetRAMSize()
    
    {
    
    int size = 0;
    
    unsigned int address_1 = &_region_RAM_start__;
    
    unsigned int address_2 = &_region_RAM_end__;
    
    size = address_2 - address_1 + 1U;
    
    return size;
    
    }
    

    【讨论】:

    • 如果_region_RAM_end__unsigned int,则表达式&_region_RAM_end__unsigned int*,不能分配给unsigned int address_2。检查您的编译器消息输出。
    猜你喜欢
    • 2012-04-06
    • 2017-02-21
    • 2018-11-15
    • 2012-02-28
    • 2017-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-03
    相关资源
    最近更新 更多