当前位置: 首页 > 工具软件 > GnuTLS > 使用案例 >

gnuTLS链接错误:undefined reference to ‘mpz_XXXX’

江同化
2023-12-01

在工作过程中,编译并使用libgnutls.a静态库时出现如下报错:

./lib/libs/libgnutls.a(provable-prime.o): In function `st_provale_prime_small`:
/root/gnutls-3.6.4/lib/nettle/int/provable-prime.c:1026: undefined reference to `mpz_init`
……

问题现象为:没有 mpz_init()接口的定义,即链接时没有找到这个符号的定义。

1、查看libgnutls.a静态库的符号表,确认请求的符号mpz_init格式

$ nm -A ./libgnutls.a | grep mpz_init
/usr/lib/x86_64-linux-gnu/libgnutls.a:dsa-keygen-fips186.o: U mpz_init
/usr/lib/x86_64-linux-gnu/libgnutls.a:dsa-validate.o: U mpz_init
/usr/lib/x86_64-linux-gnu/libgnutls.a:lt18-mpi.o: U mpz_init
/usr/lib/x86_64-linux-gnu/libgnutls.a:lt19-pk.o: U mpz_init
/usr/lib/x86_64-linux-gnu/libgnutls.a:provable-prime.o: U mpz_init
/usr/lib/x86_64-linux-gnu/libgnutls.a:rsa-keygen-fips186.o: U mpz_init

2、查看libgmp.a静态库的符号表,确认是否包含符号mpz_init

$ nm -A /usr/lib/x86_64-linux-gnu/libgmp.a | grep mpz_init
/usr/lib/x86_64-linux-gnu/libgmp.a:bin_ui.o: U __gmpz_init
/usr/lib/x86_64-linux-gnu/libgmp.a:bin_ui.o: U __gmpz_init_set_ui
/usr/lib/x86_64-linux-gnu/libgmp.a:mfac_uiui.o: U __gmpz_init
/usr/lib/x86_64-linux-gnu/libgmp.a:lt22-init.o:0000000000000000 T __gmpz_init
/usr/lib/x86_64-linux-gnu/libgmp.a:lt23-init2.o:0000000000000000 T __gmpz_init2
/usr/lib/x86_64-linux-gnu/libgmp.a:lt24-inits.o:0000000000000000 T __gmpz_inits
/usr/lib/x86_64-linux-gnu/libgmp.a:lt26-iset.o:0000000000000000 T __gmpz_init_set
/usr/lib/x86_64-linux-gnu/libgmp.a:lt27-iset_d.o:0000000000000000 T __gmpz_init_set_d
/usr/lib/x86_64-linux-gnu/libgmp.a:lt28-iset_si.o:0000000000000000 T __gmpz_init_set_si
/usr/lib/x86_64-linux-gnu/libgmp.a:lt29-iset_str.o:0000000000000000 T __gmpz_init_set_str
/usr/lib/x86_64-linux-gnu/libgmp.a:lt30-iset_ui.o:0000000000000000 T __gmpz_init_set_ui
/usr/lib/x86_64-linux-gnu/libgmp.a:remove.o: U __gmpz_init
/usr/lib/x86_64-linux-gnu/libgmp.a:remove.o: U __gmpz_init_set
/usr/lib/x86_64-linux-gnu/libgmp.a:randlc2s.o: U __gmpz_init_set_str
/usr/lib/x86_64-linux-gnu/libgmp.a:randlc2x.o: U __gmpz_init
/usr/lib/x86_64-linux-gnu/libgmp.a:randlc2x.o: U __gmpz_init2
/usr/lib/x86_64-linux-gnu/libgmp.a:randlc2x.o: U __gmpz_init_set
/usr/lib/x86_64-linux-gnu/libgmp.a:randmts.o: U __gmpz_init2
/usr/lib/x86_64-linux-gnu/libgmp.a:randmts.o: U __gmpz_init_set

注意这一行:
/usr/lib/x86_64-linux-gnu/libgmp.a:lt22-init.o:0000000000000000 T __gmpz_init
根据上面的内容,可以确定libgmp.a中,符号格式为__gmpz_init

问题原因初步可以确定

请求的符号格式与 静态库提供的符号内容不一致,在链接的时候没有找到函数定义,从而编译失败了。

3、查看报错位置的原文件内容

源文件/lib/nettle/int/provable-prime.c包含头文件内容如下:

#if HAVE_CONFIG_H
#include <config.h>
#endif

#include <assert.h>
#include <stdlib.h>
#include <nettle/memxor.h>
#include <nettle/bignum.h>    //注意这一行,该文件包含头文件 gmp.h
#include <dsa-fips.h>

