我的问题如下:
我正在尝试编写嵌入式应用程序,它必须提供自己的链接器脚本(使用 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.o
和bar.o
。一个联动:
g++ -o prog a.o foo.o bar.o # A
是不一样的联动:
g++ -o prog a.o -lfoobar. # B
链接器无条件地使用链接序列中的每个目标文件,因此在情况下A
,它链接a.o
, foo.o
, bar.o
in prog
。
链接器不会无条件地使用作为链接序列中静态库成员的每个目标文件。静态库是一种向链接器提供一堆目标文件的方法,从中可以选择需要的文件。
假设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 ...
不需要main
在 中的任何一个处找到 的-ly -lz ...
定义,main
也不会链接 的定义。
情况与您的notmain
. 如果您希望将其链接,您可以执行以下操作之一:-
添加-Wl,--undefined=notmain
到您的链接选项(替换为 C++notmain
的损坏名称notmain
)。这将使链接器假定它有一个未定义的引用,notmain
即使它没有看到任何引用。
将命令添加EXTERN(notmain)
到您的链接器脚本中(再次使用 C++ 的 mangling)。这相当于1。
显式链接定义了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
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句