在Linux中如何在运行时忽略未定义的符号?
收藏

我知道默认情况下未定义的符号在编译时被忽略。但是,我也希望在运行时忽略它们。我需要分发一个。这样就可以运行mpi和不运行mpi。如果是MPI作业,我会提前知道,如果不是,我不会打任何MPI电话。如果不是MPI运行,我需要应用程序不在乎它不能解析MPI符号。
这可能吗?我可以发誓我以前做过,但我不能让它工作。每次运行时,我都会立即得到以下结果,即使代码中的逻辑永远不允许引用该符号:

undefined symbol: hpmp_comm_world

值得一提的是,我正在使用英特尔Fortran编译器来构建.so文件。
编辑
我找到了链接器标志:“-z lazy”,它应该在调用函数时解析对函数的引用,这正是我想要的。这并不能解决我的问题,但hpmp_comm_world是一个变量,而不是函数。那会有什么不同吗?


最佳答案:

可以将符号定义为对其定义的弱引用。然后,如果定义不存在,符号的值将为零。
例如,假设下面是ref.c,它引用了一个可能存在也可能不存在的函数和变量;我们将使用它来构建libref.so(在您的问题中,对应于您的库):

#include <stdio.h>

void global_func(void);
void global_func(void) __attribute__ ((weak));

extern int global_variable __attribute__((weak));

void ref_func() {
  printf("global_func = %p\n", global_func);
  if (&global_variable)
    global_variable++;
  if (global_func)
    global_func();
}

这里,global_funcglobal_variable是对可能可用的函数和变量的弱引用。此代码打印函数的地址,如果变量存在,则递增变量,如果函数存在,则调用函数。(注意,函数和变量的地址在未定义时为零,因此必须与零进行比较。)
假设这是def.c,它定义了&global_variableglobal_func;我们将使用它来构建libdef.so(在您的问题中,对应于mpi):
#include <stdio.h>

int global_variable;

void global_func(void) {
  printf("Hi, from global_func!  global_variable = %d\n", global_variable);
}

最后,假设我们有一个主程序main.c,它从libref.so调用global_variable
#include <stdio.h>

extern void ref_func(void);

int main(int argc, char **argv) {
  printf("%s: ", argv[0]);
  ref_func();
  return 0;
}

下面是生成libref.so和libdef.so的makefile,然后生成两个可执行文件,这两个文件都与libref.so链接,但只有一个链接与libdef.so链接:
all: ref-absent ref-present
ref-absent: main.o libref.so
    $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@
ref-present: main.o libref.so libdef.so
    $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@

lib%.so: %.o
    $(CC) $(CFLAGS) $(LDFLAGS) -shared $^ -o $@

ref.o def.o: CFLAGS += -fpic

clean:
    rm -f *.o *.so ref-absent ref-present

生成:
$ make
cc    -c -o main.o main.c
cc -fpic   -c -o ref.o ref.c
cc   -shared ref.o -o libref.so
cc   main.o libref.so -o ref-absent
cc -fpic   -c -o def.o def.c
cc   -shared def.o -o libdef.so
cc   main.o libref.so libdef.so -o ref-present
$ 

注意ref-absent和ref-present linked都没有问题,即使ref-absent中没有ref_func的定义。
现在我们可以运行程序,看到ref-absent跳过函数调用,而ref-present使用它。(我们必须设置ld_library_path,以允许动态链接器在当前目录中找到我们的共享库。)
$ LD_LIBRARY_PATH=. ./ref-absent
./ref-absent: global_func = (nil)
$ LD_LIBRARY_PATH=. ./ref-present
./ref-present: global_func = 0x15d4ac
Hi, from global_func!  global_variable = 1
$ 

诀窍是将global_name属性附加到库引用的每个mpi函数的每个声明中。然而,如ref.c所示,可以有多个声明,只要其中一个提到弱属性,就完成了。所以你可能不得不这样说(我不太了解MPI):
#include <mpi.h>

mpi_fake_type_t mpi_function_foo(mpi_arg_type_t) __attribute__((weak));
mpi_fake_type_t mpi_function_bar(mpi_other_arg_type_t) __attribute__((weak));

对mpi函数的每个引用都需要在该函数的((弱)声明的范围内;这就是编译器如何决定将哪种符号引用放入对象文件的方式。您需要有自动测试来验证您没有意外生成任何非弱引用。

公众号