#include <nettle/macros.h>

继续查看头文件 <nettle/bignum.h>: /usr/include/nettle/bignum.h

#ifndef NETTLE_BIGNUM_H_INCLUDED
#define NETTLE_BIGNUM_H_INCLUDED

#include "nettle-meta.h"

#include "nettle-types.h"

/* For NETTLE_USE_MINI_GMP */
#include "version.h"

#if NETTLE_USE_MINI_GMP
# include "mini-gmp.h"                   // 注意该行

# define GMP_NUMB_MASK (~(mp_limb_t) 0)

/* Function missing in older gmp versions, and checked for with ifdef */
# define mpz_limbs_read mpz_limbs_read
/* Side-channel silent powm not available in mini-gmp. */
# define mpz_powm_sec mpz_powm
#else
# include <gmp.h>                      // 注意该行
#endif

头文件中通过宏开关NETTLE_USE_MINI_GMP来指明包含哪个头文件。

4、分别查看 mini-gmp.hgmp.h 文件中 函数的声明

mini-gmp.h 中内容摘取如下:

……
void mpz_init (mpz_t);
void mpz_init2 (mpz_t, mp_bitcnt_t);
……

gmp.h 中内容摘取如下:

……
#define mpz_init __gmpz_init
__GMP_DECLSPEC void mpz_init (mpz_ptr) __GMP_NOTHROW;

#define mpz_init2 __gmpz_init2
__GMP_DECLSPEC void mpz_init2 (mpz_ptr, mp_bitcnt_t);
……

当调用 gmp.h头文件,在预编译阶段会将所用的 mpz_init字段替换为 __gmpz_init,结合第2步骤中查看的 libgmp.a静态库符号表,可以确定 编译 libgmp时包含的头文件时gmp.h

根据上面内容进而可以判断出

在编译libgnutls.a静态库时调用的头文件是mini-gmp.h

证明该结论可以在头文件/usr/include/nettle/bignum.h添加error打印来确定:

……
/* For NETTLE_USE_MINI_GMP */
#include "version.h"

#if NETTLE_USE_MINI_GMP
# error "use mini-gmp.h"    //添加的error打印
# include "mini-gmp.h"

# define GMP_NUMB_MASK (~(mp_limb_t) 0)

/* Function missing in older gmp versions, and checked for with ifdef */
# define mpz_limbs_read mpz_limbs_read
/* Side-channel silent powm not available in mini-gmp. */
# define mpz_powm_sec mpz_powm
#else
# error "gmp.h"    //添加的error打印
# include <gmp.h>
#endif
……

重新编译libgnutls.a,根据编译错误打印可以证明前面所述的结论成立。

5、解决编译问题

临时解决方法:在头文件/usr/include/nettle/bignum.h中,取消宏定义开关NETTLE_USE_MINI_GMP,如下:

……

#undef NETTLE_USE_MINI_GMP     // 取消宏定义开关

#if NETTLE_USE_MINI_GMP
# include "mini-gmp.h"

# define GMP_NUMB_MASK (~(mp_limb_t) 0)

/* Function missing in older gmp versions, and checked for with ifdef */
# define mpz_limbs_read mpz_limbs_read
/* Side-channel silent powm not available in mini-gmp. */
# define mpz_powm_sec mpz_powm
#else
# include <gmp.h>     // 此时会包含该头文件
#endif

再次编译 libgnutls.a静态库,编译好后查看静态库中需要的符号格式内容如下:

$ nm -A /usr/lib/x86_64-linux-gnu/libgnutls.a | grep mpz_init
/usr/lib/x86_64-linux-gnu/libgnutls.a:dsa-keygen-fips186.o: U __gmpz_init
/usr/lib/x86_64-linux-gnu/libgnutls.a:dsa-validate.o: U __gmpz_init
/usr/lib/x86_64-linux-gnu/libgnutls.a:lt18-mpi.o: U __gmpz_init
/usr/lib/x86_64-linux-gnu/libgnutls.a:lt19-pk.o: U __gmpz_init
/usr/lib/x86_64-linux-gnu/libgnutls.a:provable-prime.o: U __gmpz_init
/usr/lib/x86_64-linux-gnu/libgnutls.a:rsa-keygen-fips186.o: U __gmpz_init

继续项目编译,问题解决

正规解决方法: 在编译 libnettle.a 静态库,在nettle项目中执行 命令:

./configure

时,添加选项参数 --enable-mini-gmp=no, 重新编译 libgmp.a,并重新安装 libgmp*(没有测试确认)*

 类似资料: