Что мне делать, если две библиотеки предоставляют функцию с одинаковым именем, вызывающую конфликт?

94

Что мне делать, если у меня есть две библиотеки, которые предоставляют функции с эквивалентными именами?

qeek
источник
2
это статические библиотеки или динамически связанные?
Alnitak
нам нужны подробности ... экспортируются ли эти имена? или они используются только для внутреннего пользования? Вы можете изменить имена?
Йоханнес Шауб - литб,
Оба они динамически связаны. Я не могу изменить имена, поскольку мне не принадлежат библиотеки.
qeek
Отличный вопрос. Конечно , это не было бы проблемой , с этими двумя библиотеками , если все символы были приставочные с уникальным идентификатором (например vorbis_..., sf_..., sdl_...). По сути, это то, что C ++ делает с именами символов для функций с именами.
Vortico
Это очень интересный вопрос, но, к сожалению, слишком неточный, что является причиной слишком большого количества слишком общих ответов.
югр 06

Ответы:

52
  • Если вы управляете одним или обоими: отредактируйте один, чтобы изменить имя и перекомпилировать. Или, что то же самое, посмотрите ответы Бена и unknown , которые будут работать без доступа к исходному коду.
  • Если вы не контролируете ни один из них, вы можете завершить одно из них. Это компиляция другой ( статически связанной !) Библиотеки, которая ничего не делает, кроме реэкспорта всех символов оригинала, кроме ошибочного, доступ к которому осуществляется через оболочку с альтернативным именем. Какая неприятность.
  • Добавлено позже: поскольку qeek говорит, что он говорит о динамических библиотеках, решения, предложенные Ферруччо и Мувичиелем , вероятно, являются лучшими. (Кажется, я живу в давние времена, когда по умолчанию использовалась статическая связь. Это окрашивает мое мышление.)

По поводу комментариев: Под "экспортом" я подразумеваю сделать видимыми модули, связанные с библиотекой - эквивалент externключевому слову в области видимости файла. Как это контролируется, зависит от ОС и компоновщика. И это то, что я всегда должен смотреть.

dmckee --- котенок экс-модератора
источник
Это была моя первая мысль, но разве вы не столкнетесь с той же проблемой столкновения? В конце концов, весь проект должен быть связан - во время компиляции / компоновки или во время выполнения - в это время обе библиотеки-нарушители должны загружаться как есть.
Sniggerfardimungus,
@unknown: оболочка должна быть скомпилирована со статической компоновкой и не должна экспортировать оскорбительный символ. Тогда вы все еще можете динамически связать оболочку. Отредактировано для большей ясности, спасибо.
dmckee --- котенок экс-модератора
Если проблема qeek связана с ddl, а не со статическими библиотеками, как можно создать новую библиотеку с оболочкой? Поскольку библиотека-оболочка должна будет динамически оборачивать функцию в библиотеке, с которой вы не хотите связываться в первую очередь.
jeffD
@dmckee - что вы имеете в виду под "экспортом"?
4
может быть, кто-нибудь может привести простой пример этой техники? Один exe, две библиотеки, каждая из которых содержит одну функцию с тем же именем.
53

Можно переименовать символы в объектном файле, используя objcopy --redefine-sym old=new file (см. Man objcopy).

Затем просто вызовите функции, используя их новые имена, и свяжите их с новым объектным файлом.

Бен
источник
2
Ницца. Это было бы тривиально добавить в Makefile. Если библиотеки когда-либо будут обновлены, заклинание objcopy будет намного проще обновить, чем некоторые другие решения.
sigjuice
9
Не забудьте также переименовать символы в заголовочных файлах.
mouviciel 02
^ sed / awk / perl также может быть полезен для автоматизации переименования символов в заголовке
Alex Reinking
16

В Windows вы можете использовать LoadLibrary () для загрузки одной из этих библиотек в память, а затем использовать GetProcAddress () для получения адреса каждой функции, которую необходимо вызвать, и вызова функций через указатель функции.

например

