netCDF与HDF5库冲突引发段错误的成因及解决方法问询
netCDF与HDF5库混用段错误排查
问题说明
经过数小时的程序调试,确认netCDF与HDF5库之间存在冲突(程序需要同时读写两种格式的文件),已找到部分临时解决方案,详见本文末尾。
最小复现代码
如下精简代码运行后会直接触发段错误,甚至不需要调用包含HDF5对象定义的函数:
#include <iostream> #include <string> #include "H5Cpp.h" #include <netcdf> using namespace std; void stupidfunction() // 注意:该函数从未被调用 { H5::Group grp1; // 仅该对象的潜在定义就会导致netCDF出现段错误! } int main(int argn, char ** args) { std::string outputFilename = "/tmp/test.nc"; try { std::cout << "Now opening " << outputFilename << std::endl; netCDF::NcFile sfc; sfc.open(outputFilename, netCDF::NcFile::replace); std::cout << "closing file" << std::endl; sfc.close(); return true; } catch(netCDF::exceptions::NcException& e) { std::cout << "EX: " << e.what() << std::endl; return false; } return 0; }
编译命令
h5c++ test.cpp -std=gnu++11 -O0 -g3 -lnetcdf_c++4 -lnetcdf -o test
相关依赖版本
libnetcdf-c++4-1 4.3.1-2build1 amd64 C++ interface for scientific data access to large binary data libnetcdf-c++4-dev 4.3.1-2build1 amd64 creation, access, and sharing of scientific data in C++ libnetcdf-dev 1:4.7.3-1 amd64 creation, access, and sharing of scientific data libnetcdf15:amd64 1:4.7.3-1 amd64 Interface for scientific data access to large binary data netcdf-bin 1:4.7.3-1 amd64 Programs for reading and writing NetCDF files netcdf-doc 1:4.7.3-1 all Documentation for NetCDF hdf5-helpers 1.10.4+repack-11ubuntu1 amd64 Hierarchical Data Format 5 (HDF5) - Helper tools hdf5-tools 1.10.4+repack-11ubuntu1 amd64 Hierarchical Data Format 5 (HDF5) - Runtime tools libhdf4-0 4.2.14-1ubuntu1 amd64 Hierarchical Data Format library (embedded NetCDF) libhdf5-103:amd64 1.10.4+repack-11ubuntu1 amd64 Hierarchical Data Format 5 (HDF5) - runtime files - serial version libhdf5-cpp-103:amd64 1.10.4+repack-11ubuntu1 amd64 Hierarchical Data Format 5 (HDF5) - C++ libraries libhdf5-dev 1.10.4+repack-11ubuntu1 amd64 Hierarchical Data Format 5 (HDF5) - development files - serial versio
错误信息
GDB运行报错
Program received signal SIGSEGV, Segmentation fault. 0x00007ffff7a47a01 in __vfprintf_internal (s=s@entry=0x7fffff7ff480, format=format@entry=0x7ffff77e13a8 "can't locate ID", ap=ap@entry=0x7fffff7ff5e0, mode_flags=mode_flags@entry=2) at vfprintf-internal.c:1289 1289 vfprintf-internal.c: No such file or directory.
GDB栈回溯信息
(gdb) bt #0 0x00007ffff7a47a01 in __vfprintf_internal (s=s@entry=0x7fffff7ff480, format=format@entry=0x7ffff77e13a8 "can't locate ID", ap=ap@entry=0x7fffff7ff5e0, mode_flags=mode_flags@entry=2) at vfprintf-internal.c:1289 #1 0x00007ffff7a5cd4a in __vasprintf_internal (result_ptr=0x7fffff7ff5d8, format=0x7ffff77e13a8 "can't locate ID", args=0x7fffff7ff5e0, mode_flags=2) at vasprintf.c:57 #2 0x00007ffff75c7e56 in H5E_printf_stack () from /usr/lib/x86_64-linux-gnu/libhdf5_serial.so.103 #3 0x00007ffff76553b9 in H5I_inc_ref () from /usr/lib/x86_64-linux-gnu/libhdf5_serial.so.103 ... many many lines repeating H5E_printf_stack, H5E__push_stack and H5I_inc_ref ... #56139 0x00007ffff76553b9 in H5I_inc_ref () from /usr/lib/x86_64-linux-gnu/libhdf5_serial.so.103 #56140 0x00007ffff75c7c2f in H5E__push_stack () from /usr/lib/x86_64-linux-gnu/libhdf5_serial.so.103 #56141 0x00007ffff75c7e7e in H5E_printf_stack () from /usr/lib/x86_64-linux-gnu/libhdf5_serial.so.103 #56142 0x00007ffff761fc85 in H5G_loc () from /usr/lib/x86_64-linux-gnu/libhdf5_serial.so.103 #56143 0x00007ffff7546903 in H5Acreate1 () from /usr/lib/x86_64-linux-gnu/libhdf5_serial.so.103 #56144 0x00007ffff790b11b in NC4_write_provenance () from /usr/lib/x86_64-linux-gnu/libnetcdf.so.15 #56145 0x00007ffff790b5a8 in ?? () from /usr/lib/x86_64-linux-gnu/libnetcdf.so.15 #56146 0x00007ffff790b7b0 in nc4_close_hdf5_file () from /usr/lib/x86_64-linux-gnu/libnetcdf.so.15 #56147 0x00007ffff790b9ea in NC4_close () from /usr/lib/x86_64-linux-gnu/libnetcdf.so.15 #56148 0x00007ffff78ca579 in nc_close () from /usr/lib/x86_64-linux-gnu/libnetcdf.so.15 #56149 0x00007ffff7f82270 in netCDF::NcFile::close() () from /usr/lib/x86_64-linux-gnu/libnetcdf_c++4.so.1 #56150 0x00005555555a7959 in main (argn=1, args=0x7fffffffe5b8) at test.cpp:29 (gdb)
问题根因
该错误是典型的库符号冲突导致的:
- netCDF4格式的底层本身依赖HDF5实现读写,你手动链接的独立HDF5库和netCDF依赖的HDF5库存在大量重复符号
- HDF5的C++接口会注册全局的构造/析构逻辑,即使你没有显式调用相关代码,这些全局逻辑也会污染netCDF内部调用的HDF5上下文,导致HDF5内部ID查找出现死循环,最终栈溢出触发段错误
- 指定classic格式后,netCDF不会调用底层HDF5相关逻辑,所以不会触发冲突
解决方案
临时规避方案
将netCDF文件格式指定为classic或classic64即可避免错误,修改示例如下:
sfc.open(outputFilename, netCDF::NcFile::replace, netCDF::NcFile::classic);
或者
sfc.open(outputFilename, netCDF::NcFile::replace, netCDF::NcFile::classic64);
永久修复方案
- 调整链接顺序,把netCDF相关库放在HDF5库前面,让链接器优先使用netCDF依赖的HDF5符号,编译命令修改为:
h5c++ test.cpp -std=gnu++11 -O0 -g3 -lnetcdf_c++4 -lnetcdf -lhdf5_cpp -lhdf5 -o test - 编译netCDF的时候使用和项目完全一致的HDF5版本,保证两者的HDF5依赖完全对齐
- 如果不需要在同一个编译单元同时调用两种库的接口,可以把HDF5相关代码和netCDF相关代码拆分到不同的动态库中,避免符号直接冲突
内容的提问来源于stack exchange,提问作者Knut Stanley Jacobsen
相关产品推荐
相关产品推荐

