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

时间:2010-08-03 18:37:52

标签: linux linker

我知道默认情况下,在编译时会忽略未定义的符号。但是,我也希望它们在运行时被忽略。我需要分发一个使用和不使用MPI运行的.so。我会提前知道它是否是MPI工作,如果不是,我将不会进行任何MPI_ *调用。如果它不是MPI运行,我需要应用程序不关心它无法解析MPI_ *符号。

这可能吗?我本来可以发誓我之前做过这件事,但是我无法让它发挥作用。我每次运行时都会立即得到以下内容,即使我的代码中的逻辑永远不会允许引用该符号:

undefined symbol: hpmp_comm_world

为了我的价值,我使用英特尔Fortran编译器来构建.so文件。

修改

我找到了链接器标志:“-z lazy”,它应该在调用函数时解析对函数的引用,这就是我想要的。这不能解决我的问题,但是hpmp_comm_world是一个变量 - 而不是一个函数。这应该有所作为吗?

1 个答案:

答案 0 :(得分:6)

您可以将符号定义为其定义的弱引用。然后,如果定义不存在,则符号的值将为零。

例如,假设以下是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是对可能可用的函数和变量的弱引用。此代码打印函数的地址,如果存在则递增变量,并调用函数(如果存在)。 (注意,函数和变量的地址在未定义时为零,因此必须将&global_variable与零进行比较。)

假设这是def.c,它定义global_funcglobal_variable;我们将用它来构建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调用ref_func

#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
$ 

请注意,即使没有global_name的定义,ref_ensent和ref-present也没有问题链接。

现在我们可以运行程序,并看到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
$ 

你的诀窍是将((weak))属性附加到你的库引用的每个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函数的每个引用都需要在该函数的((弱))声明的范围内;这就是编译器决定放入目标文件的符号引用类型的方式。您需要进行自动化测试,以验证您是否未意外生成任何非弱引用。