Piszę dość dużą bibliotekę współdzielonych obiektów C ++ i napotkałem mały problem, który sprawia, że debugowanie jest uciążliwe:
Jeśli zdefiniuję funkcję / metodę w pliku nagłówkowym i zapomnę utworzyć dla niej kod pośredniczący (podczas programowania), ponieważ tworzę jako współdzieloną bibliotekę obiektów, a nie plik wykonywalny, nie pojawiają się żadne błędy w czasie kompilacji, informujące mnie, że mam zapomniał zaimplementować tę funkcję. Jedyny sposób, w jaki dowiaduję się, że coś jest nie tak, to w czasie wykonywania, kiedy w końcu aplikacja łącząca się z tą biblioteką wypada z błędem „niezdefiniowany symbol”.
Szukam łatwego sposobu, aby sprawdzić, czy mam wszystkie potrzebne symbole w czasie kompilacji, być może coś, co mogę dodać do mojego pliku Makefile.
Jednym z rozwiązań, które wymyśliłem, jest uruchomienie skompilowanej biblioteki w nm -C -U
celu uzyskania odszyfrowanej listy wszystkich niezdefiniowanych odniesień. Problem polega na tym, że pojawia się również lista wszystkich odniesień, które znajdują się w innych bibliotekach, takich jak GLibC, do których oczywiście zostanie dołączony link wraz z tą biblioteką podczas składania ostatecznej aplikacji. Byłoby możliwe użycie wyjścia nm
do grep
przez wszystkie moje pliki nagłówkowe i sprawdzenie, czy którakolwiek z nazw odpowiada… ale to wydaje się szalone. Z pewnością nie jest to rzadki problem i istnieje lepszy sposób jego rozwiązania?
źródło
nm -C -u
uratował mnie wiele razy! (zwróć uwagę na małe litery-u
w moim systemie). Pozostawię ten komentarz tutaj, abym mógł go znaleźć następnym razem, gdy będę go potrzebował.Odpowiedzi:
Sprawdź opcję konsolidatora
-z defs
/--no-undefined
. Podczas tworzenia obiektu współużytkowanego spowoduje to, że łącze nie powiedzie się, jeśli istnieją nierozwiązane symbole.Jeśli używasz gcc do wywołania konsolidatora, użyjesz
-Wl
opcji kompilatora , aby przekazać opcję do konsolidatora:Jako przykład rozważ 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 wbudujesz to we współdzielony obiekt, to się powiedzie:
Ale jeśli dodasz
-z defs
, łącze zawiedzie i poinformuje Cię o brakującym symbolu:> 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
źródło
-Bsymbolic
do wiersza poleceń konsolidatora. Nawet jeśliforgot_to_define
teraz istnieje w bibliotece dzięki-z
sprawdzeniu, plik wykonywalny może nadal przesłonić go swoją własną definicją, a własne definicje biblioteki przejdą do tego nadpisania;-Bsymbolic
wymusza to, aby własne definicje biblioteki dotyczące jej funkcji trafiały do jej funkcji.// forgot_to_define(fp);
to zrobię nie zgłasza błęduW systemie Linux (którego prawdopodobnie używasz)
ldd -r a.out
powinien dać ci dokładnie odpowiedź, której szukasz.UPDATE: trywialny sposób tworzenia,
a.out
na podstawie którego można sprawdzić:echo "int main() { return 0; }" | g++ -xc++ - ./libMySharedLib.so ldd -r ./a.out
źródło
A co z zestawem testowym? Tworzysz pozorowane pliki wykonywalne, które odsyłają do potrzebnych symboli. Jeśli łączenie się nie powiedzie, oznacza to, że interfejs biblioteki jest niekompletny.
źródło
Miałem kiedyś ten sam problem. Pracowałem nad modelem komponentów w C ++ i oczywiście komponenty powinny ładować się dynamicznie w czasie wykonywania. Przychodzą mi na myśl trzy rozwiązania, które zastosowałem:
Mam nadzieję, że to pomoże.
źródło