Вы можете определить символ как слабую ссылку на его определение. Тогда значение символа будет равно нулю, если определение отсутствует.
Например, предположим, что следующим является 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_func
и global_variable
- слабые ссылки на возможно доступные функцию и переменную. Этот код печатает адрес функции, увеличивает переменную, если она присутствует, и вызывает функцию, если она присутствует. (Обратите внимание, что адреса функции и переменной равны нулю, когда они не определены, поэтому &global_variable
необходимо сравнивать с нулем.)
И предположим, что это def.c, который определяет global_func
и global_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, которая вызывает ref_func
из libref.so:
#include <stdio.h>
extern void ref_func(void);
int main(int argc, char **argv) {
printf("%s: ", argv[0]);
ref_func();
return 0;
}
Вот Makefile, который создает libref.so и libdef.so, а затем создает два исполняемых файла, каждый из которых ссылается на 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-отсутствующий и ref-present связаны без проблем, даже если для ref-отсутствующего нет определения global_name
.
Теперь мы можем запускать программы и видеть, что ref-отсутствующий пропускает вызов функции, а 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 должна находиться в пределах ((слабой)) декларации для этой функции; Вот как компилятор решает, какую ссылку на символ поместить в объектный файл. Вам понадобятся автоматические тесты, чтобы убедиться, что вы случайно не сгенерировали неслабые ссылки.