2009-10-24 13 views
62

Piszę dość dużą bibliotekę obiektów współdzielonych w C++ i natknąłem się na mały problem, który sprawia, że ​​debugowanie bólu:Łatwe sprawdzanie nierozwiązanych symboli w udostępnianych bibliotekach?

Jeśli zdefiniuję funkcję/metodę w pliku nagłówkowym i zapomnę utworzyć skrót do to (podczas programowania), ponieważ buduję jako bibliotekę obiektów wspólnych, a nie jako plik wykonywalny, podczas kompilacji nie pojawiają się żadne błędy mówiące mi, że zapomniałem wprowadzić tę funkcję. Jedyny sposób, aby dowiedzieć się, że coś jest nie tak, jest w czasie wykonywania, kiedy ostatecznie aplikacja łącząca się z tą biblioteką przewraca się z błędem "niezdefiniowanego symbolu".

Szukam łatwego sposobu sprawdzenia, czy mam wszystkie symbole potrzebne w czasie kompilacji, być może coś, co mogę dodać do mojego pliku Makefile.

Jednym z rozwiązań, które udało mi się wymyślić, jest uruchomienie skompilowanej biblioteki przez nm -C -U, aby uzyskać demangowaną listę wszystkich nieokreślonych odniesień. Problem polega również na tym, że pojawia się lista wszystkich odniesień znajdujących się w innych bibliotekach, takich jak GLibC, które oczywiście będą połączone razem z tą biblioteką po złożeniu ostatecznej aplikacji. Możliwe byłoby użycie wyjścia z nm do grep przez wszystkie moje pliki nagłówkowe i zobaczenie, czy któraś z nazw odpowiada ... ale to wydaje się szalone. Z pewnością nie jest to rzadkość i istnieje lepszy sposób na jej rozwiązanie?

+1

'nm -C -u' uratował mnie wiele razy! (zauważ małymi literami "-u" w moim systemie.) Pozostawiając ten komentarz tutaj, aby móc go znaleźć następnym razem, kiedy go potrzebuję. – dpritch

Odpowiedz

73

Zapoznaj się z rozwiązaniem łącznik -z defs/--no-undefined. Podczas tworzenia obiektu współużytkowanego spowoduje to niepowodzenie łącza, jeśli istnieją nierozwiązane symbole.

Jeśli używasz gcc powołać łącznika, można użyć kompilatora -Wl opcję, aby przejść do opcji linkera:

gcc -shared ... -Wl,-z,defs 

Jako przykład rozważmy następujący plik:

#include <stdio.h> 

void forgot_to_define(FILE *fp); 

void doit(const char *filename) 
{ 
    FILE *fp = fopen(filename, "r"); 
    if (fp != NULL) 
    { 
     forgot_to_define(fp); 
     fclose(fp); 
    } 
} 

teraz, jeśli budować, że do obiektu udostępnionego, będzie to sukces:

> gcc -shared -fPIC -o libsilly.so silly.c && echo succeeded || echo failed 
succeeded 

Ale jeśli dodać -z defs, link nie powiedzie się i opowiedzieć o swojej brakującej symbolem:

> gcc -shared -fPIC -o libsilly.so silly.c -Wl,-z,defs && echo succeeded || echo failed 
/tmp/cccIwwbn.o: In function `doit': 
silly.c:(.text+0x2c): undefined reference to `forgot_to_define' 
collect2: ld returned 1 exit status 
failed 
+2

+1 ta odpowiedź może pomóc facetom, którzy przybyli z tła Windows, w którym * DLLs * zewnętrzne symbole muszą zostać rozwiązane podczas kompilacji. Sposób na ładny fragment kodu! –

+0

@ShmilTheCat: Cześć, Próbowałem wstawiania -Wl, -z, defs w moim pliku make, nadal otrzymuję niezdefiniowany błąd symbolu podczas działania, ale nie podczas kompilacji. Co mogę zrobić?. W moim scenariuszu mam dwa foldery jeden w drugim, (powiedzmy, A/B, i, e B jest w środku A), i widzę kilka symboli lub B w "libA.so". Nie jestem pewien, czy każdy symbol w B jest dostępny w "libA.so". Jak się upewnić? – Vinay

