GNU LD: 如何使用-T指定的链接器脚本覆盖符号值(地址)

14
我的使用情况如下:
  • 我正在使用带有Makefile的典型SDK
  • 我相信链接器是经过修补的gcc。 gcc --version给出4.3.4
  • SDK定义了链接器脚本(称为Linker.ld)
  • Linker.ld包括LinkerMemMap.cfg,该文件定义了链接的ELF镜像中各个部分的绝对地址
  • SDK基于Makefile提供应用程序模板(GNU Make 3.81)和make本身
  • 在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)

适用的内容:

  • 在链接最终的ELF映像之前,我尝试在makefile中将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,--defsym=SYMBOL_RAM_START=$(VALUE_TO_OVERRIDE) -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 --defsym=SYMBOL_RAM_START=$(VALUE_TO_OVERRIDE) -o$(OUTPUT).elf

    3. gcc $(OBJS) -l$(Lib1) -l$(Lib2) --defsym=SYMBOL_RAM_START=$(VALUE_TO_OVERRIDE) -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指定的链接脚本定义的符号?
  • 你们中有人能看出我在这里做错了什么吗?
1个回答

20
在等待某人回复时,我解决了这个问题。这里有一些问题,我想为那些可能犯同样错误的人解释一下我的发现。
首先,任何要传递给链接器的选项都必须使用-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 -Xlinker --defsym=SYMBOL_RAM_START=$(VALUE_TO_OVERRIDE) -o$(OUTPUT).elf` 3.`gcc $(OBJS) -l$(Lib1) -l$(Lib2) -Xlinker --defsym=SYMBOL_RAM_START=$(VALUE_TO_OVERRIDE) -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中所示),它确实起作用。但最终我不得不修补链接器脚本。
这个解决方案并没有真正覆盖符号,而是提供了一种定义符号以便可以被覆盖的方法。

您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Multisync
@Aravind,如果我能抱抱你,我就会了! :) 今晚,我的调试工作涉及一个复杂的多进程操作系统内核,在其中gdb基本上是无用的,因为进程具有相同的(虚拟)地址。当我调试一个复杂的问题时,能够将内存位置分开是非常有帮助的。谢谢!参考:https://github.com/chaos4ever/chaos/commit/177957dac462b6134c5134e6567019f73a401b8b - Per Lundberg
@Multisync 有趣的想法,但我不知道弱符号在链接器符号方面是否起作用。部分分配是一个链接时间的事情,正如我所说的,选项1、2和3之所以没有起作用,是因为链接器需要知道在分配它们时将部分放在哪里,而这是通过链接器脚本进行的。至少根据我上面解释的发现,我认为它是这样做的。 - Aravind
1
@Per Lundberg - 很高兴它对你有用。最终看起来这似乎是一件很简单的事情,但是要达到那个效果却花了一些时间。 - Aravind

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接