Co powinienem zrobić, jeśli dwie biblioteki udostępniają funkcję o tej samej nazwie, powodując konflikt?

94

Co powinienem zrobić, jeśli mam dwie biblioteki, które udostępniają funkcje o równoważnych nazwach?

qeek
źródło
2
czy są to biblioteki statyczne czy połączone dynamicznie?
Alnitak
potrzebujemy więcej szczegółów ... czy te nazwy zostały wyeksportowane? czy są używane tylko wewnętrznie? Czy możesz zmienić imiona?
Johannes Schaub - litb
Obie są ze sobą dynamicznie połączone. Nie mogę zmienić nazw, ponieważ nie jestem właścicielem bibliotek.
qeek
Świetne pytanie. Oczywiście, że nie byłoby problemu z tymi bibliotekami, jeśli wszystkie symbole zostały poprzedzone unikalnym identyfikatorem (np vorbis_..., sf_..., sdl_...). To jest zasadniczo to, co C ++ robi z nazwami symboli dla funkcji w przestrzeni nazw.
Vortico
Jest to bardzo interesujące pytanie, ale niestety zbyt nieprecyzyjne, co jest przyczyną zbyt wielu zbyt szerokich odpowiedzi.
yugr

Odpowiedzi:

52
  • Jeśli kontrolujesz jedną lub obie: edytuj jedną, aby zmienić nazwę i ponownie skompilować Lub równoważnie zobacz odpowiedzi Bena i nieznanego , które będą działać bez dostępu do kodu źródłowego.
  • Jeśli nie kontrolujesz żadnego z nich, możesz zawinąć jeden z nich. To znaczy skompilować inną ( połączoną statycznie !) Bibliotekę, która nie robi nic poza ponownym eksportowaniem wszystkich symboli oryginału z wyjątkiem obraźliwego, do którego można dotrzeć poprzez opakowanie z alternatywną nazwą. Co za kłopot.
  • Dodane później: Ponieważ qeek mówi, że mówi o bibliotekach dynamicznych, rozwiązania sugerowane przez Ferruccio i mouviciela są prawdopodobnie najlepsze. (Wydaje się, że żyję w dawnych czasach, kiedy statyczne połączenie było domyślne. To koloruje moje myślenie.)

A propos komentarzy: Przez „eksport” mam na myśli pokazanie modułów łączących się z biblioteką - odpowiednik externsłowa kluczowego w zakresie pliku. Sposób kontrolowania tego zależy od systemu operacyjnego i konsolidatora. I zawsze muszę na to patrzeć.

dmckee --- kociak byłego moderatora
źródło
To była moja pierwsza myśl, ale czy nie skończysz z tym samym problemem kolizji? Ostatecznie cały projekt musi zostać połączony - w czasie kompilacji / linkowania lub w czasie wykonywania - w którym to momencie obie biblioteki naruszające zasady muszą ładować się tak, jak są.
Sniggerfardimungus
@unknown: opakowanie musi być skompilowane za pomocą statycznego połączenia i nie powinno eksportować obraźliwego symbolu. Wtedy nadal możesz dynamicznie łączyć opakowanie. Zredagowano dla większej jasności, dzięki.
dmckee --- kociak byłego moderatora
Jeśli problem qeek dotyczy bibliotek ddl, a nie bibliotek statycznych, jak można stworzyć nową bibliotekę z opakowaniem? Ponieważ biblioteka opakowująca musiałaby dynamicznie zawijać funkcję w bibliotece, z którą nie chcesz łączyć się w pierwszej kolejności.
jeffD
@dmckee - co rozumiesz przez „eksport”?
4
może ktoś mógłby podać prosty przykład tej techniki? Jeden plik exe, dwie biblioteki, każda zawierająca jedną funkcję o tej samej nazwie.
53

Istnieje możliwość zmiany nazw symboli w pliku obiektowym za pomocą objcopy --redefine-sym old=new file (zobacz man objcopy).

Następnie po prostu wywołaj funkcje, używając ich nowych nazw i połącz się z nowym plikiem obiektowym.

Ben
źródło
2
Ładny. Dodanie tego do pliku Makefile byłoby trywialne. Gdyby biblioteki były kiedykolwiek aktualizowane, inkantacja objcopy byłaby znacznie łatwiejsza do zaktualizowania niż niektóre inne rozwiązania.
sigjuice,
9
Nie zapomnij również zmienić nazwy symboli w plikach nagłówkowych.
mouviciel
^ sed / awk / perl przydałoby się również do zautomatyzowania zmiany nazw symboli w nagłówku
Alex Reinking
16

W systemie Windows możesz użyć LoadLibrary (), aby załadować jedną z tych bibliotek do pamięci, a następnie użyć GetProcAddress (), aby uzyskać adres każdej funkcji, którą musisz wywołać, i wywołać funkcje za pomocą wskaźnika funkcji.