+0

@ShmilTheCat Aby jeszcze bardziej przypominać bibliotekę DLL systemu Windows, możesz dodać '-Bsymbolic' do linii poleceń linkera. Nawet jeśli 'forgot_to_define' teraz istnieje w bibliotece dzięki sprawdzeniu' -z', to plik wykonywalny może nadal przesłonić go swoją własną definicją, a własne definicje biblioteki przejdą do tego nadpisania; '-Bsymbolic' wymusza rzeczy tak, aby własne definicje biblioteki spełniały jej funkcje. – Kaz

7

Co z zestawem testowym? Tworzysz fałszywe pliki wykonywalne, które łączą się z symbolami, których potrzebujesz. Jeśli połączenie nie powiedzie się, oznacza to, że twój interfejs biblioteki jest niekompletny.

3

Raz miałem ten sam problem. Opracowywałam model komponentu w C++ i oczywiście komponenty powinny ładować się dynamicznie w środowisku wykonawczym. Przyszły mi do głowy trzy rozwiązania: te, które zastosowałem:

  1. Poświęć trochę czasu na zdefiniowanie systemu kompilacji, który jest w stanie skompilować statycznie. Stracisz trochę czasu na jego inżynierię, ale zaoszczędzi ci to wiele czasu na złapaniu tych denerwujących błędów w czasie wykonywania.
  2. Grupuj swoje funkcje w dobrze znanych i zrozumiałych sekcjach, dzięki czemu możesz grupować funkcje/kody pośredniczące, aby mieć pewność, że każda odpowiednia funkcja ma swój skrót. Jeśli poświęcisz sporo czasu na udokumentowanie tego, możesz napisać skrypt sprawdzający definicje (np. Poprzez swoje komentarze doxygen) i sprawdzić odpowiedni plik .cpp.
  3. Wykonaj kilka testowych plików wykonywalnych, które wczytują ten sam zestaw bibliotek i określ flagę RTLD_NOW na dlopen (jeśli jesteś w * NIX). Będą sygnalizować brakujące symbole.

Nadzieję, że pomaga.

+0

na liniach RTLD_NOW, czy istnieje env var, aby wymusić natychmiastowe (nie leniwe) powiązanie? –

+1

tak. tutaj jest LD_BIND_NOW (libc5; glibc od 2.1.1) Jeśli ustawiony na niepusty łańcuch, powoduje, że dynamiczny linker rozwiązuje wszystkie symbole podczas uruchamiania programu, zamiast odraczania funkcji resolval wywołania funkcji do punktu, w którym są one po raz pierwszy przywoływane. Jest to przydatne w przypadku korzystania z debuggera. –

+0

Może to być również przydatne do celów OP: LD_WARN (tylko ELF) (glibc od 2.1.3) Jeśli ustawiony na niepusty łańcuch, ostrzec o nierozwiązanych symboli. –

8

W systemie Linux (którego wydaje się, że się używasz) ldd -r a.out powinien dać ci dokładnie taką odpowiedź, której szukasz.

UPDATE: trywialny sposób, aby utworzyć a.out przeciwko którym do sprawdzenia:

echo "int main() { return 0; }" | g++ -xc++ - ./libMySharedLib.so 
ldd -r ./a.out 
+3

To prawie dokładnie to samo co nm -C -U, które zasugerowałem w oryginalnym poście. Problem polega na tym, że nie ma aplikacji "a.out", o której można by mówić, jest to biblioteka współdzielona, ​​więc ldd -r mylibrary.so daje całą masę danych wyjściowych, ponieważ używa symboli z różnych innych dynamicznych bibliotek. Jestem szczególnie zainteresowany w brakujących symbolach, które są zdefiniowane w * moich * plikach nagłówkowych, a nie w bibliotekach zewnętrznych. –

Powiązane problemy