应用程序在链接描述文件中定义的访问符号

Posted

技术标签:

【中文标题】应用程序在链接描述文件中定义的访问符号【英文标题】: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 标签。世界上还有其他链接器! :-) 【参考方案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;

在符号表中创建一个名为 @sampfoo 的条目,其中包含 内存位置 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。检查您的编译器消息输出。

以上是关于应用程序在链接描述文件中定义的访问符号的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法从 ELF 中提取符号,并使用 LD 将另一个文件与之前 ELF 中定义的符号链接在一起?

Linux基础结课考核

从 public/storage 到 storage/app/public 的符号链接仍然使 storage 目录中的文件可以从 Web 访问

在 C 函数中定义一个唯一的全局程序集标签/符号

静态链接

链接常识