na przykład

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);

pobierze adres funkcji o nazwie bar w foo.dll i wywoła ją.

Wiem, że systemy Unix obsługują podobną funkcjonalność, ale nie mogę wymyślić ich nazw.

Ferruccio
źródło
dlopen dlsym, i dlclose. Jednak hermetyzacja w systemie Unix może nie być tak skuteczna, jak w systemie Windows.
user877329
8

Oto myśl. Otwórz jedną z naruszających bibliotek w edytorze szesnastkowym i zmień wszystkie wystąpienia naruszających ciągów na coś innego. Powinieneś wtedy móc używać nowych nazw we wszystkich przyszłych połączeniach.

AKTUALIZACJA: Właśnie to zrobiłem w tym celu i wydaje się, że działa. Oczywiście nie przetestowałem tego dokładnie - może to być nic więcej niż naprawdę dobry sposób na odstrzelenie nogi za pomocą strzelby hekseditowej.

Sniggerfardimungus
źródło
właściwie nie jest to straszne rozwiązanie. Trochę hackerskie, ale wszystko, co robisz, to zmienianie ciągów w tabeli symboli. Nie ma w tym żadnej prawdziwej szkody funkcjonalnej.
Evan Teran,
Prawdopodobnie chciałbyś również zmienić nazwę biblioteki - aby nie pojawił się ktoś inny, próbując ponownie załadować tę rzecz. Przeszedłbyś od jednego konfliktu do dziesiątek lub setek. =] Uwielbiam to, jeśli chodzi o stackoverflow: mamy przetestowaną odpowiedź na pytanie, która ma 3 głosy. Pierwsza (niekompletna) odpowiedź: 17. =]
Sniggerfardimungus
1
Możliwości zmiany nazwy są ograniczone, ponieważ możesz tylko skrócić nazwy . Również w Linuksie trudno będzie zaktualizować tablice skrótów ELF.
yugr
8

Jeśli masz tam pliki .o, dobra odpowiedź tutaj: https://stackoverflow.com/a/6940389/4705766

Podsumowanie:

  1. objcopy --prefix-symbols=pre_string test.o aby zmienić nazwy symboli w pliku .o

lub

  1. objcopy --redefine-sym old_str=new_str test.o aby zmienić nazwę określonego symbolu w pliku .o.
Jee lee
źródło
7

Zakładając, że używasz Linuksa, musisz najpierw dodać

#include <dlfcn.h>

Zadeklaruj zmienną wskaźnika funkcji w odpowiednim kontekście, na przykład

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

Jak Ferruccio podał na https://stackoverflow.com/a/678453/1635364 , załaduj jawnie bibliotekę, której chcesz użyć, wykonując (wybierz swoje ulubione flagi)

void* dlhandle;
void* sym;

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

Przeczytaj adres funkcji, którą chcesz wywołać później

sym = dlsym(dlhandle, "conflicting_server_init");

przypisać i obsadzić w następujący sposób

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

Zadzwoń podobnie jak oryginał. Na koniec zwolnij, wykonując

dlclose(dlhandle);
vraa
źródło
6

Nie powinieneś ich używać razem. O ile dobrze pamiętam, w takim przypadku linker wystawia błąd.

Nie próbowałem, ale to rozwiązanie może być z dlopen(), dlsym()i dlclose()które pozwalają obsłużyć programowo bibliotek dynamicznych. Jeśli nie potrzebujesz tych dwóch funkcji w tym samym czasie, możesz otworzyć pierwszą bibliotekę, użyć pierwszej funkcji i zamknąć pierwszą bibliotekę przed użyciem drugiej biblioteki / funkcji.

mouviciel
źródło
Dzięki. Nie myślałem o tym. Chociaż chciałbym mieć oba w tym samym czasie.
qeek
A co jeśli chciałbym używać obu jednocześnie?
QZHua
@QZHua: Inne odpowiedzi (np. Dotyczące zmiany nazwy symboli) powinny rozwiązać twój problem.
mouviciel
4

Ten problem jest powodem, dla którego C ++ ma przestrzenie nazw. Nie ma dobrego rozwiązania w c dla 2 bibliotek innych firm o tej samej nazwie.

Jeśli jest to obiekt dynamiczny, możesz jawnie załadować udostępnione obiekty (LoadLibrary / dlopen / etc) i wywołać je w ten sposób. Alternatywnie, jeśli nie potrzebujesz obu bibliotek w tym samym czasie w tym samym kodzie, możesz zrobić coś z łączeniem statycznym (jeśli masz pliki .lib / .a).

Oczywiście żadne z tych rozwiązań nie dotyczy wszystkich projektów.

