Как сделать так, чтобы функция имела внутреннюю библиотечную связь?
Например, если у меня есть два файла foo.c
а также bar.o
, а также foo.c
содержит функцию foo()
который ссылается на функцию bar()
в bar.o
:
int foo(int x) { x = bar(x); /* ... */ }
Как я могу скомпилировать статическую или динамическую библиотеку, которая предоставляет foo()
, но не выставляет bar()
? Другими словами, я хочу bar()
быть связанным только внутри библиотеки.
2 ответа
В стандартном C вы можете только экспортировать функцию или нет, опция "экспортировать только в эти файлы" отсутствует. Так что в основном вам придется двигаться bar()
в foo.c
и объявить это как static
, Если вы хотите сохранить файл отдельно, уродливый хак будет #include
это из foo.c
(а не компилировать bar.o
)...
С инструментами, выходящими за рамки стандарта C, вы можете удалить публичный экспорт из библиотеки по ссылке или после нее. Некоторые решения компоновщика показаны ниже, и с помощью GCC и clang (в тех случаях, когда вы можете изменить код) вы можете скрыть функцию, добавив к ней префикс нестандартного атрибута: __attribute__ ((visibility ("hidden")))
- эквивалентом этого будет единица компиляции -fvisibility=hidden
при компиляции, например, bar.c
,
С обходной путь
Если вы можете редактировать код C свободно, обходной путь в стандартном C будет bar()
static
в bar.c
и доставить указатель на функцию для использования в foo()
с помощью некоторых средств, например, экспортировать указатель на struct
содержащий указатель на функцию (и любые другие "частные" данные), и не раскрывать детали struct
вне частного заголовка, используемого только вашей библиотекой.
Например:
В bar.h
(конфиденциально, не делитесь с пользователем библиотеки):
struct bar_private_struct { int (*bar)(int); };
extern struct bar_private_struct *bar_functions;
В bar.c
:
#include "bar.h"
static int bar (int x) { /* … */ return x; }
static struct bar_private_struct functions = { bar };
struct bar_private_struct *bar_functions = &functions;
В foo.c
:
#include "bar.h"
int foo (int x) { x = bar_functions->bar(x); /* … */ }
В этом решении будет экспортированный указатель с именем bar_functions
, но никакие детали о данных / функциях, на которые указывают, не раскрываются через этот экспорт. Без доступа к bar.h
пользователь библиотеки должен будет перепроектировать содержимое, чтобы правильно вызывать "частные" функции. В случае нескольких "приватных" функций этот подход также может объединять их в один экспортированный указатель, удаляя помехи из списка экспорта.
Линкерные решения
Исследуя конкретные линкеры, я нашел способ исключить определенные символы из динамической библиотеки:
С GNU ld
создайте скрипт версии, такой как libfoobar.version
:
FOOBAR {
global: *;
local: bar;
};
Вызов через gcc
:
gcc -shared -o libfoobar.so foo.o bar.o -Wl,-version-script=libfoobar.version
С clang
ld
(в OS X) создайте список неэкспортированных символов, таких как unexported
(один символ на строку):
_bar
Вызов через clang
:
clang -shared -o libfoobar.dylib foo.o bar.o -Wl,-unexported_symbols_list,unexported
В обоих случаях функция bar
скрыт и внешне не вызывается, но foo
остается в рабочем состоянии (и звонки bar
внутренне), хотя оба имели одинаковую внешнюю видимость в своих исходных (и объектных) файлах.
Тестовый код, foo.c
:
int bar(int x);
int foo (int x) { return bar(x) * 3; }
bar.c
:
int bar (int x) { return x * 2; }
main.c
(ссылка на библиотеку перед удалением экспорта для bar
):
#include <stdio.h>
int foo(int x);
int bar(int x);
int main () {
(void) printf("foo(2) = %d\n", foo(2));
(void) printf("bar(2) = %d\n", bar(2));
return 0;
}
Прецедент:
# before unexporting bar:
$ nm -gU libfoobar.dylib
0000000000000f70 T _bar
0000000000000f50 T _foo
$ ./main
foo(2) = 12
bar(2) = 4
# after unexporting bar:
$ nm -gU libfoobar.dylib
0000000000000f50 T _foo
$ ./main
foo(2) = 12
dyld: lazy symbol binding failed: Symbol not found: _bar
Функции могут иметь только внутреннюю или внешнюю связь.
Если вы хотите использовать разные модули, ваши функции должны иметь внешнюю связь, чтобы их можно было вызывать из одного блока перевода в другой.
Вы можете использовать внешний указатель на статическую функцию, но, конечно, это позволит другим модулям вызывать функцию через указатель на функцию.