HMODULE lib = LoadLibrary("foo.dll");
void *p = GetProcAddress(lib, "bar");
// cast p to the approriate function pointer type (fp) and call it
(*fp)(arg1, arg2...);
FreeLibrary(lib);

получит адрес функции с именем bar в foo.dll и вызовет ее.

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

Ферруччо
источник
dlopen dlsym, и dlclose. Однако инкапсуляция в Unix может быть не такой эффективной, как в Windows.
user877329 03
8

Вот такая мысль. Откройте одну из проблемных библиотек в шестнадцатеричном редакторе и замените все вхождения ошибочных строк на что-то другое. После этого вы сможете использовать новые имена во всех будущих звонках.

ОБНОВЛЕНИЕ: я только что сделал это с этой целью, и, похоже, это сработало. Конечно, я не проверял это полностью - это может быть не более чем действительно хороший способ оторвать себе ногу из дробовика hexedit.

Sniggerfardimungus
источник
на самом деле не страшное решение. Немного хакерский, но все, что вам нужно сделать, это изменить строки в таблице символов. Никакого реального функционального вреда в этом нет.
Эван Теран,
Вы, вероятно, также захотите переименовать библиотеку, чтобы кто-то другой не попытался загрузить ее снова. Вы можете перейти от одного конфликта к десяткам или сотням. =] Мне нравится это в stackoverflow: у нас есть проверенный ответ на вопрос, за который проголосовало 3 человека. Первый (неполный) ответ: 17. =]
Sniggerfardimungus,
1
Переименование возможности ограничены , как вы только будете в состоянии сделать имена короче . Также в Linux вам будет сложно обновлять хеш-таблицы ELF.
югр 06
8

Если у вас есть файлы .o, хороший ответ здесь: https://stackoverflow.com/a/6940389/4705766

Резюме:

  1. objcopy --prefix-symbols=pre_string test.o переименовать символы в файле .o

или

  1. objcopy --redefine-sym old_str=new_str test.o для переименования конкретного символа в файле .o.
Джи Ли
источник
7

Предполагая, что вы используете Linux, вам сначала нужно добавить

#include <dlfcn.h>

Объявите переменную указателя функции в правильном контексте, например,

int (*alternative_server_init)(int, char **, char **);

Как и Ферруччо, указанное в https://stackoverflow.com/a/678453/1635364 , загрузите явно библиотеку, которую вы хотите использовать, выполнив (выберите свои любимые флаги)

void* dlhandle;
void* sym;

dlhandle = dlopen("/home/jdoe/src/libwhatnot.so.10", RTLD_NOW|RTLD_LOCAL);

Прочтите адрес функции, которую вы хотите вызвать позже

sym = dlsym(dlhandle, "conflicting_server_init");

назначьте и приведите следующим образом

alternative_server_init = (int (*)(int, char**, char**))sym;

Звоните аналогично оригиналу. Наконец, выгрузите, выполнив

dlclose(dlhandle);
враа
источник
6

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

Я не пробовал, но решение может быть с dlopen(), dlsym()и dlclose()которые позволяют программно обрабатывать динамические библиотеки. Если вам не нужны две функции одновременно, вы можете открыть первую библиотеку, использовать первую функцию и закрыть первую библиотеку перед использованием второй библиотеки / функции.

Mouviciel
источник
Спасибо. Не думал об этом. Хотя хотелось бы иметь и то, и другое одновременно.
qeek
Что, если я хочу использовать и то, и другое одновременно?
QZHua
@QZHua: Другие ответы (например, связанные с переименованием символов) должны решить вашу проблему.
mouviciel
4

Эта проблема является причиной того, что в C ++ есть пространства имен. На самом деле нет отличного решения в c для двух сторонних библиотек с одинаковым именем.

Если это динамический объект, вы можете явно загрузить общие объекты (LoadLibrary / dlopen / etc) и вызвать их таким образом. В качестве альтернативы, если вам не нужны обе библиотеки одновременно в одном и том же коде, вы можете что-то сделать со статической компоновкой (если у вас есть файлы .lib / .a).

Конечно, ни одно из этих решений не применимо ко всем проектам.

