tags:

views:

69

answers:

1

I know that by default undefined symbols are ignored at compile time. However, I would also like them to be ignored at run-time. I need to distribute a .so that will run with and without MPI. I will know ahead of time if it is an MPI job and if it is not I won't make any MPI_* calls. If it's not an MPI run I need the application to not care that it cannot resolve the MPI_* symbols.

Is this possible? I could have sworn I've done this before but I can't get it working. Everytime I run I immediately get the following even though the logic in my code will never allow that symbol to be referenced:

undefined symbol: hpmp_comm_world

For what it's worth I am using the Intel Fortran Compiler to build the .so file.

EDIT

I found the linker flag: "-z lazy" which is supposed to resolve references to functions when the function is called which is what I want. That doesn't fix my problem, but hpmp_comm_world is a variable - not a function. Should that make a difference?

+3  A: 

You can define a symbol to be a weak reference to its definition. Then, the symbol's value will be zero if the definition is not present.

For example, suppose the following is ref.c, which references a function and variable that may or may not be present; we'll use it to build libref.so (corresponding to your library, in your question):

#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();
}

Here, global_func and global_variable are the weak references to the possibly-available function and variable. This code prints the function's address, increments the variable if it is present, and calls the function if it is present. (Note that the function's and variable's addresses are zero when they are not defined, so it is &global_variable that you must compare with zero.)

And suppose this is def.c, which defines global_func and global_variable; we'll use it to build libdef.so (corresponding to MPI, in your question):

#include <stdio.h>

int global_variable;

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

And finally, suppose we have a main program, main.c, which calls ref_func from libref.so:

#include <stdio.h>

extern void ref_func(void);

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

Here's the Makefile that builds libref.so and libdef.so, and then builds two executables, both of which link against libref.so, but only one of which links against 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

Do the build:

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

Note that both ref-absent and ref-present linked without problems, even though there is no definition for global_name in ref-absent.

Now we can run the programs, and see that ref-absent skips the function call, while ref-present uses it. (We have to set LD_LIBRARY_PATH to allow the dynamic linker to find our shared libraries in the current directory.)

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

The trick for you will be getting the ((weak)) attribute attached to every declaration of every MPI function your library references. However, as ref.c shows, there can be multiple declarations, and as long as one of them mentions the weak attribute, you're done. So you'll probably have to say something like this (I don't really know 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));

Every reference to an MPI function needs to be in the scope of a ((weak)) declaration for that function; that's how the compiler decides what sort of symbol reference to put in the object file. You'll want to have automated tests to verify that you haven't accidentally generated any non-weak references.

Jim Blandy