связать статическую библиотеку с общей библиотекой и скрыть экспортированные символы

У меня раздражающая проблема с компоновщиком. Я хочу связать некоторые символы из общей библиотеки со статической библиотекой, но не экспортировать ее символы (т.е. я не могу просто объединить библиотеки или связать их с --whole-archive). Я хочу связать (например, связать исполняемый файл, решить неопределенные символы) мою разделяемую библиотеку со статической и удалить неопределенные символы.

То, что я ищу, — это, вероятно, просто опция компоновщика, но я не могу это понять.

Я постараюсь описать проблему как можно лучше (это не так просто), а затем приведу минимальный игрушечный пример для игры.

РЕДАКТИРОВАТЬ: проблема была решена, решение размещено в нижней части вопроса

Краткое описание:

Я хочу использовать LD_PRELOAD трюк, чтобы перехватить некоторые вызовы функций в исполняемом файле. Этот исполняемый файл связан со сторонней общей библиотекой, которая содержит определение функций, которые я хочу перехватить.

Эта сторонняя библиотека также содержит символы из еще одной библиотеки, которую я также использую в своей библиотеке, но с другой (несовместимой) версией.

Я хочу скомпилировать мою общую библиотеку и связать ее во время компиляции с определениями последней (статической) библиотеки без экспорта символов, чтобы моя общая библиотека использовала версию, отличную от той, которую я хочу перехватить.

Упрощенное описание проблемы

У меня есть сторонняя библиотека под названием libext.so, для которого у меня нет исходного кода. Это определяет функцию bar и использует функцию foo из другой библиотеки, но символы там определены:

$> nm libext.so
0000000000000a16 T bar
00000000000009e8 T foo

Как я уже заметил, foo это внешняя зависимость, для которой я хочу использовать более новую версию. У меня есть обновленная библиотека для этого, давайте назовем это libfoo.a:

$> nm libfoo.a
0000000000000000 T foo

Теперь проблема в том, что я хочу создать динамическую библиотеку, которая переопределяет bar, но я хочу, чтобы моя библиотека использовала определение foo от libfoo.a и я хочу функции от libext.so вызвать функцию foo от libext.so, Другими словами, я хочу, чтобы компиляция моей библиотеки libfoo.a,

То, что я ищу, это определить библиотеку, которая использует libfoo.a но не экспортирует свои символы. Если я свяжу свою библиотеку с libfoo.a, Я получил:

$> nm libmine.so
0000000000000a78 T bar
0000000000000b2c T foo

Что означает, что я перегружаю оба foo а также bar (я не хочу переопределять foo). Если я не свяжу свою библиотеку с libfoo.a, Я получил:

$> nm libmine.so
0000000000000a78 T bar
U foo

Так что моя библиотека будет использовать их версию fooчто я тоже не хочу. Что я хочу это:

$> nm libmine.so
0000000000000a78 T bar

куда foo связан во время компиляции и его символ не экспортируется.

Минимальный пример

Вам не нужно читать это, но вы можете использовать его, чтобы поиграть и найти решение.

bar.cpp: представляет стороннее приложение, для которого у меня нет кода:

#include <iostream>
extern "C" void foo(){ std::cerr << "old::foo" << std::endl; }
extern "C" void bar(){ std::cerr << "old::bar" << std::endl; foo(); }

foo.cpp: представляет более новую версию функции, используемой как моей библиотекой, так и третьей стороной:

#include <iostream>
extern "C" void foo(){ std::cerr << "new::foo" << std::endl; }

trap.cpp: код из моей библиотеки, это ловушки barназывает новый foo и вперед:

#include <iostream>
extern "C" {
#include <dlfcn.h>
}
extern "C" void foo();
extern "C" void bar(){
std::cerr << "new::bar" << std::endl;
foo(); // Should be new::foo
void (*fwd)() = (void(*)())dlsym(RTLD_NEXT, "bar");
fwd(); // Should use old::foo
}

exec.cpp: фиктивный исполняемый файл для вызова bar:

extern "C" void bar();

int main(){
bar();
}

Makefile: Только Unix, извините

default:
# The third party library
g++ -c -o bar.o bar.cpp -fpic
gcc -shared -Wl,-soname,libext.so -o libext.so bar.o
# The updated library
g++ -c -o foo.o foo.cpp -fPIC
ar rcs libfoo.a foo.o
# My trapping library
g++ -c -o trap.o trap.cpp -fPIC
gcc -shared -Wl,-soname,libmine.so -o libmine.so trap.o -ldl -L. -lfoo
# The dummy executable
g++ -o test exec.cpp -L. libext.so

В этом случае, bar звонки foo; нормальное исполнение:

$> ./test
old::bar
old::foo

Предварительная загрузка моей библиотеки перехватывает barзовет мой foo и вперед barтекущее исполнение:

$> LD_PRELOAD=libmine.so ./test
new::bar
new::foo
old::bar
new::foo

Последняя строка неверна, желаемый результат:

$> LD_PRELOAD=libmine.so ./test
new::bar
new::foo
old::bar
old::foo

Решение

1) Как указано в принятом ответе, мы можем использовать скрипт версии компоновщика, чтобы изменить область действия нежелательных символов с глобального на локальный:

BAR {
global: bar;
local: *;
};

Компиляция с версией компоновщика показывает foo является локальным, и программа теперь ведет себя как ожидалось:

$> gcc -shared -Wl,-soname,libmine.so -Wl,--version-script=libmine.version -o libmine.so trap.o -ldl -L. -lfoo
$> nm libmine.so
0000000000000978 T bar
0000000000000000 A BAR
0000000000000a2c t foo
$> LD_PRELOAD=libmine.so ./test
new::bar
new::foo
old::bar
old::foo

2) Альтернативой является повторная компиляция libfoo.a с атрибутом -fvisibility=hidden и ссылка против этого. Видимость экспортированных символов также локальна, и поведение такое же, как и выше.

13

Решение

Вы хотите использовать скрипт версии компоновщика, который экспортирует символы, которые вы хотите (bar здесь) и шкуры все остальное.

пример Вот.

3

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]