我有一个 Fortran 工具,它使用 MKL 库中的函数 dgemm、dgemv、dgesvx 和 dsyev。我需要将代码编译为共享对象 (.so),以使其在不同的电脑上运行。我正在使用 gfortran 编译器和链接器。 MKL 库的链接必须是静态,因为另一台电脑上未安装 MKL 和 gfortran。
My .so 本身不是可执行文件,而是访问其函数的 Python 工具的一部分 - 因此 Fortran 代码中没有类似于 main() 函数的东西。 (我在这方面有几个错误,所以我想在这里提一下......)
(1) 我使用 Intel Link Line Advisor 来确定所需的标志。生成的链接器 (LDFLAGS) 和编译器标志 (FFLAGS) 是:
(2) 由于个人需求,我添加了以下Flag: FF标志:
(3) 我将所有内容合并到一个 Makefile 中。
OUTPUT_LIBRARY := OI.so
MKLROOT := /opt/intel/oneapi/mkl/latest/
# Name of Compiler
FC := gfortran
# Compileroptions
FFLAGS := -Wall -m64 -fPIC -fdefault-integer-8 -I"${MKLROOT}/include" # -fdec-math ist veraltet, wird daher entfernt
FFLAGS_RELEASE := -O3
FFLAGS_DEBUG := -g
## Linkeroptions
core := $(MKLROOT)/lib/libmkl_core.a
ilp := $(MKLROOT)/lib/libmkl_gf_ilp64.a
sequential := $(MKLROOT)/lib/libmkl_sequential.a
LDFLAGS := -Wall -m64 -shared -Wl,--start-group $(core) $(ilp) $(sequential) -Wl,--end-group -lpthread -lm -ldl
# Source-Files
SRC := S_Module_ivu.for\
s_analyse_IVU.for\
covsrt.for\
gaussj.for\
mrqcof.for\
mrqmin.for\
nrrank.f\
S_acsurf_ivu.for\
S_diff_ivu.for\
S_expo_fit_neu_ivu.for\
S_getac_ivu.for\
S_justl.for\
S_ord_ivu.for\
S_PERCENTILE_ivu.for\
getKanal.f90\
S_fileprocs_ivu.f90\
S_setoiparams_ivu.f90\
SS_oi_ivu.f90\
S_ANA_VARIANZ_IVU.FOR\
S_CORRECS_ivu.FOR\
S_FUNC_DEF_ivu.FOR\
S_LOCGEO_ivu.FOR\
S_NORMC_IVU.FOR\
S_SCALCWTSW_ivu.FOR
SRC_for := $(filter %.for,$(SRC))
SRC_FOR := $(filter %.FOR,$(SRC))
SRC_f := $(filter %.f,$(SRC))
SRC_f90 := $(filter %.f90,$(SRC))
OBJ_for := $(patsubst %.for,%.o,$(SRC_for))
OBJ_FOR := $(patsubst %.FOR,%.o,$(SRC_FOR))
OBJ_f := $(patsubst %.f,%.o,$(SRC_f))
OBJ_f90 := $(patsubst %.f90,%.o,$(SRC_f90))
OBJ_ALL := $(OBJ_for) $(OBJ_FOR) $(OBJ_f) $(OBJ_f90)
# Default-Target: .so in release mode
all: release
release: FFLAGS += $(FFLAGS_RELEASE)
debug: FFLAGS += $(FFLAGS_DEBUG)
release: $(OUTPUT_LIBRARY)
debug: $(OUTPUT_LIBRARY)
# Compile
$(OBJ_for): %.o: %.for
$(FC) $(FFLAGS) -c $< -o $@
$(OBJ_FOR): %.o: %.FOR
$(FC) $(FFLAGS) -c $< -o $@
$(OBJ_f): %.o: %.f
$(FC) $(FFLAGS) -c $< -o $@
$(OBJ_f90): %.o: %.f90
$(FC) $(FFLAGS) -c $< -o $@
# Link
$(OUTPUT_LIBRARY): $(OBJ_ALL)
$(FC) $(LDFLAGS) -o $@ $^
# Option for cleaning up
.PHONY: clean
clean:
rm -f $(OBJ_ALL)
rm -f *.mod
rm -f $(OUTPUT_LIBRARY)
运行
make release
时,我没有收到任何错误消息,并且 OI.so 已成功创建。然而,当检查 OI.so 中的未定义符号时(使用 nm -u OI.so
),它们将所有必需的 MKL 函数列为未定义。由于这些未定义的函数,我的程序无法运行。
有什么建议我在这里做错了什么吗?
抱歉,如果我来得太晚了。问题是 您的链接在引用库的对象文件之前消耗库。1 该答案对您的 Makefile 的应用值得澄清。
GNU/Linux 链接器按照命令行顺序使用输入文件(目标文件和库),从不重新访问输入文件,但它会循环访问 重新访问由
--start-group/--end-group
选项包围的一系列静态库,解决未定义的引用,直到没有新的引用出现。在所有情况下,包括该例外,它只会考虑一个库(静态或共享)来查找未定义引用的定义已经在手。也就是说,由目标文件已输入引入的未定义符号(在命令行上明确命名的目标文件或从静态库中提取的目标文件)。与库相反,输入的任何显式命名的目标文件都会无条件链接到输出文件。
这意味着,在将某些显式命名的对象文件输入到链接之前,有 0 个未定义的引用需要解析,并且链接器将忽略所有库 - 即使它们可能提供链接器尚未解析的未定义引用的定义看。这种未定义的引用将一直存在,直到链接结束(除非再次输入必要的库),并且将以失败告终。 这些事实破坏了您的特定链接,因为您的 Makefile 编码错误,特别是链接规则:
$(OUTPUT_LIBRARY): $(OBJ_ALL)
$(FC) $(LDFLAGS) -o $@ $^
其中
LDFLAGS
是:
LDFLAGS := -Wall -m64 -shared -Wl,--start-group $(core) $(ilp) $(sequential) -Wl,--end-group -lpthread -lm -ldl
此分配
LDFLAGS
在所有目标文件
$^
之前输入所有库,结果它们被忽略。按照 GNU Make 的约定,应为 LDFLAGS
分配链接选项
other,而不是库选项,并且库选项应分配给
LDLIBS
。要更正您的 Makefile,请指定:LDFLAGS := -Wall -m64 -shared
LDLIBS := -Wl,--start-group $(core) $(ilp) $(sequential) -Wl,--end-group -lpthread -lm -ldl
并将您的链接配方更改为:
$(OUTPUT_LIBRARY): $(OBJ_ALL)
$(FC) $(LDFLAGS) -o $@ $^ $(LDLIBS)