Ubuntu Hyper-V虚拟机编译C++代码时Makefile链接错误求助
问题场景
在Ubuntu Hyper-V虚拟机中编译C++项目,Makefile编译阶段执行正常,但链接时出现Relocation错误,同项目可在其他同事的Ubuntu机器正常编译。
错误信息
/usr/bin/ld: netcdf/libs/libnetcdf_c++.a(netcdf.o): relocation R_X86_64_32S against symbol `_ZTV16NcTypedComponent' can not be used when making a PIE object; recompile with -fPIE
/usr/bin/ld: failed to set dynamic section sizes: bad value
collect2: error: ld returned 1 exit status
make: *** [Makefile:158: dos-tem] Error 1
项目Makefile内容
# Basic dos-tem Makefile CC=g++ CFLAGS= -c -Wall -ansi -O0 -g -fPIC LIBS=-lnetcdf_c++ -lnetcdf LIBDIR=-Lnetcdf/libs INCLUDES=-Inetcdf/includes SOURCES= src/TEM.o \ src/atmosphere/AtmosUtil.o \ src/atmosphere/Atmosphere.o \ src/data/BgcData.o \ src/data/CohortData.o \ src/data/EnvData.o \ src/data/FirData.o \ src/data/GridData.o \ src/data/RegionData.o \ src/data/RestartData.o \ src/disturb/WildFire.o \ src/ground/Bucket.o \ src/ground/DoubleLinkedList.o \ src/ground/Ground.o \ src/ground/Mineral.o \ src/ground/Moss.o \ src/ground/Peat.o \ src/ground/Richard.o \ src/ground/Rock.o \ src/ground/Snow_Env.o \ src/ground/Soil_Bgc.o \ src/ground/Soil_Env.o \ src/ground/Stefan.o \ src/ground/layer/Front.o \ src/ground/layer/Layer.o \ src/ground/layer/MineralLayer.o \ src/ground/layer/MossLayer.o \ src/ground/layer/OutputDepth.o \ src/ground/layer/PeatLayer.o \ src/ground/layer/RockLayer.o \ src/ground/layer/SnowLayer.o \ src/ground/layer/SoilLayer.o \ src/input/CohortInputer.o \ src/input/GridInputer.o \ src/input/RegionInputer.o \ src/input/RestartInputer.o \ src/input/SiteinInputer.o \ src/input/SoilclmInputer.o \ src/lookup/CCohortLookup.o \ src/lookup/CohortLookup.o \ src/lookup/SoilLookup.o \ src/output/AtmosphereOutputer.o \ src/output/CCohortdriver.o \ src/output/RegnOutputer.o \ src/output/RestartOutputer.o \ src/output/SiteOutputer.o \ src/output/SnowSoilOutputer.o \ src/output/SoilclmOutputer.o \ src/output/StatusOutputer.o \ src/output/VegetationOutputer.o \ src/run/AtmOutData.o \ src/run/Cohort.o \ src/run/Controller.o \ src/run/Grid.o \ src/run/ModelData.o \ src/run/RegnOutData.o \ src/run/SiteIn.o \ src/run/SnowSoilOutData.o \ src/run/SoilClm.o \ src/run/Timer.o \ src/run/VegOutData.o \ src/runmodes/Regioner.o \ src/runmodes/RunCohort.o \ src/runmodes/Siter.o \ src/util/CrankNicholson.o \ src/util/Exception.o \ src/util/FileChecking.o \ src/util/Integrator.o \ src/util/Interpolator.o \ src/vegetation/Vegetation_Bgc.o \ src/vegetation/Vegetation_Env.o OBJECTS= AtmosUtil.o \ Atmosphere.o \ BgcData.o \ CohortData.o \ EnvData.o \ FirData.o \ GridData.o \ RegionData.o \ RestartData.o \ WildFire.o \ Bucket.o \ DoubleLinkedList.o \ Ground.o \ Mineral.o \ Moss.o \ Peat.o \ Richard.o \ Rock.o \ Snow_Env.o \ Soil_Bgc.o \ Soil_Env.o \ Stefan.o \ Front.o \ Layer.o \ MineralLayer.o \ MossLayer.o \ OutputDepth.o \ PeatLayer.o \ RockLayer.o \ SnowLayer.o \ SoilLayer.o \ CohortInputer.o \ GridInputer.o \ RegionInputer.o \ RestartInputer.o \ SiteinInputer.o \ SoilclmInputer.o \ CCohortLookup.o \ CohortLookup.o \ SoilLookup.o \ AtmosphereOutputer.o \ CCohortdriver.o \ RegnOutputer.o \ RestartOutputer.o \ SiteOutputer.o \ SnowSoilOutputer.o \ SoilclmOutputer.o \ StatusOutputer.o \ VegetationOutputer.o \ AtmOutData.o \ Cohort.o \ Controller.o \ Grid.o \ ModelData.o \ RegnOutData.o \ SiteIn.o \ SnowSoilOutData.o \ SoilClm.o \ Timer.o \ VegOutData.o \ Regioner.o \ RunCohort.o \ Siter.o \ CrankNicholson.o \ Exception.o \ FileChecking.o \ Integrator.o \ Interpolator.o \ Vegetation_Bgc.o \ Vegetation_Env.o TEMOBJ= TEM.o dos-tem: $(SOURCES) $(TEMOBJ) $(CC) -o peat-dos-tem $(OBJECTS) $(TEMOBJ) $(LIBDIR) $(LIBS) lib: $(SOURCES) $(CC) -o libDOSTEM.so -shared $(INCLUDES) $(OBJECTS) $(LIBDIR) $(LIBS) .cpp.o: $(CC) $(CFLAGS) $(INCLUDES) $< clean: rm -f $(OBJECTS) DVMDOSTEM TEM.o libDOSTEM.so* *~
已尝试的无效操作
- 更新netCDF包及GNU工具
- 移除Makefile中的-fPIC参数
- 将-fPIC替换为-fPIE
- 将-fPIC移至CC=g++行并移除CFLAGS中的该参数
- 同时在CC行和CFLAGS中保留-fPIC
解决方案
问题根源
Ubuntu新版本默认启用**PIE(位置无关可执行文件)**编译作为安全特性,你的虚拟机系统版本可能比同事的更高,默认开启了该选项。而项目中使用的netcdf/libs/libnetcdf_c++.a静态库是用非PIE模式编译的,PIE可执行文件无法与非PIE静态库链接,导致Relocation错误。
快速修复方法
修改Makefile中dos-tem目标的链接命令,添加-no-pie参数,强制生成非PIE可执行文件:
dos-tem: $(SOURCES) $(TEMOBJ) $(CC) -no-pie -o peat-dos-tem $(OBJECTS) $(TEMOBJ) $(LIBDIR) $(LIBS)
修改后执行make clean && make即可完成编译。
进阶方案(如需保留PIE特性)
如果需要生成PIE可执行文件,需重新编译netCDF C++静态库,编译时添加-fPIE参数,确保库与可执行文件的编译模式一致。
内容的提问来源于stack exchange,提问作者Caroline

