CMake - Link 项目以正确的顺序包含 C 运行时对象文件

CMake - Link project with C runtime object files in correct order

有 C 运行时/启动文件:crt0.ocrti.ocrtbegin.ocrtend.ocrtn.o.

该项目是关于 运行 i686 裸机上的可执行文件,因此没有 OS。

问题出在最终可执行文件的 linking 中 - 项目目标文件与上述所有 C 运行时文件 linked 的顺序很重要。

我的构建系统是 CMake,我使用 target_sources 命令来定义可执行文件使用的源文件(编译然后 linked 在一起):

set( BARE_METAL app.bin )
add_executable( ${BARE_METAL} "" )

target_sources( ${BARE_METAL}
  PRIVATE
    boot.s  # contains symbols analogical to crt0.s
    crti.s
    crtn.s
    a.cpp
    b.cpp
    # … other files go here
)
target_link_options( ${BARE_METAL}
  PUBLIC
    -t linker.ld
    -nostdlib
    -nostartfiles
)
target_link_libraries( ${BARE_METAL}
  PUBLIC
    support
)

问题是 GCC 没有 link 使用 crtbegin.ocrtend.o,即使它可以在我的工具链中找到:

你能告诉我如何从 CMake 以正确的顺序调用带有所有必需参数的 linker 吗?我希望 link 命令类似于

./i686-elf-ld -t linker.ld -nostdlib -nostartfiles boot.o crti.o crtbegin.o a.o b.o -libsupport.a crtend.o crtn.o

请注意C运行时文件的顺序。我已经简化了所有文件的目录结构,但关键点应该在这里。

非常感谢任何愿意帮助我的人……马丁

更新:

我试图同时调查更多,等待任何类型的建议,有前途的方法似乎是使用 CMAKE_CXX_LINK_EXECUTABLE 为 linking 定义项目特定的构建规则。

我的项目使用了在 root/main project() 语句之前设置的工具链文件。在子项目中,我设置了 CMAKE_CXX_LINK_EXECUTABLE 并且它似乎对其他子项目没有影响。这就是为什么我称之为有前途的原因,尽管整个事情还没有奏效。

我的计划是从三个项目特定的 C 运行时文件 crt0.ocrti.ocrtn.o 中创建一个对象库,以将它们从 <OBJECTS> 中删除,使该项目依赖于此对象库,但在 link 构建规则中手动管理运行时文件。

你觉得这样能顺利结束吗?我会post有结果了...

感谢@KamilCuk,我已经成功做到了 运行。这是我做的解决方案(供任何试图解决相同问题的人使用)

project( Bare_Metal LANGUAGES CXX C ASM )

set( BARE_METAL app.bin )
set( BARE_METAL_CRTS app-crts )

add_library( ${BARE_METAL_CRTS} OBJECT
    boot.s  # contains symbols analogical to crt0.s
    crti.s
    crtn.s
)

add_executable( ${BARE_METAL} "" )

target_sources( ${BARE_METAL}
  PRIVATE
    a.cpp
    b.cpp
    # … other files go here
)
target_link_options( ${BARE_METAL}
  PUBLIC
    -t linker.ld
    -nostdlib
    -nostartfiles
)
target_link_libraries( ${BARE_METAL}
  PUBLIC
    support
)

# This ensures the boot.o crti.o, crtn.o to be build prior to BARE_METAL target
add_dependencies( ${BARE_METAL} ${BARE_METAL_CRTS} )

# Ask GCC to get the full path name of crtbegin. and crtend.o
execute_process( COMMAND ${CMAKE_CXX_COMPILER} -print-file-name=crtbegin.o OUTPUT_VARIABLE CRTBEGIN_O OUTPUT_STRIP_TRAILING_WHITESPACE )
execute_process( COMMAND ${CMAKE_CXX_COMPILER} -print-file-name=crtend.o OUTPUT_VARIABLE CRTEND_O OUTPUT_STRIP_TRAILING_WHITESPACE)

# Set linker executable to be used - navigate to the toolchain used (might be setup in toolchain file)
set( BARE_METAL_LINKER ${BARE_METAL_TOOLCHAIN_ROOT}/bin/${CMAKE_TARGET_PROCESSOR}-elf-ld )

set( CMAKE_CXX_LINK_EXECUTABLE "${BARE_METAL_LINKER} <CMAKE_C_LINK_FLAGS> <FLAGS> <LINK_FLAGS> boot.obj crti.obj ${REVOLTA_CRTBEGIN} <OBJECTS> -o <TARGET> <LINK_LIBRARIES> ${REVOLTA_CRTEND} crtn.obj" )

请记住上面的代码不是复制粘贴的解决方案,而是完整的,可以用作 guide/inspiration 来做这些事情:)