You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CMake无法链接C编写的共享库问题求助

问题:ROS CMake链接C语言库时提示未定义引用

我有一个C语言编写的项目,通过GCC 9.4.0编译为静态/动态库,使用Makefile可正常编译链接main.c生成可执行文件,执行nm -D mylib.so能看到所有所需符号与函数。但在ROS中通过CMake调用该库时,链接失败:目标节点my_node的main.cpp调用void* CreateEnvironment();等函数时,均提示未定义引用。

方案1:使用预编译库

对应的CMakeLists.txt:

cmake_minimum_required(VERSION 3.0.2)

find_library(
    MY_LIB
    NAME    mylib
    PATHS   "${PROJECT_SOURCE_DIR}/lib"
    NO_DEFAULT_PATH # 加不加无区别
)

add_library(mylib SHARED IMPORTED)
set_target_properties(mylib PROPERTIES IMPORTED_LOCATION "${MY_LIB}")

add_executable(my_node src/main.cpp)
target_link_libraries(my_node mylib ${catkin_LIBRARIES})

报错信息:

/usr/bin/c++ -rdynamic CMakeFiles/…/main.cpp.o -o /home/…/clips_node  -Wl,-rpath,/home/…/lib:/opt/ros/noetic/lib /home/…/lib/libmylib.so […]
/usr/bin/ld: CMakeFiles/…/main.cpp.o: in function `foo()':
main.cpp:(.text+0x11): undefined reference to `CreateEnvironment()'

方案2:通过CMake重新编译库

猜测是编译标准或符号导出问题,尝试用CMake重新编译并强制C++链接,生成的SO文件用nm -D能看到所有符号,但链接可执行文件时依旧报错。对应的CMakeLists.txt:

cmake_minimum_required(VERSION 3.0.2)

## Build library
SET(MYLIB_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/include/mylib")
AUX_SOURCE_DIRECTORY(${PROJECT_SOURCE_DIR}/src/mylib MYLIB_SOURCES)
add_library(mylib ${MYLIB_SOURCES})
target_include_directories(mylib PRIVATE ${MYLIB_INCLUDE_DIR})
set_target_properties(mylib PROPERTIES
                            LINKER_LANGUAGE "CXX"
                            ENABLE_EXPORTS 1
)

## Build node
add_executable(my_node src/main.cpp)
target_include_directories(my_node PUBLIC ${MYLIB_INCLUDE_DIR})
target_link_libraries(my_node mylib ${catkin_LIBRARIES})

报错内容与方案1几乎一致,仅库路径不同。

已尝试的其他方法

  • 调整target_link_libraries中元素的顺序
  • 将PRIVATE改为PUBLIC
  • 在target_link_libraries中添加-lmylib
  • 在target_link_libraries中添加libmylib.so的完整路径
  • 替换共享库为静态库

目前共享库已传递给链接器,但链接器拒绝链接。用Makefile时可通过调整库顺序解决,但CMake自动处理下仍未找到问题所在。


解决方案:处理C/C++名称修饰差异

问题根源是C语言库与C++代码的名称修饰(Name Mangling)不匹配:C编译器会对函数名进行修饰以支持重载,而C语言库的符号是原始C风格命名,导致C代码编译后找不到对应符号。

针对方案1(预编译库)的修复

  1. 在main.cpp中引用C语言库头文件时,用extern "C"包裹,告诉C++编译器按C风格解析函数名:
extern "C" {
#include "mylib.h" // 替换为你的库头文件名
}
  1. (可选,更规范)在CMake中补充库的语言属性设置:
add_library(mylib SHARED IMPORTED)
set_target_properties(mylib PROPERTIES
    IMPORTED_LOCATION "${MY_LIB}"
    IMPORTED_LINK_INTERFACE_LANGUAGES "C" # 明确库为C语言编译
)

针对方案2(CMake重新编译库)的修复

  1. 修改库的头文件(如mylib.h),添加条件编译以兼容C/C++:
#ifndef MYLIB_H
#define MYLIB_H

#ifdef __cplusplus
extern "C" {
#endif

// 声明你的函数
void* CreateEnvironment();
// 其他函数声明...

#ifdef __cplusplus
}
#endif

#endif
  1. 修改CMakeLists.txt,去掉强制C++链接的设置,让CMake自动按C语言编译库:
add_library(mylib ${MYLIB_SOURCES})
target_include_directories(mylib PRIVATE ${MYLIB_INCLUDE_DIR})
# 移除LINKER_LANGUAGE设置,CMake会根据源文件后缀自动判断语言
# 可选:指定C标准版本
set_target_properties(mylib PROPERTIES
    C_STANDARD 99 # 根据库实际使用的C标准调整
)

验证方法

编译后执行nm -C main.cpp.o查看C++目标文件中的符号,应能看到原始的CreateEnvironment(),而非被修饰后的名字(如_Z16CreateEnvironmentv),同时对比nm -D mylib.so中的符号,确认名称一致。

内容的提问来源于stack exchange,提问作者Kyordhel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 02:10:20