单片机工程使用链接优化-flto找不到定义_链接静态库

**IDE:**CLion

HOST: Windows 11

**MinGW:**x86_64-14.2.0-release-posix-seh-ucrt-rt_v12-rev0

**GCC:**arm-gnu-toolchain-13.3.rel1-mingw-w64-i686-arm-none-eabi

示例工程https://github.com/ichliebedich-DaCapo/STM32F407VET6

一、链接过程

在单片机开发中,我希望尽可能开发"低耦合高内聚"的代码,我理想中的工程依赖关系如下图:

(后续还考虑加入FreeRTOS、DSP、FATS什么的)

基于上面目的,于是把工程分成各个模块,采用独立编译成静态库,最后再链接的方法。

对于HAL库的静态编译可以使用file() 先收集需要文件到变量DRIVERS_SRC 中,然后使用set() 设置需要包含的头文件目录DRIVERS_DIR ,之后就可以使用add_library() 添加要编译生成的库的名称和所需的资源文件,STATIC是属性,表示要编译的是静态库

再使用**target_include_directories()**添加库所需的头文件目录

最后可以使用属性ARCHIVE_OUTPUT_DIRECTORY 来专门指定静态库的生成目录

cpp 复制代码
# 官方驱动库
file(GLOB_RECURSE DRIVERS_SRC "Drivers/STM32F4xx_HAL_Driver/Src/*.c")
set(DRIVERS_DIR
        Drivers
        Drivers/STM32F4xx_HAL_Driver/Inc
        Drivers/STM32F4xx_HAL_Driver/Inc/Legacy
        Drivers/CMSIS/Device/ST/STM32F4xx/Include
        Drivers/CMSIS/Include
)
add_library(libdrivers STATIC ${DRIVERS_SRC})
target_include_directories(libdrivers PUBLIC ${DRIVERS_DIR})
# 设置静态库的输出目录
set_target_properties(libdrivers PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${LIB_DIR})

库的名称也是有规范的,一般都是"lib+xxx"组成的,比如这次,编译的库用于驱动,所以起名为lib+drivers -> libdrivers。当然由于这是基于STM32提供的HAL,所以也可以起名为libhal
依此方法,编译BSP时,由于BSP依赖HAL库,所以需要链接HAL库(即libdrivers)。即下方中的target_link_libraries() 函数,使用这种方法自然也可以链接一些第三方库,比如SDL

cpp 复制代码
target_link_libraries(libbsp PUBLIC libdrivers)
cpp 复制代码
# ------BSP库------
file(GLOB_RECURSE BSP_SRC "BSP/src/*.c")
set(BSP_DIR
        BSP/inc
)
add_library(libbsp STATIC ${BSP_SRC})
target_include_directories(libbsp PUBLIC ${BSP_DIR})
target_link_libraries(libbsp PUBLIC libdrivers)
# 设置静态库的输出目录
set_target_properties(libbsp PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${LIB_DIR})

根据上面的方法可以链接成不同的静态库,再使用**target_link_libraries()**把它们全部链接为可执行文件

cpp 复制代码
# 设置链接目标
add_executable(${PROJECT_NAME}.elf ${SOURCES} ${LINKER_SCRIPT})
target_link_libraries(${PROJECT_NAME}.elf PRIVATE libapp libdsp libdata libgui libbsp libmodule)
# 设置可执行文件的输出目录
set_target_properties(${PROJECT_NAME}.elf PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${BIN_DIR})

二、链接问题

1,链接缺失系统调用函数

只不过不是所有文件都能采用上面链接方法的,比如系统调用、启动文件等一同链接成一个库,此时会显示缺少各种定义,而这些缺失的定义却显然是系统调用里的函数

这个我也不晓得是什么原因,可能是单独链接这些系统调用文件成静态库时,还需要添加编译器所带的库如libgcc等。

总之把这些底层文件单独收集起来

cpp 复制代码
file(GLOB_RECURSE SOURCES "Core/*.*" "Application/Base/ISR.cpp")

然后添加到可执行目标中,其他地方不变

cpp 复制代码
add_executable(${PROJECT_NAME}.elf ${SOURCES} ${LINKER_SCRIPT})

最后再链接时就不会出现问题了

2,使用链接优化出现缺失定义的错误

如果像前面把各种库独立编译,那么在开启链接优化时,会出先下面的报错,总之就是各种找不到定义,连main都找不到

这个其实很好理解,使用-flto链接优化时,会把所有资源文件编译后再链接优化一下。那么问题就出现在了编译这块,因为LTO 无法跨静态库边界进行优化,可能导致一些符号无法正确解析。

所以需要把所有资源文件统一添加到可执行目标时,LTO 优化的范围涵盖了所有源文件,这样才能正确地处理跨文件的优化和符号解析。

当然这也与编译器和连接器有关,不同的编译器和链接器在处理 LTO 时可能有不同的行为。某些编译器在处理多个静态库时可能不够智能,无法正确处理跨库的优化。

虽然理论上可以使用-flto=full选项,但有的编译器没有这个选项

当然,还有一种解决办法,就是使用ar工具手动把各种静态库合并,比如下面把lib1.a、lib2.a、lib3.a都合并为liball.a 。

cpp 复制代码
# 合并静态库
add_custom_command(
    OUTPUT liball.a
    COMMAND ${CMAKE_AR} crs liball.a $<TARGET_FILE:lib1> $<TARGET_FILE:lib2> $<TARGET_FILE:lib3>
    DEPENDS lib1 lib2 lib3
)

对于此处用到的编译器,上面两种方法都不如直接添加文件到可执行目标中快捷省事

cpp 复制代码
    include_directories(Application/Conf ${APP_DIR}
            ${DRIVERS_DIR}
            ${LVGL_DIR}
            #            ${FREERTOS_DIR}
            ${BSP_DIR}
            ${DATA_DIR}
            ${GUI_DIR}
            ${MODULE_DIR}
            ${APPLICATION_DIR}
    )
    file(GLOB_RECURSE SOURCES "Core/*.*" "Application/Base/ISR.cpp"
            ${DRIVERS_SRC}
            ${LVGL_SRC}
            #            ${FREERTOS_SRC}
            ${BSP_SRC}
            ${DATA_SRC}
            ${GUI_SRC}
            ${MODULE_SRC}
            ${APPLICATION_SRC}
    )
相关推荐
Lay_鑫辰1 小时前
禾川HCQ1系列PAC脉冲控制步进驱动器
运维·人工智能·单片机·嵌入式硬件·自动化
一只电子牛蛙4 小时前
【单片机】IIC需要注意什么(企业级回答)
单片机·嵌入式硬件
沐欣工作室_lvyiyi4 小时前
基于单片机的无线水塔监控系统设计(论文+源码)
人工智能·stm32·单片机·嵌入式硬件·单片机毕业设计
上海文顺负载箱5 小时前
怎样衡量电阻负载的好坏
单片机·嵌入式硬件
云山工作室8 小时前
基于单片机的智能照明控制系统(论文+源码
stm32·单片机·嵌入式硬件·毕业设计·毕设
前面的题目以后再来探索吧9 小时前
ota-总结--wifi
stm32·单片机
奇偶变不变9 小时前
RTOS之事件集
java·linux·jvm·单片机·算法
嗯? 嗯。14 小时前
单片机的诗意
单片机·嵌入式硬件
电子工程师UP学堂15 小时前
电子应用设计方案-65:智能餐桌系统方案设计
网络·人工智能·单片机·嵌入式硬件·计算机外设