Що робити, якщо дві бібліотеки надають функцію з однаковим іменем, що генерує конфлікт?


94

Що робити, якщо у мене є дві бібліотеки, які надають функції з еквівалентними іменами?


2
це статичні бібліотеки чи динамічно пов’язані?
Alnitak

нам потрібні докладніші відомості ... чи експортуються ці імена? або вони використовуються лише внутрішньо? Чи можете ви змінити імена?
Йоганнес Шауб - літ

Вони динамічно пов’язані, обидва. Я не можу змінити імена, оскільки не є власником бібліотек.
qeek

Чудове запитання. Звичайно , це не було б проблемою , з цими двома бібліотеками , якщо всі символи були префіксальні з унікальним ідентифікатором (наприклад vorbis_..., sf_..., sdl_...). Це по суті те, що C ++ робить з іменами символів для функцій з простором імен.
Vortico

Це дуже цікаве питання, але, на жаль, занадто неточне, що є причиною надто великої кількості занадто широких відповідей.
югр

Відповіді:


52
  • Якщо ви керуєте одним або обома: відредагуйте один, щоб змінити ім'я та перекомпілюйте. Або аналогічно перегляньте відповіді Бена та Невідомого , які працюватимуть без доступу до вихідного коду.
  • Якщо ви не контролюєте жодного з них, ви можете обернути одного з них. Тобто скомпілює іншу ( статично пов’язану !) Бібліотеку, яка не робить нічого, крім реекспорту всіх символів оригіналу, за винятком оскаржуючого, який досягається через обгортку з альтернативним ім’ям. Які клопоти.
  • Додано пізніше: Оскільки qeek каже, що він говорить про динамічні бібліотеки, рішення, запропоновані Ферруччо та Мувічіелем, є, мабуть, найкращими. (Здається, я живу в давні часи, коли статичне зв’язування було за замовчуванням. Це фарбує моє мислення)

Про коментарі: Під "експортом" я маю на увазі зробити видимими модулі, що посилаються на бібліотеку - еквівалентно externключовому слову в області файлу. Те, як це контролюється, залежить від ОС та лінкера. І це те, що мені завжди потрібно шукати.


Це була і моя перша думка, але чи не опинитеся ви в тій же проблемі зіткнення? Врешті-решт, весь проект повинен зв’язати - під час компіляції / підключення або під час виконання - тоді обидві бібліотеки-порушники повинні завантажуватися як є.
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

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


8

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

ОНОВЛЕННЯ: Я щойно зробив це з цього приводу, і, здається, це працює. Звичайно, я цього ретельно не перевірив - можливо, це не більше, ніж справді хороший спосіб обдути ногу дробовиком із шестигранною рушницею.


насправді не страшне рішення. Трохи хакерський, але все, що ви б робили, це зміна рядків у таблиці символів. Жодної реальної функціональної шкоди в цьому немає.
Еван Теран,

Можливо, ви також хотіли б перейменувати бібліотеку - щоб хтось інший не спробував завантажити річ ще раз. Ви перейшли б від одного конфлікту до десятків чи сотень. =] Мені подобається це щодо stackoverflow: у нас є перевірена відповідь на питання, і воно має 3 голоси. Перша (неповна) відповідь: 17. =]
Sniggerfardimungus

1
Можливості перейменування обмежені, оскільки ви зможете лише скорочувати імена . Також у Linux вам буде важко оновлювати хеш-таблиці ELF.
югр

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()яке дозволяє програмно обробляти динамічні бібліотеки. Якщо вам не потрібні дві функції одночасно, ви можете відкрити першу бібліотеку, використати першу функцію та закрити першу бібліотеку перед використанням другої бібліотеки / функції.


Дякую. Не думав про це. Хоча, я хотів би мати обидва одночасно.
qeek

Що робити, якщо я хотів би використовувати обидва одночасно?
QZHua

@QZHua: Інші програми захисту (наприклад, із перейменуванням символів) повинні вирішити вашу проблему.
mouviciel

6

Якщо у вас там є .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.

4

Ця проблема є причиною того, що c ++ має простори імен. Насправді не є чудовим рішенням в c для 2 сторонніх бібліотек, що мають однакову назву.

Якщо це динамічний об'єкт, ви можете явно завантажити спільні об'єкти (LoadLibrary / dlopen / etc) і викликати його таким чином. Крім того, якщо вам не потрібні обидві бібліотеки одночасно в одному коді, ви можете зробити щось із статичним посиланням (якщо у вас є файли .lib / .a).

Звичайно, жодне з цих рішень не стосується всіх проектів.


1
О, так. На це загальне запитання це здається гарною відповіддю. Однак - простори імен - це круто, якщо ви компілюєте все разом в одному компіляторі. Ура, зіткнення без імен. Але якщо ви отримали бібліотеку у двійковому вигляді і хочете інтегрувати її з іншим компілятором, то - удачі. Правила керування іменами в об'єктних файлах - це лише перша перешкода (зовнішній "C" може допомогти, що скасовує ефект простору імен).
Томаш Гандор

3

Лаятися? Наскільки мені відомо, ви не можете багато чого зробити, якщо у вас є дві бібліотеки, які виставляють точки зв’язку з однаковим ім’ям, і вам потрібно пов’язати обидві.


12
Присягається безумовно, перший крок. У цьому немає сумнівів.
dmckee --- екс-модератор кошеня

1
"ти не можеш багато чого зробити" - це все ще актуально? Інші відповіді дають безліч різних рішень.
югр

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. Чи проходить длопен процес пошуку зіткнень? Якщо цього не сталося, OP може просто завантажити обидві бібліотеки вручну і призначити нові імена всім функціям, які надають його бібліотеки.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.