GCC - 如何告诉链接器不要跳过未使用的部分



我的问题是:

我正在尝试编写嵌入式应用程序,该应用程序必须提供自己的链接器脚本(使用 arm-none-eabi-gcc 编译器/链接器)。

嵌入式引导加载程序加载二进制文件并从0x8000地址开始,这就是为什么我需要专用的链接器脚本,它允许我将所需的启动函数放入该地址。脚本的代码如下:

MEMORY
{
ram : ORIGIN = 0x8000, LENGTH = 0x1000
}
SECTIONS
{
.start : { *(.start) } > ram
.text : { *(.text*) } > ram
.bss : { *(.bss*) } > ram
}

有了这个,我现在想做的是有一个函数,它将插入 .start 部分,以便它位于0x8000的开头。为此,在我的库中,我使用以下函数:

__attribute__((section(".start"))) void notmain() {
main();
}

这似乎工作正常,但后来我将这个库与函数notmain与定义main()函数的项目链接起来。在链接过程中.start我可以看到该部分不再存在并且notmain符号 完全不见了。当我将notmain函数移出库(进入项目)时,一切都很好。

我的理解是,链接器看到,.start 部分在我的应用程序中根本没有使用,这使得它跳过所有部分。我已经尝试向函数 notmain 添加几个属性,例如 (__attribute__((used)) __attribute__((externally_visible))),但它也不起作用(最终二进制文件中仍然缺少 notmain)。

CMake 源代码如下:

**项目**

project(AutomaticsControlExample)
enable_language(ASM)
set(CMAKE_CXX_STANDARD 14)
set(SOURCES main.cpp PID.hpp)
set(DEPENDENCIES RPIRuntime PiOS)
add_executable(${PROJECT_NAME} ${SOURCES})
target_link_libraries(${PROJECT_NAME} ${DEPENDENCIES})
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_OBJDUMP} -D ${PROJECT_NAME}
COMMAND ${CMAKE_OBJDUMP} -D ${PROJECT_NAME} > ${PROJECT_NAME}.list
COMMAND ${CMAKE_OBJCOPY} ${PROJECT_NAME} -O binary ${PROJECT_NAME}.bin
COMMAND ${CMAKE_OBJCOPY} ${PROJECT_NAME} -O ihex ${PROJECT_NAME}.hex)

**图书馆**

project(RPIRuntime)
enable_language(ASM)
set(CMAKE_CXX_STANDARD 14)
set(LINKER_SCRIPT memmap)
set(LINKER_FLAGS "-T ${CMAKE_CURRENT_SOURCE_DIR}/${LINKER_SCRIPT}")

set(SOURCES
notmain.cpp
assert.cpp)
add_library(${PROJECT_NAME} STATIC ${SOURCES})
target_link_libraries(${PROJECT_NAME} ${LINKER_FLAGS})

我的问题是:有没有办法防止链接器省略链接.start部分?

如您所知,静态库是对象文件的ar存档。

假设libfoobar.a只包含foo.obar.o。联动:

g++ -o prog a.o foo.o bar.o     # A

与联动不同

g++ -o prog a.o -lfoobar.   # B

链接器无条件使用链接序列中的每个对象文件, 所以在A的情况下,它会proga.ofoo.obar.o链接。

链接器不会无条件地使用作为 成员的每个对象文件 链接序列中的静态库。静态库是一种提供 链接器 一堆对象文件,可从中选取所需的对象文件。

假设a.o调用函数foo,这是在foo.o中定义的,并且a.o引用bar.o中未定义任何内容。

在这种情况下,链接器无条件地将a.o链接到prog,之后prog包含对foo的未定义引用,链接器需要 定义。接下来,它到达libfoobar.a并检查存档(通过其索引, 通常)以查看存档的任何成员是否定义了foo。它发现foo.o确实如此 所以。因此,它从存档中提取foo.o并链接它。它不需要定义 对于bar.o中定义的任何符号,因此不会将bar.o添加到链接中。这 联动B与以下内容完全相同:

g++ -o prog a.o foo.o

另一方面,假设a.o调用bar,这是在bar.o中定义的, 并且未引用foo.o中定义的任何内容。在这种情况下,链接B为 与:

g++ -o prog a.o bar.o

因此,插入到静态库中以与之链接的对象文件 默认情况下,您的可执行文件永远不会被链接,除非它提供定义 对于在对象文件中引用但未定义的至少一个符号 这已经链接了。

您的函数notmain未在唯一的对象文件中引用,main.o您在程序中显式链接。因此,当main.o链接到您的程序时, 该程序不包含对notmain的未定义引用:链接器不需要定义notmain- 它从未听说过notmain- 并且不会链接任何目标文件 从静态库中获取notmain的定义。这没什么 与链接部分有关。

将普通程序与静态库链接时,理所当然 你这样做是这样的:

g++ -o prog main.o x.o ... -ly -lz ....

其中*.o文件之一(例如main.o)是定义main函数的对象文件。你从来没有 将main.o放入其中一个静态库中。那是因为,在一个普通的程序中,main未在显式链接的任何其他对象文件中调用, 因此,如果main.o在您的某个库中,则链接:

g++ -o prog x.o ... -ly -lz ...

不需要在任何-ly -lz ...中找到main的定义,也没有定义 的main将被链接。

情况与您的notmain相同.如果你想链接它,你可以做以下一件事:-

  1. -Wl,--undefined=notmain添加到链接选项(将notmain替换为notmain的残缺名称,为C++)。这将使链接器假定它具有 对notmain的未定义引用,即使它没有看到任何引用。

  2. 将命令EXTERN(notmain)添加到链接器脚本(再次重整 对于C++)。这相当于1

  3. 显式链接定义notmain的对象文件。不要把它放在静态库中。

3实际上是您在发现时所做的:

当我将notmain函数移出库(进入项目)时,一切都很好。

但是,对于3,您不需要在项目和任何其他项目中编译notmain.cpp需要notmain.o的项目。您可以独立构建,安装它/usr/local/lib并显式添加/usr/local/lib/notmain.o项目的链接。这将遵循海湾合作委员会本身的例子,它明确地 链接普通程序的crt*.o启动文件,只需附加它们的 链接的绝对名称,例如

/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crti.o
...
/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crtn.o

最新更新