在 CMake 中构建期间添加链接器选项

问题描述 投票:0回答:1

是否可以向 CMake 生成的链接命令行添加其他选项作为某些构建操作的结果?

具体来说,我需要通过静态库保留一些未引用的符号,这意味着在链接过程中会出现类似

--undefined=symbol
的情况。我无法提前知道这些名称(至少,没有一些巨大的表,这正是我首先要避免的),但我可以根据它们的 ELF 部分和名称前缀来识别它们。通过
nm
和一些解析,我现在有了链接调用时需要提供的符号列表。我唯一剩下的问题是如何将它们从 PRE_LINK 自定义命令转换为实际的 GCC 链接咒语。

一般来说,是否可以让 CMake 根据另一个命令的构建时输出来调整一个命令的调用?

其他信息

我尝试过的一些非 CMake 的东西:

  • --whole-archive
    :这可行,但我不想使用它,因为这是一个嵌入式项目,我非常希望符号排除所有不在命名部分中的其他内容。
  • 重新打包对象文件,以便所有特殊符号都在一个对象中,可以通过
    --whole-archive
    包含该对象:虽然我觉得应该这样做,但我无法使其工作。最终,我只需要符号本身的值(这些是指向驱动程序初始化结构的指针),但是
    objcopy
    还需要移动
    .data
    部分,我认为这会破坏链接。
  • --retain-symbols-file
    :如果这没有删除所有其他符号,它就会完全按照我想要的方式进行。
  • 全局匹配:现代 LLVM 支持未定义符号的全局匹配,这可以工作,因为所有相关符号都具有相同的前缀。不幸的是,SoC 供应商仍在使用一些旧的 GCC 8.4.0 版本,该版本不支持此或其他现代便利选项。
cmake linker static-libraries symbols elf
1个回答
0
投票

不幸的是,我无法按照要求进行此操作。据我所知,CMake 在实际构建运行时根本不支持变量修改。 如果有人更了解请指正。

也就是说,我能够找到合理的解决方法。将其发布在这里,以防将来对某人有所帮助。

简而言之,只要您想要执行的任何操作都可以在外部文件中指定信息,您就可以解决此问题。然后,可以在配置时声明此外部文件,因为其名称已知,但直到链接时才会考虑其内容。最后,注入自定义命令以在适当的时间生成文件。

实际上,这在

CMakeLists.txt
中采用以下形式:

# Tell CMake about our additional linker script which will be generated in add_custom_command() below
target_link_options(${COMPONENT_TARGET} PUBLIC -T${CMAKE_CURRENT_BINARY_DIR}/driverinit.ld)

# Before linking, extract names of all .driver.init symbols so they can be fed to the linker for preservation when included into final ELF.
add_custom_command(TARGET ${COMPONENT_LIB}
    COMMENT "Generating driverinit.ld..."
    VERBATIM
    PRE_LINK
    COMMAND find . -name "*.c.obj" -exec ${CMAKE_NM} {} \;
                                       | grep " __driverinit_[a-zA-Z0-9_]*$"
                                       | cut "-d " -f3
                                       | tr "\\n" " "
                                       | xargs -I@ echo "EXTERN("@")" > driverinit.ld
)

这将在最终链接之前找到我当前二进制文件的所有目标文件,提取与

__driverinit_.*
匹配的所有符号,将它们清理为仅符号名称并将它们输出到由
EXTERN()
包围的链接器脚本。该链接器脚本的名称是固定的,可以提前输入到
target_link_options()

请注意,就我而言,我的项目是使用 ESP-IDF 构建环境的 ESP32。为了保留最终输出二进制文件中的适当部分,我还必须包含以下内容(如果您控制构建系统,则不需要)。

CMakeLists.txt:

idf_component_register(
    ...
    LDFRAGMENTS "${CMAKE_SOURCE_DIR}/drivers.lf")

drivers.lf:

# Declare a new fragment section, which will expand to all .driver.init* sections defined in component's C files 
[sections:driver_init]
entries:
    .driver.init+

# Define a scheme, named driver_default, to specify which physical regions the given sections should be placed in
[scheme:driver_default]
entries:
    driver_init -> iram0_text

# Finally, specify which parts of what components will use newly created scheme
[mapping:driver_default]
archive: *                                                          # Match all archives
entries:
    # All objests in archive will use "driver_default" scheme
    * (driver_default);
            driver_init -> iram0_text SURROUND(driver_init) KEEP()  # Add flags for the "driver_init -> iram0_text" entry in the "driver_default" scheme

© www.soinside.com 2019 - 2024. All rights reserved.