【问题标题】:GCC - how to tell linker not to skip unused sectionsGCC - 如何告诉链接器不要跳过未使用的部分
【发布时间】:2017-06-21 12:02:44
【问题描述】:

我的问题如下:

我正在尝试编写嵌入式应用程序,它必须提供自己的链接器脚本(使用 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 部分?

【问题讨论】:

    标签: gcc cmake linker


    【解决方案1】:

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

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

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

    与链接相同:

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

    链接器无条件地使用链接序列中的每个目标文件, 所以在A的情况下,它会在prog中链接a.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) 添加到您的链接器脚本(再次使用 mangling 对于 C++)。这相当于 1

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

    3 实际上就是您在发现以下情况时所做的:

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

    但是,对于 3,您不需要在您的项目和任何其他项目中编译 notmain.cpp 需要notmain.o的项目。您可以独立构建它,安装它 在 /usr/local/lib 中并明确添加 /usr/local/lib/notmain.o 到 您的项目的链接。那将遵循 GCC 本身的示例,该示例明确 链接普通程序的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
    

    【讨论】:

    • 谢谢,这是非常解释性的答案。我终于明白为什么它会以它的方式工作。非常感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-28
    • 2010-12-01
    • 1970-01-01
    • 2011-02-18
    • 2011-03-17
    • 1970-01-01
    • 2014-04-14
    相关资源
    最近更新 更多