Wie funktioniert inkrementelles Linken? (ld, gcc, objcopy)
-
Hallo,
ich versuche, mehrere Objektdateien zu einer zusammenzulinken. Die Objektdateien kommen nicht von mir. Dazu verwende ich inkrementelles Linken („-r“-Flag).
Dies tue ich für mehrere Bibliotheken. Die so erhaltenen o-Dateien archiviere ich in einer „libx.a“.
Wenn ich nun versuche, ein Programm unter Verwendung der libx.a zu kompilieren, dann erhalte ich diverse Fehler, weil Symbole doppelt definiert wurden. Wie werde ich diese los? Mein naiver Ansatz war, die Symbole einfach lokal zu machen, d.h. vor der Archivierung lasse ich auf jede inkrementell gelinkte o-Datei folgendes Kommando laufen:
objcopy -w -L "!foo*" meine-o-datei.o
Das Wildcard sorgt dafür, dass alle außer den für mich interessanten Symbolen privat gemacht werden. Die für mich interessanten Symbole kollidieren auch *nicht*, d.h. das ist kein Problem.
Leider erhalte ich jetzt trotzdem diverse Linker-Fehler, wenn ich versuche, die Bibliothek zu verwenden. Diese Fehler sehen dann folgendermaßen aus. Leider ist durch Googeln *nichts* nützliches zu finden, und die Doku schweigt sich auch aus.
`std::vector<node, std::allocator<node> >::_M_insert_aux(__gnu_cxx::__normal_iterator<node*, std::vector<node, std::allocator<node> > >, node const&)' referenced in section `.text' of libpizzachili.a(SSA-wrap.o): defined in discarded section
.text.\_ZNSt6vectorI4nodeSaIS0\_EE13\_M\_insert\_auxEN9\_\_gnu\_cxx17\_\_normal\_iteratorIPS0\_S2\_EERKS0_[std::vector<node, std::allocator<node> >::\_M\_insert\_aux(\_\_gnu\_cxx::\_\_normal_iterator<node*, std::vector<node, std::allocator<node> > >, node const&)]' of libpizzachili.a(SSA-wrap.o) \
std::vector<node, std::allocator<node> >::_M_insert_aux(__gnu_cxx::__normal_iterator<node*, std::vector<node, std::allocator<node> > >, node const&)' referenced in section `.text' of libpizzachili.a(SSA-wrap.o): defined in discarded section `.text._ZNSt6vectorI4nodeSaIS0_EE13_M_insert_auxEN9__gnu_cxx17__normal_iteratorIPS0_S2_EERKS0_[std::vector<node, std::allocator<node> >::_M_insert_aux(__gnu_cxx::__normal_iterator<node*, std::vector<node, std::allocator<node> > >, node const&)]' of libpizzachili.a(SSA-wrap.o)Was bedeutet das? Was kann ich damit anfangen?
Übrigens: Das ist doch schon vom Ansatz her vollkommen falsch, oder? Ich meine, es ist ja wohl nicht selten, dass Symbole aus unterschiedlichen Objektdateien kollidieren, da kann's ja wohl nicht sein, dass man die Binärdateien patchen muss (oder?). Wie wird da üblicherweise vorgegangen? Ich finde leider nichts dazu.
Mal ein Minimalbeispiel um zu demonstrieren, was ich meine:
//lib1.c int f(int a, int b) { return a + b; }
//lib2.c int f(int a, int b) { return a + b; }
// wrap1.cpp extern "C" int f(int, int); namespace wrap1 { int f(int a, int b) { return ::f(a, b); } }
// wrap2.cpp extern "C" int f(int, int); namespace wrap2 { int f(int a, int b) { return ::f(a, b); } }
// main.cpp int main() { int r1 = wrap1::f(1, 2); int r2 = wrap2::f(1, 2); }
Um das zum Laufen zu bringen, muss ich schon echt Verrenkungen anstellen, und dieses Beispiel klappt nur, weil die Bibliotheken keine C- oder C++-Standardfunktionen verwenden:
$ gcc -c lib1.c $ gcc -c lib2.c $ g++ -c wrap1.cpp $ g++ -c wrap2.cpp $ ld -r lib1.o wrap1.o -o wrap1new.o $ ld -r lib2.o wrap2.o -o wrap2new.o $ objcopy -L f wrap1new.o $ objcopy -L f wrap2new.o $ ar rc libwrap.a wrap?new.o $ g++ main.cpp libwrap.a
-
Ich habe inzwischen herausgefunden, dass es für einige Einsatzgebiete ausreicht, folgendermaßen vorzugehen:
- Erst jede Bibliothek mit ihrem Wrapper inkrementell linken (Parameter
-i
oder-i -x
), - dann alle resultierenden Objektdateien wieder inkrementell und mit der Option
-z muldefs
linken. Dadurch werden die doppelten Definitionen einfach ignoriert.
- Erst jede Bibliothek mit ihrem Wrapper inkrementell linken (Parameter
-
Noch etwas: Die von mir gepostete Fehlermeldung tritt komischerweise teilweise nur bei neuen GCC-Versionen (nach 4.1) auf und scheint damit zusammenzuhängen, dass in Version 4.2 die Standardheader leicht verändert wurden, z.B. wurde ein '#undef min' vor der Definition von 'std::min' entfernt. Das erklärt aber nicht die std::vector-Fehler.