Manchmal sind Programme wie der GCC selbst für gestandene Entwickler ein Rätsel. Nehmen wir ein beliebiges, einfaches, ggf. etwas größeres Projekt, dessen C-Source zu compilieren ist. Nimmt man jetzt noch an, dass dieses Projekt mehrere Module beinhalten kann, so bieten sich ja eigentlich Libraries an. Handelt es sich dabei um ein Projekt, bei dem dynamisches Linken nicht möglich ist, so sind sogar .a-Dateien (Object File Archives) eine gangbare Alternative – könnte man denken. Aber gut. Selbst als gestandener Entwickler wird man immer wieder von seinen Tools überrascht.
So ging es zumindest mir die letzten Tage, während ich an einem etwas größeren Projekt gearbeitet habe, bei dem ich zur Modularisierung meine Sources in einzelne Verzeichnisse gepackt und dann jedes Verzeichnis in eine .a-Datei verpackt habe. Das ist auch weiter nix, was unter klassische Rocket Science gehört, aber immerhin ein etwas exzentrischer Ansatz, Libraries zu nutzen ohne wirklich Libraries zu verwenden. Das ist aber auch weniger mein Punkt.
Da ich für die einzelnen Module eine gewisse Austauschbarkeit haben wollte und zudem Namespaces nutzen wollte, fiel mein Blick auf den Linker, mit dessen Hilfe Symbolnamen dynamisch beim Linken umgebogen werden sollten. Und dieser Satz ist bewusst im Konjunktiv verfasst, da genau hier Murphy wieder einmal einen Auftritt hatte: Möchte man mit dem GCC, bzw. dem Linker die in einer Objektfile referenzierten Symbole (also Variablen und Funktionsnamen), die z.B. nach dem Schema LibName_Task_ImplVer_Function aufgebaut sind an externe Objektfiles binden, die von der spezifischen Implementierung unabhängige Namen nutzen (also LibName_Task_Function), so wäre der übliche Weg eigentlich die Verwendung von ld –def-symbol generischerName=spezifischerName …
Wie gesagt ließen sich also alle C-Sources reibungslos übersetzen, was an sich schon einmal ein recht großer Erfolg war, woran es dann scheiterte, war mir mehr oder wenigerfraglich: Während des Linkens versuchte LD zwar Abhängigkeiten zu finden, ignorierte aber beim Einbinden von neuen Archivdateien großzügig die Abhängigkeiten, die durch Symbole, die via Kommandozeile referenziert wurde. Zum Umschreiben von Funktionsnamen und somit der Nutzung von Namespaces taugt LD demnach nicht.
Wäre zumindest das Problem bzgl. dem Linken der Programmbestandteile geklärt. Folge war zwar, dass ich eine Reihe von Sources in bezug auf die verwendeten Bezeichner etwas refactoren musste, was sich aber in Grenzen hielt und recht zügig erledigt war.
Nun fand LD zwar alle Symbole, konnte aber dennoch seine eigene Libraries nicht finden. Um den Weg hier abzukürzen, genügte es, statt LD direkt aufzurufen, die Kommandozeile vom GCC zusammenstellen zu lassen, was nur wenige Modifikationen erforderte. Was zumindest bei mir nun (GCC 3.4.2) zu einem weiteren Problem führte: Möchte man ein Programm mit einer Befehlszeile wie
gcc -o executable.bin archive1.a archive2.a archive3.a archive4.a
linken, ignoriert der GCC jegliche Archive, solange er nicht zumindest einmal eine O-Datei gesehen hat, in der das main-Symbol aufgetaucht ist. Dass man bei dieser Art einige Archive doppelt angeben muss, mag ja vielleicht noch einzusehen sein, dass er aber jegliche Archive ignoriert, solange nichtt explizit die Object File angegeben wurde, in der der Programmeinsprungspunkt auftaucht, ist vollständig unlogisch, da Archive eigentlich wie normale Object-Files behandelt werden sollten. Ein Aufruf der Form
gcc -o executable.bin main.o archive1.a archive2.a archive3.a archive4.a
funktioniert somit, selbst wenn main.o bereits in einem der Archive enthalten ist. Mit Logik hat das zumindest nur wenig zu tun …
Ob sich dieses Verhalten ändert, wenn man -Wl,-u -Wl,main als Parameter mitgibt, habe ich aber nicht mehr probiert. Ich vermute hier aber einmal, dass dies den GCC genauso wenig beeindruckt, wie rein die Angabe von A-Dateien, ggf. mit dem Unterschied, dass er beim Linken jegliche Boot-Symbole vergessen wird.