【问题标题】:GNU LD: How to override a symbol value (an address) defined by the linker script specified using -TGNU LD:如何覆盖使用 -T 指定的链接描述文件定义的符号值(地址)
【发布时间】:2012-04-19 10:17:38
【问题描述】:

我的用例如下:

  • 我正在使用基于 Makefile 的项目附带的典型 SDK
  • 我相信链接器已修补 gcc。 gcc --version 给我 4.3.4
  • SDK 定义了链接器脚本(我们称之为 Linker.ld)
  • Linker.ld 包括 LinkerMemMap.cfg,它定义了链接的 ELF 映像中各个部分的绝对地址
  • SDK 提供基于 Makefiles (GNU Make 3.81) 的应用程序模板并自行制作
  • 在 SDK 提供的 Makefile 模板中,当调用 gcc 时,Linker.ld 带有 -T 命令行选项,如下所示:

gcc $(OBJS) -l$(Lib1) -l$(Lib2) -nostdlib -lgcc -L$(library_path) -g -msmall-mode -mconst-switch-tables -mas-mode -mno-initc -Wl,--start-group,--end-group,-T,$(PATH_TO_Linker.ld),--gc-sections -o$(OUTPUT).elf

我的要求如下:

  • 我想使用 Linker.ld 中定义的部分,并按照 LinkerMemMap.cfg 使用内存映射,但是调整 LinkerMemMap.cfg 中定义的特定符号(我们称之为 SYMBOL_RAM_START)

什么有效:

  • 我已经尝试在 makefile 中,在链接最终 ELF 映像之前,将 LinkerMemMap.cfg(包含在 Linker.ld 中)复制到构建目录并修补它以重新定义 SYMBOL_RAM_START。这确实起作用,因为链接器首先在当前文件夹中搜索链接器脚本和链接器脚本包含的文件。

什么不能:

  • 不幸的是,我们的利益相关者认为上述方法风险太大且太复杂而无法理解。我想用以下内容覆盖链接器命令行上的符号值:

    1. gcc $(OBJS) -l$(Lib1) -l$(Lib2) -nostdlib -lgcc -L$(library_path) -g -msmall-mode -mconst-switch-tables -mas-mode -mno-initc -Wl,--start-group,--end-group,-T,$(PATH_TO_Linker.ld),--gc-sections,<b>--defsym=SYMBOL_RAM_START=$(VALUE_TO_OVERRIDE)</b> -o$(OUTPUT).elf

    2. gcc $(OBJS) -l$(Lib1) -l$(Lib2) -nostdlib -lgcc -L$(library_path) -g -msmall-mode -mconst-switch-tables -mas-mode -mno-initc -Wl,--start-group,--end-group,-T,$(PATH_TO_Linker.ld),--gc-sections <b>--defsym=SYMBOL_RAM_START=$(VALUE_TO_OVERRIDE)</b> -o$(OUTPUT).elf

    3. gcc $(OBJS) -l$(Lib1) -l$(Lib2) <b>--defsym=SYMBOL_RAM_START=$(VALUE_TO_OVERRIDE)</b> -nostdlib -lgcc -L$(library_path) -g -msmall-mode -mconst-switch-tables -mas-mode -mno-initc -Wl,--start-group,--end-group,-T,$(PATH_TO_Linker.ld),--gc-sections -o$(OUTPUT).elf

这些似乎对链接器创建的链接图像没有任何影响。

  • --defsym 能否覆盖使用 -T 指定的链接脚本定义的符号?
  • 请大家看看我在这里做错了什么?

【问题讨论】:

    标签: gcc ld linker-scripts


    【解决方案1】:

    在等待有人回复时,我确实解决了问题。这里的问题几乎没有问题,我想向可能犯同样错误的人解释我的发现。

    首先 任何要传递给链接器的选项都必须使用 -Xlinker 或 -Wl 指定。因此 2 和 3 在上述情况下都不起作用。修正后的 2 和 3 如下:

    1. 已经正确了

    2. gcc $(OBJS) -l$(Lib1) -l$(Lib2) -nostdlib -lgcc -L$(library_path) -g -msmall-mode -mconst-switch-tables -mas-mode -mno-initc -Wl,--start-group,--end-group,-T,$(PATH_TO_Linker.ld),--gc-sections <b>-Xlinker --defsym=SYMBOL_RAM_START=$(VALUE_TO_OVERRIDE)</b> -o$(OUTPUT).elf

    3. gcc $(OBJS) -l$(Lib1) -l$(Lib2) <b>-Xlinker --defsym=SYMBOL_RAM_START=$(VALUE_TO_OVERRIDE)</b> -nostdlib -lgcc -L$(library_path) -g -msmall-mode -mconst-switch-tables -mas-mode -mno-initc -Wl,--start-group,--end-group,-T,$(PATH_TO_Linker.ld),--gc-sections -o$(OUTPUT).elf

    现在对于上述选项 1 和 2,--defsym 位于链接描述文件之后,并且链接描述文件已经定义了 SYMBOL_RAM_START。它确实覆盖它。但是不会使用被覆盖的值,因为这些部分已经被定义为链接描述文件已经被使用了。

    对于上述选项 3 的情况,SYMBOL_RAM_START 是在链接器读取链接器脚本之前定义的。因此,当解析链接描述文件时,脚本中指定的值会覆盖它。

    解决办法:

    为了使其工作,链接描述文件需要有条件地初始化符号 SYMBOL_RAM_START,如下所示:

    SYMBOL_RAM_START = DEFINED( SYMBOL_RAM_START ) ? SYMBOL_RAM_START : DEFAULT_VALUE ;

    鉴于链接描述文件中的上述内容,当在包含链接描述文件之前定义 SYMBOL_RAM_START 时(如上面的选项 3 所示),它确实有效。但最后我不得不修补链接描述文件。

    这个解决方案并没有真正覆盖符号,而是提供了一种可以定义符号以便覆盖它的方法。

    【讨论】:

    • 只是一个想法,我不确定它是否有效:在基本链接器脚本中将SYMBOL_RAM_START 声明为弱符号是否也有效?这样,通过命令行参数提供的值可能会覆盖链接描述文件中的标签。
    • @Aravind,如果我能给你一个拥抱,我会的! :) 这个答案今晚对我来说是无价的,因为我正在调试一个复杂的多进程操作系统内核,其中gdb 基本上是无用的,因为这些进程具有相同的(虚拟)地址。在调试复杂问题时,能够将内存位置分开对我非常有帮助。谢谢!供参考:github.com/chaos4ever/chaos/commit/…
    • @Multisync 有趣的想法,但我不知道弱符号在链接器符号方面的工作方式是否相同。节分配是链接时间的事情,正如我所说的那样,选项 1、2 和 3 并不能精确地工作,因为链接器需要知道将节放在哪里,因为它在通过链接描述文件时分配它们。至少根据我上面解释的发现,我认为它是这样做的。
    • @Per Lundberg - 我很高兴它对你有用。到头来,这似乎是一件很简单的事情,但是却花了一些时间。
    猜你喜欢
    • 2015-05-05
    • 2018-11-09
    • 2014-04-22
    • 2011-03-26
    • 2012-04-06
    • 2012-02-28
    • 2011-11-04
    • 2013-05-17
    • 1970-01-01
    相关资源
    最近更新 更多