是否可以向 CMake 生成的链接命令行添加其他选项作为某些构建操作的结果?
具体来说,我需要通过静态库保留一些未引用的符号,这意味着在链接过程中会出现类似
--undefined=symbol
的情况。我无法提前知道这些名称(至少,没有一些巨大的表,这正是我首先要避免的),但我可以根据它们的 ELF 部分和名称前缀来识别它们。通过 nm
和一些解析,我现在有了链接调用时需要提供的符号列表。我唯一剩下的问题是如何将它们从 PRE_LINK 自定义命令转换为实际的 GCC 链接咒语。
一般来说,是否可以让 CMake 根据另一个命令的构建时输出来调整一个命令的调用?
其他信息
我尝试过的一些非 CMake 的东西:
--whole-archive
:这可行,但我不想使用它,因为这是一个嵌入式项目,我非常希望符号排除所有不在命名部分中的其他内容。--whole-archive
包含该对象:虽然我觉得应该这样做,但我无法使其工作。最终,我只需要符号本身的值(这些是指向驱动程序初始化结构的指针),但是 objcopy
还需要移动 .data
部分,我认为这会破坏链接。--retain-symbols-file
:如果这没有删除所有其他符号,它就会完全按照我想要的方式进行。不幸的是,我无法按照要求进行此操作。据我所知,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