GCC: statycznie linkowane C++ dla systemów wbudowanych

Do tej pory wszystkie moje programy dla urządzeń wbudowanych (ang. embedded systems) pisałem w C – po pierwsze dlatego, że były raczej małe, a po drugie, łatwo jest skonstruować odpowiedni zestaw narzędzi do kros-kompilacji (ang. toolchain). Tym razem jednak pracuję nad projektem dość mocno rozbudowanym i stwierdziłem, że warto napisać to w C++, którego kod jest znacznie łatwiej zarządzalny (a planuję dość długi jego cykl życia, z kolejnymi wersjami). Zbudowałem więc cały nowy toolchain (innym razem opiszę, jak to samemu zrobić) GCC z przeznaczeniem dla procesora ARM9, działającym z zestawem instrukcji armv4t, z systemem Linux z jądrem 2.6.x, BusyBox i biblioteką uClibc. Na marginesie, powiem wam, że bardzo mi się ta platforma spodobała, umożliwia przeprowadzenie naprawdę bardzo ciekawych projektów za rozsądne pieniądze.

Wszystko byłoby pięknie, gdyby nie to, że okazało się, iż zlinkowanie statyczne biblioteki standardowej C++ (libstdc++) jest zadaniem wcale nietrywialnym… Biblioteka ta w zasadzie w normalnym, codziennym użytkowaniu powinna być linkowana dynamicznie, ale w przypadku dystrybuowania programu na system o całkiem innej specyfikacji, niż maszyna, na której przeprowadza się budowanie aplikacji, najlepszym i najbezpieczniejszym wyjściem jest właśnie linkowanie statyczne. Okazuje się jednak, że używając normalnych metod, to się nie udaje… Wszelkie normalne metody sprowadzały się do dwóch efektów: albo używaliśmy g++ i wtedy okazywało się, że tak naprawdę libstdc++ było nadal zlinkowane dynamicznie, pomimo usilnych starań, albo używaliśmy gcc i wtedy owszem, biblioteka była poprawnie zlinkowana, ale kod wynikowy na maszynie docelowej powodował jedynie komunikat „Illegal instruction”. Dziesiątki dziwnych kombinacji opcji i flag dla linkera, zero pozytywnych efektów. Po dwóch dniach walki byłem gotów na przepisanie w połowie gotowego programu w C i zarzucenie idei z C++.

Aż w końcu… ostatnia próba, po niej miałem zamiar już przepisać kod od nowa w C. Skoro gcc generował niepoprawny kod C++ dla platformy ARM, należało użyć g++, jednak pozostawał problem, jak tego zmusić do statycznego linkowania – wciąż ignorował wszelkie moje próby w tym kierunku.

Stworzyłem w katalogu ze źródłami symlink do statycznej wersji biblioteki standardowej, prawidłowy plik wskazał mi sam g++:

ln -s `arm-unknown-linux-uclibcgnueabi-g++ -print-file-name=libstdc++.a`

teraz, mając w katalogu źródeł taki symlink, w Makefile dałem:

LDFLAGS = -v -static-libgcc -L.

make clean; make; make install (kiedyś też napiszę wam, jaką mam metodę na prostą instalację programu wynikowego na urządzeniu docelowym) – niemożliwe! Działa!! 🙂 wszystko pięknie i gładko – wymuszenie statycznego linkowania biblioteki pomocniczej libgcc spowodowało wymuszenie statycznego linkowania libstdc++, a że w ścieżce bibliotek (opcja -L.) była tylko statyczna wersja tej biblioteki, nie dołączył dodatkowo wersji dynamicznej, co do tej pory uparcie czynił. Rozwiązanie banalne, ale jakże nieintuicyjne i nieudokumentowane… cóż, życie 😉 Ważne, że działa.

Życzę wszystkim udanych projektów wbudowanych 🙂