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

时间:2017-06-21 12:02:44

标签: gcc cmake linker

我的问题如下:

我正在尝试编写嵌入式应用程序,它必须提供自己的链接描述文件(使用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符号 完全失踪了。当我将非主要功能从库中移出(进入项目)时,一切都很好。

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

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部分?

1 个答案:

答案 0 :(得分:3)

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

假设libfoobar.a仅包含foo.obar.o。联系:

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

与链接相同:

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

链接器无条件地消耗链接序列中的每个目标文件, 所以在A的情况下,它会在a.o中链接foo.obar.oprog

链接器无条件地使用作为其成员的每个目标文件 链接序列中的静态库。静态库是一种提供的方式 链接器是一堆目标文件,可以从中选择所需的文件。

假设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.ofoo.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 ... 位于您的某个库中,则链接:

main

无需在任何-ly -lz ...找到main的定义,也无需定义 notmain的链接将被链接。

案例与您的-Wl,--undefined=notmain相同。如果您想要链接,您可以执行以下操作之一: -

  1. notmain添加到您的关联选项中(将notmain替换为 对于C ++,错误的notmain名称。这将使链接器假设它具有 对EXTERN(notmain)的未定义引用,即使它没有看到任何内容。

  2. 将命令notmain添加到链接描述文件中(再次进行修改 对于C ++)。这相当于 1

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

  4. 3 实际上是您在发现:

    时所做的
      

    当我将非主要功能移出图书馆(进入项目)时,它会很好。

    对于 3 ,您不需要 来编译项目中的notmain.o和其他任何内容 需要/usr/local/lib的项目。您可以单独构建它,安装它 在/usr/local/lib/notmain.o中明确添加crt*.o 你的项目的联系。那将明确遵循GCC本身的例子 通过附加它们来链接普通程序的/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 启动文件 链接的绝对名称,例如

    <%
          while (rs.next()) {
    %>
            <%=StringUtils.defaultString(rs.getString("program"), "")%><br />
            <input type="hidden" class="program" name="program" value="<%=StringUtils.defaultString(rs.getString("program"), "")%>" />
    <%
          }
    %>