我的问题是:
我正在尝试编写嵌入式应用程序,该应用程序必须提供自己的链接器脚本(使用 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
的情况下,它会prog
a.o
、foo.o
、bar.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
相同.如果你想链接它,你可以做以下一件事:-
将
-Wl,--undefined=notmain
添加到链接选项(将notmain
替换为notmain
的残缺名称,为C++)。这将使链接器假定它具有 对notmain
的未定义引用,即使它没有看到任何引用。将命令
EXTERN(notmain)
添加到链接器脚本(再次重整 对于C++)。这相当于1。显式链接定义
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