Brian Mitchell
źródło
1
O tak. Na to ogólne pytanie wydaje się, że jest to dobra odpowiedź. Jednak - przestrzenie nazw są fajne, jeśli kompilujesz wszystko razem w tym samym kompilatorze. Brawo, nie ma konfliktów nazw. Ale jeśli masz bibliotekę w formie binarnej i chcesz ją zintegrować z innym kompilatorem, to powodzenia. Reguły zniekształcania nazw w plikach obiektowych to tylko pierwsza przeszkoda (może pomóc extern "C", który cofa efekt przestrzeni nazw).
Tomasz Gandor
3

Przysięgać? O ile mi wiadomo, niewiele możesz zrobić, jeśli masz dwie biblioteki, które ujawniają punkty dowiązań o tej samej nazwie i musisz połączyć je z obydwoma.

Watyna
źródło
12
Przysięga to zdecydowanie pierwszy krok. Nie ma wątpliwości.
dmckee --- kociak byłego moderatora
1
„niewiele można zrobić” - czy to nadal aktualne? Inne odpowiedzi dostarczają wielu różnych rozwiązań.
yugr
2

Wokół jednego z nich należy napisać bibliotekę opakowującą. Twoja biblioteka opakowań powinna udostępniać symbole o unikalnych nazwach, a nie symbole o nieunikalnych nazwach.

Inną opcją jest zmiana nazwy funkcji w pliku nagłówkowym i zmiana nazwy symbolu w archiwum obiektów biblioteki.

Tak czy inaczej, aby użyć obu, będzie to hack.

James Caccese
źródło
1

Pytanie zbliża się do dekady, ale cały czas pojawiają się nowe wyszukiwania ...

Jak już odpowiedziałem, objcopy z flagą --redefine-sym to dobry wybór w Linuksie. Zobacz na przykład https://linux.die.net/man/1/objcopy aby uzyskać pełną dokumentację. Jest to trochę niezgrabne, ponieważ zasadniczo kopiujesz całą bibliotekę podczas wprowadzania zmian, a każda aktualizacja wymaga powtórzenia tej pracy. Ale przynajmniej powinno działać.

W przypadku systemu Windows dynamiczne ładowanie biblioteki jest rozwiązaniem i trwałym rozwiązaniem, takim jak alternatywa dlopen w systemie Linux. Jednak zarówno dlopen (), jak i LoadLibrary () dodają dodatkowy kod, którego można uniknąć, jeśli jedynym problemem są zduplikowane nazwy. W tym przypadku rozwiązanie Windows jest bardziej eleganckie niż podejście objcopy: po prostu powiedz konsolidatorowi, że symbole w bibliotece są znane pod inną nazwą i użyj tej nazwy. Jest kilka kroków, aby to zrobić. Musisz utworzyć plik def i podać tłumaczenie nazwy w sekcji EKSPORT. Zobacz https://msdn.microsoft.com/en-us/library/hyx1zcd3.aspx (VS2015, ostatecznie zostanie zastąpiony przez nowsze wersje) lub http://www.digitalmars.com/ctg/ctgDefFiles.html(prawdopodobnie trwalsze) dla pełnych szczegółów składni pliku def. Proces polegałby na utworzeniu pliku def dla jednej z bibliotek, a następnie wykorzystaniu tego pliku def do zbudowania pliku lib, a następnie połączeniu z tym plikiem lib. (W przypadku bibliotek DLL systemu Windows pliki lib są używane tylko do łączenia, a nie wykonywania kodu). Zobacz Jak utworzyć plik .lib, jeśli mają plik .dll i plik nagłówkowy. dla procesu budowania pliku lib. Tutaj jedyną różnicą jest dodanie aliasów.

Zarówno w systemie Linux, jak i Windows zmień nazwy funkcji w nagłówkach biblioteki, której nazwy są aliasowane. Inną opcją, która powinna działać, byłaby w plikach odwołujących się do nowych nazw, #define stara_nazwa nowa_nazwa, # uwzględnienie nagłówków biblioteki, której eksport jest aliasowany, a następnie #undef stara_nazwa w programie wywołującym. Jeśli biblioteka korzysta z wielu plików, łatwiejszą alternatywą jest utworzenie nagłówka lub nagłówków, które zawijają definicje, dołączenia i undefs, a następnie użycie tego nagłówka.

Mam nadzieję, że te informacje były pomocne!

Jim Monte
źródło
0

Nigdy nie używałem dlsym, dlopen, dlerror, dlclose, dlvsym itp., Ale patrzę na stronę podręcznika systemowego i podaje przykład otwarcia libm.so i wyodrębnienia funkcji cos. Czy dlopen przechodzi przez proces wyszukiwania kolizji? Jeśli tak się nie stanie, OP może po prostu załadować obie biblioteki ręcznie i przypisać nowe nazwy wszystkim funkcjom, które zapewniają jego biblioteki.

Sniggerfardimungus
źródło