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

使用CMake ExternalProject集成GMP库时的链接错误排查与解决

问题描述

我正在基于GNU MP开展项目,使用CMake的ExternalProject组件管理该库,编写了CMake配置文件与测试代码后,执行cmake --fresh ..配置正常,但执行make时出现链接错误,错误日志显示libgmpxx.a中的函数找不到libgmp里的定义。怀疑是链接顺序问题,请问该问题的成因是什么?如何解决?我的gcc版本为12.3.0。

项目结构

proj
|  build
|- CMakeLists.txt
|- test.cpp

CMakeLists.txt

cmake_minimum_required(VERSION 3.25)

include(ExternalProject)
include(ProcessorCount)

project(proj)

# BELOW SOURCE IS MOSTLY FROM
# https://go.googlesource.com/gollvm/+/96cc8367c6d13a58fccf1f0c5e262d03df742451/CMakeLists.txt

processorcount(PROCESSOR_COUNT)

set(EXTINSTALLDIR "${CMAKE_CURRENT_BINARY_DIR}/external/install")
set(EXTLIBDIR "${EXTINSTALLDIR}/lib")
set(EXTINCLUDEDIR "${EXTINSTALLDIR}/include")
set(EXTCPPFLAGS "CFLAGS=-I${EXTINCLUDEDIR}")
if(NOT "${CMAKE_SYSROOT}" STREQUAL "")
    string(APPEND EXTCPPFLAGS " -fPIC -DPIC")
endif()
set(EXTLDFLAGS "LDFLAGS=-L${EXTLIBDIR}")
set(EXTCC "CC=${CMAKE_C_COMPILER}" "CXX=${CMAKE_CXX_COMPILER}")

find_package(Threads REQUIRED)

ExternalProject_Add(local_libgmp
    URL https://gmplib.org/download/gmp/gmp-6.3.0.tar.xz
    DOWNLOAD_DIR ${CMAKE_CURRENT_BINARY_DIR}/external-downloads
    BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/external/gmp-build
    SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/external/gmp
    CONFIGURE_COMMAND <SOURCE_DIR>/configure --enable-cxx --prefix=${EXTINSTALLDIR} ${EXTCPPFLAGS} ${EXTLDFLAGS} ${EXTCC}
    BUILD_COMMAND make install -j${PROCESSOR_COUNT}
    LOG_CONFIGURE 1
    LOG_BUILD 1
    LOG_INSTALL 1
    TEST_COMMAND make check -j${PROCESSOR_COUNT}
)
add_library(${PROJECT_NAME}::libgmp STATIC IMPORTED)
#ExternalProject_Get_Property(local_libgmp BINARY_DIR)
set_target_properties(${PROJECT_NAME}::libgmp PROPERTIES
    IMPORTED_LOCATION ${EXTLIBDIR}/libgmp.a
)
add_library(${PROJECT_NAME}::libgmpxx STATIC IMPORTED)
#ExternalProject_Get_Property(local_libgmp BINARY_DIR)
set_target_properties(${PROJECT_NAME}::libgmpxx PROPERTIES
    IMPORTED_LOCATION ${EXTLIBDIR}/libgmpxx.a
)
add_dependencies(${PROJECT_NAME}::libgmp local_libgmp)
add_dependencies(${PROJECT_NAME}::libgmpxx local_libgmp)

# END

add_executable(Test test.cpp)
target_include_directories(Test PRIVATE ${EXTINCLUDEDIR})
target_link_directories(Test PRIVATE ${EXTLIBDIR})
target_link_libraries(Test PRIVATE pthread ${PROJECT_NAME}::libgmp ${PROJECT_NAME}::libgmpxx)

test.cpp

#include <iostream>
#include <gmpxx.h>

int main() {
    std::cout << "Hello, world!" << std::endl;
    std::cout << 11_mpz + 12_mpz << std::endl;
}

链接错误日志

[  9%] Completed 'local_libgmp'
[ 81%] Built target local_libgmp
[ 90%] Building CXX object CMakeFiles/Test.dir/test.cpp.o
[100%] Linking CXX executable Test
/usr/bin/ld: external/install/lib/libgmpxx.a(osmpz.o): in function `operator<<(std::ostream&, __mpz_struct const*)':
osmpz.cc:(.text+0x58): undefined reference to `__gmpz_get_str'
/usr/bin/ld: external/install/lib/libgmpxx.a(osdoprnti.o): in function `__gmp_doprnt_integer_ostream(std::ostream&, doprnt_params_t*, char*)':
osdoprnti.cc:(.text+0x7c): undefined reference to `__gmp_doprnt_integer'
/usr/bin/ld: osdoprnti.cc:(.text+0x88): undefined reference to `__gmp_asprintf_final'
/usr/bin/ld: external/install/lib/libgmpxx.a(osfuns.o):(.data.rel.ro+0x8): undefined reference to `__gmp_asprintf_memory'
/usr/bin/ld: external/install/lib/libgmpxx.a(osfuns.o):(.data.rel.ro+0x10): undefined reference to `__gmp_asprintf_reps'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/Test.dir/build.make:99: Test] Error 1
make[1]: *** [CMakeFiles/Makefile2:111: CMakeFiles/Test.dir/all] Error 2
make: *** [Makefile:91: all] Error 2
问题成因

GNU链接器(ld)处理静态库时遵循顺序扫描规则:只有当遇到未定义符号时,才会从后续的库中查找对应定义。你的链接顺序是libgmp在前、libgmpxx在后,但libgmpxx本身依赖libgmp中的函数。当链接器处理完libgmp后再扫描libgmpxx,发现未定义符号时不会回头重新扫描libgmp,导致这些符号无法解析。

解决方法

方法1:手动调整链接顺序

将被依赖的库放在依赖库的后面,也就是先写libgmpxx,再写libgmp:

target_link_libraries(Test PRIVATE pthread ${PROJECT_NAME}::libgmpxx ${PROJECT_NAME}::libgmp)

方法2:让导入库自动处理依赖(更规范)

修改libgmpxx的导入库配置,添加对libgmp的依赖关系,CMake会自动处理正确的链接顺序:

add_library(${PROJECT_NAME}::libgmpxx STATIC IMPORTED)
set_target_properties(${PROJECT_NAME}::libgmpxx PROPERTIES
    IMPORTED_LOCATION ${EXTLIBDIR}/libgmpxx.a
    IMPORTED_LINK_INTERFACE_LIBRARIES ${PROJECT_NAME}::libgmp
)

之后链接时只需指定libgmpxx即可,CMake会自动将libgmp添加到正确位置:

target_link_libraries(Test PRIVATE pthread ${PROJECT_NAME}::libgmpxx)

额外优化:完善导入库接口

可以将头文件路径附加到导入库的属性中,无需给Test单独设置target_include_directories:

set_target_properties(${PROJECT_NAME}::libgmp PROPERTIES
    IMPORTED_LOCATION ${EXTLIBDIR}/libgmp.a
    INTERFACE_INCLUDE_DIRECTORIES ${EXTINCLUDEDIR}
)
set_target_properties(${PROJECT_NAME}::libgmpxx PROPERTIES
    IMPORTED_LOCATION ${EXTLIBDIR}/libgmpxx.a
    INTERFACE_INCLUDE_DIRECTORIES ${EXTINCLUDEDIR}
    IMPORTED_LINK_INTERFACE_LIBRARIES ${PROJECT_NAME}::libgmp
)

此时Test的配置可简化为:

add_executable(Test test.cpp)
target_link_libraries(Test PRIVATE pthread ${PROJECT_NAME}::libgmpxx)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 02:46:07