应用程序在链接描述文件中定义的访问符号
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_1
和 address_2
的值分别为 0。
编译器优化设置为“无”。这一直困扰着我一段时间。我在这里是否缺少一些非常明显的东西(除了“我一开始就不应该这样做”)?
解决方案 感谢 n.m.为了答案
unsigned int address_1 = (unsigned int) (&_region_RAM_start__);
否则address_1
和address_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 中定义的符号链接在一起?
从 public/storage 到 storage/app/public 的符号链接仍然使 storage 目录中的文件可以从 Web 访问