Брайан Митчелл
источник
2
О да. На этот общий вопрос это кажется хорошим ответом. Однако - пространства имен - это круто, если вы компилируете все вместе в одном компиляторе. Ура, никаких столкновений имен. Но если вы получаете библиотеку в двоичном виде и хотите интегрировать ее с другим компилятором, то - удачи. Правила изменения имен в объектных файлах - это только первое препятствие (может помочь внешний "C", который отменяет эффект пространств имен).
Tomasz Gandor
3

Поклясться? Насколько мне известно, вы мало что можете сделать, если у вас есть две библиотеки, которые предоставляют точки ссылок с одинаковыми именами, и вам нужно связать их с обеими.

Ватин
источник
12
Поклясться - это определенно первый шаг. Никакого сомнения по этому поводу.
dmckee --- котенок экс-модератора
1
«Вы мало что можете сделать» - это все еще актуально? Другие ответы предоставляют множество различных решений.
югр 06
2

Вы должны написать библиотеку-оболочку вокруг одного из них. Ваша библиотека-оболочка должна предоставлять символы с уникальными именами, а не символы с неуникальными именами.

Другой вариант - переименовать имя функции в файле заголовка и переименовать символ в архиве объектов библиотеки.

В любом случае, использовать оба, это будет хакерская работа.

Джеймс Каксесе
источник
1

Вопрос почти десятилетней давности, но все время новые поиски ...

Как уже было сказано, objcopy с флагом --redefine-sym - хороший выбор в Linux. См., Например, https://linux.die.net/man/1/objcopy. для получения полной документации. Это немного неуклюже, потому что вы, по сути, копируете всю библиотеку при внесении изменений, и каждое обновление требует повторения этой работы. Но, по крайней мере, это должно работать.

Для Windows динамическая загрузка библиотеки является решением и постоянным, как альтернатива dlopen в Linux. Однако и dlopen (), и LoadLibrary () добавляют дополнительный код, которого можно избежать, если единственной проблемой являются повторяющиеся имена. Здесь решение Windows более элегантно, чем подход objcopy: просто сообщите компоновщику, что символы в библиотеке известны под другим именем, и используйте это имя. Есть несколько шагов, чтобы сделать это. Вам необходимо создать файл def и предоставить перевод имени в разделе ЭКСПОРТ. См. Https://msdn.microsoft.com/en-us/library/hyx1zcd3.aspx (VS2015, в конечном итоге он будет заменен более новыми версиями) или http://www.digitalmars.com/ctg/ctgDefFiles.html(возможно, более постоянный) для полной информации о синтаксисе файла def. Процесс заключался бы в том, чтобы создать файл def для одной из библиотек, затем использовать этот файл def для создания файла lib и затем связать с этим файлом lib. (Для библиотек DLL Windows файлы lib используются только для связывания, но не для выполнения кода.) См. Раздел Как создать файл .lib при наличии файла .dll и файла заголовка, чтобы узнать о процессе построения файла lib. Единственное отличие - добавление псевдонимов.

И для Linux, и для Windows переименуйте функции в заголовках библиотеки, имена которых имеют псевдонимы. Другой вариант, который должен работать, - это в файлах, относящихся к новым именам, #define old_name new_name, #include заголовки библиотеки, экспорт которой является псевдонимом, а затем #undef old_name в вызывающей стороне. Если в библиотеке используется много файлов, более простой альтернативой является создание заголовка или заголовков, которые обертывают определения, включают и undefs, а затем использовать этот заголовок.

Надеюсь, эта информация была полезной!

Джим Монте
источник
0

Я никогда не использовал dlsym, dlopen, dlerror, dlclose, dlvsym и т. Д., Но я смотрю справочную страницу, и на ней приводится пример открытия libm.so и извлечения функции cos. Проходит ли dlopen процесс поиска коллизий? Если этого не произойдет, OP может просто загрузить обе библиотеки вручную и присвоить новые имена всем функциям, которые предоставляют его библиотеки.

Sniggerfardimungus
источник