28 lip 2010

"undefined reference" - jak zdiagnozować takie błędy?

Jednym z bardziej irytujących błędów jakie możemy otrzymać kompilując projekt jest błąd linkowania "undefined reference".

np.:
example.cpp: undefined reference to `boost::program_options::option_description::option_description()'

Jeśli błąd dotyczy naszego kodu, to pół biedy. Wyświetlane w komunikatach nazwy coś nam mówią. Wiemy, co ostatnio modyfikowaliśmy w kodzie.
Gorzej jeżeli linker nie może znaleźć obiektu z używanej bibioteki, w dodatku jej wywołania znajdują się w innej używanej bibliotece. Wtedy to nazwy poszukiwanych przez linker symboli niewiele nam mówią.

Poniżej znajduje się kilka porad i narzędzi użytecznych podczas analizy błędów "undefined reference".

1. Należy się upewnić, że linkujemy w projekcie bibliotekę zawierającą funkcję poszukiwaną przez linker.

W powyższym przypadku musimy sprawdzić, czy wywołanie g++ (gcc) zawiera parametr:
g++ ... -lboost_program_options

Jeżeli linkujemy odpowiednią bibliotekę a mimo to nadal mamy błąd "undefined reference", to:

2. Sprawdzamy, czy w linkowanej biblioteece faktycznie znajduje się poszukiwany symbol.
readelf -s W /usr/lib/libboost_serialization.so

Powyższe wywołanie readelf listuje wszystkie symbole jakie znajdują się w dynamicznej bibliotece.

Niestety symbole listowane przez readelf są udekorowane (ang. name mangling), a linker pokazuje nazwy symboli z języka C++ bez dekoracji.

Na szczęście istnieje w Linuxie narzędzie do zamiany dekorowanej nazwy na deklarację C++: c++filt.

Możemy użyć następującego zestawu instrukcji do zamiany dekoracji na identyfikatory C++ dla wszystkich symboli w danej bibliotece:
readelf -s -W /usr/lib/libboost_program_options.so | awk '$8 != "" { print $8; }' | c++filt

Teraz możemy odszukać symbolu w bibliotece.

Jeżeli go tam znajdziemy, to zapewne g++ podczas linkowania używa tej biblioteki w innej wersji (czyli używa innego pliku niż ten, który badaliśmy za pomocą readelf). Należy sprawdzić ścieżki podawane jako parametr -L.

Jeżeli symbolu nie znajdziemy, to prawdopodobnie do linkowania użyte zostały headery w innej wersji niż linkowana biblioteka. W takim przypadku może pomóc reinstalacja biblioteki.