Hallo,
ich möchte eine Shared-Lib bauen und alle ungenutzten Funktionen vom
Compilier- bzw. Link-Vorgang rausnehmen.
Bei einem normalen Programm kann sich GCC & LD an der main()
runterhangeln, und so ungenutzte Funktionen erkennen und rausschmeißen.
Doch wie mache ich das bei einer Shared-Lib?
Hier weiß die Toolchain ja erstmal nicht, welche Funktionen später
genutzt werden, und welche nicht.
Ich muss also einzelne Funktionen als Einsprungpunkt, Quasi-main(),
markieren können.
Wie mache ich das? Ich welche GCC & LD Flags muss ich setzen?
Danke
Hi,
Das solltest du gar nicht machen, da es eine shared lib ist, kann es ja
auch sein, dass die auch von einem anderem Programm genutzen werden
soll.
Wenn das dann Sachen aufrufen möchte, die du rausgeworfen hast, stürzt
es ab.
Marcus schrieb:> Das solltest du gar nicht machen, da es eine shared lib ist, kann es ja> auch sein, dass die auch von einem anderem Programm genutzen werden> soll.> Wenn das dann Sachen aufrufen möchte, die du rausgeworfen hast, stürzt> es ab.
Das sind berechtigte Sorgen, aber ich weiß was ich mache.
Ist ja auch nicht so wichtig, warum das in diesem speziellen Projekt
notwendig ist.
Jörg W. schrieb:> Du nimmst einfach mal nur die rein, die du später auch brauchen> wirst …
Ja, das wäre die alternative Methode.
Alles, bis auf benötigten Funktionen auskommentieren, und bei Errors
Stück für Stück wieder einkommentieren.
Da ich faul bin, möchte ich aber, das GCC & LD das für mich übernehmen.
olpo schrieb:> Das sind berechtigte Sorgen, aber ich weiß was ich mache.
Ja nee, ist klar...
Was ist für dich eine Shared lib?
Denn üblicherweise versteht man darunter eine lib, erst zur Laufzeit vom
Programm geladen geladen wird. Unter diesen Umständen ist deine Frage
daher völlig sinnlos. Was also willst du wirklich?
Oliver
olpo schrieb:> Alles, bis auf benötigten Funktionen auskommentieren, und bei Errors> Stück für Stück wieder einkommentieren.
Das ist die trial&error-Methode.
Die beste Methode wäre ein sauberes Konzept …
olpo schrieb:> Das sind berechtigte Sorgen, aber ich weiß was ich mache.
Na offensichtlich nicht, sonst wuerdest Du hier nicht fragen.
Eine shared Lib wird ja ueberhaupt nicht einkompiliert, sondern das
fertige Programm weiss nur, welche Routine in welcher Lib benutzt werden
muss. Bei der Installation des Programms wird ggf. ueberprueft, ob es
die fragliche Lib auf dem System gibt.
wendelsberg
Im Embedded Bereich muss man halt auch mal hacken.
Die Lib. ist viel zu groß für die Plattform und hat ewig viele
Abhängigkeiten, die sich nicht so einfach quer-kompilieren lassen.
Ich weiß auch genau, welche Programme die Lib. nutzen werden.
Das klingt hässlich, ist mir klar.
Aber es muss ja nicht jeder Thread in eine Grundsatzdiskussion ausarten.
olpo schrieb:> Die Lib. ist viel zu groß für die Plattform
Wenn du eine shared lib hast, hast du doch vermutlich auch ein
System, welches virtual memory unterstützt. Dann ist das eigentlich
egal, denn es wird (halt mit Granularität ganzer Speicherseiten) ja
nur das in den Speicher geladen, was auch gebraucht wird.
> und hat ewig viele> Abhängigkeiten, die sich nicht so einfach quer-kompilieren lassen.
Gut, das ist ein anderes Ding.
> Ich weiß auch genau, welche Programme die Lib. nutzen werden.
Auf einem unixoiden System könnte man mit objdump -R analysieren,
welche dynamischen Symbole diese Programme benutzen. Damit kannst
du dir dann eine Liste anfertigen lassen.
Und warum nutzt man da eine shared Lib?
Der normale Weg waere doch, (wenn die Lib nicht fuer andere Programme
gebraucht wird, was der Fall zu sein scheint) die benoetigten Funktionen
fest einzukompilieren.
Und da baut der gcc nur das ein was er fuer noetig haelt.
wendelsberg
olpo schrieb:> Das sind berechtigte Sorgen, aber ich weiß was ich mache.> Ist ja auch nicht so wichtig, warum das in diesem speziellen Projekt> notwendig ist.
Dann versteht nur leider keiner, was du willst und kann dir somit keine
sinnvolle Antwort geben.
> Jörg W. schrieb:>> Du nimmst einfach mal nur die rein, die du später auch brauchen>> wirst …>> Ja, das wäre die alternative Methode.> Alles, bis auf benötigten Funktionen auskommentieren, und bei Errors> Stück für Stück wieder einkommentieren.
Warum hast du überhaupt Funktionen reingemacht, die du nicht brauchst?
> Da ich faul bin, möchte ich aber, das GCC & LD das für mich übernehmen.
Und das programmübergreifend? Schließlich ist der Zweck einer Shared Lib
ja, von mehreren Programmen genutzt zu werden. Oder hast du nur ein
einziges Programm? Dann wäre die Frage, wozu überhaupt eine Shared Lib.
Irgendwie ergibt das alles keinen Sinn.
Meines Wissens kann man zur Linkzeit des main Programms nur nicht
benötigte libraries automatisch entfernen lassen. Der Linker darf aus
naheliegenden Gründen nicht beim linken des main Programms die
angezogene shared library modifizieren. Da beim linken der shared
library die Anforderungen des main Programms unbekannt sind kann der
linker auch hier nichts optimieren.
Der einzige Weg aus der Misere ist der vorhin genannte, nämlich allen
code (inkl des codes, der ursprünglich in der shared library war) in ein
einziges executable zu kompilieren und linken. Dann haben compiler &
linker alle Informationen um die unnötigen Teile zu eliminieren.
The D. schrieb:> Der einzige Weg aus der Misere ist der vorhin genannte, nämlich allen> code (inkl des codes, der ursprünglich in der shared library war) in ein> einziges executable zu kompilieren und linken.
Und sei es nur testhalber – wenn man sich dabei ein Mapfile vom
Linker anlegen lässt, kann man anhand dessen rausfusseln, was aus
der Bibliothek tatsächlich notwendig war. Basierend auf dieser
Information könnte man eine passende shared lib zurechtdengeln.
Ich kann olop schon verstehen. Angenommen jemand baut sich eine
Pluginschnittstelle um Module dynamisch laden zu können. un weiter
angenommen das Hauptprogramm benötigt nur 3 Funktionen aus dem Plugin:
init(), run(), deinit().
Wenn ich unter Windows eine dll schreibe, sorge ich dafür, dass diese 3
Funtionen als "__declspec(dllexport)" gekennzeichnet werden. Damit weiß
der Linker, aus dieser dll brauche ich außer diesen 3 Funktionen nur
noch die die davon abhängig sind. Wie macht man sowas einem gcc unter
Linux klar?
temp schrieb:> Ich kann olop schon verstehen. Angenommen jemand baut sich eine> Pluginschnittstelle um Module dynamisch laden zu können. un weiter> angenommen das Hauptprogramm benötigt nur 3 Funktionen aus dem Plugin:> init(), run(), deinit().> Wenn ich unter Windows eine dll schreibe, sorge ich dafür, dass diese 3> Funtionen als "__declspec(dllexport)" gekennzeichnet werden. Damit weiß> der Linker, aus dieser dll brauche ich außer diesen 3 Funktionen nur> noch die die davon abhängig sind. Wie macht man sowas einem gcc unter> Linux klar?
Das Problem ist doch, dass die shared library schon fertig gebaut als
binary file vorliegt und komplett in den Speicher geladen werden muss,
auch wenn nur 3 Symbole daraus vom main Programm benutzt werden. Besser
wäre es, wenn die monolithische library in mehrere kleine shared libs
zerlegt werden würde und main nur die eine benötigte lib mit den
notwendigen Symbolen anziehen könnte.
Alternativ ist es vielleicht möglich, eine static library zu bauen und
zum main Programm zu linken. Ich bin da allerdings nicht der Experte.
The D. schrieb:> Das Problem ist doch, dass die shared library schon fertig gebaut als> binary file vorliegt und komplett in den Speicher geladen werden muss,
Das ist Dein Denkfehler. Aus der Shared Lib wird nur das geladen, was
auch läuft. Macht jedes pagende Linux so.
Das Problem des TO ist überhaupt kein Problem. Ich verstehe auch nicht,
was der ganze Zirkus soll.
Ist das Programm das einzige, was die Lib benutzt, kann er sie statisch
dazulinken. Ist das Programm nicht das einzige, muss er sich abfinden,
dass die Lib auch genutzt wird.
Einerseits weiß der TO nicht, was sein Programm aus der Lib so alles
braucht, aber er gibt vor, genau zu wissen, was die anderen Programme
von der Lib benötigen.
Das passt alles nicht zusammen.
Uijuijui, warum immer diese Grundsatzdiskussionen?
Das Bauprozess ist kompliziert, und bin zu faul mich hier hier lang und
breit zu rechtfertigen, warum ich welche Lösung bevorzuge.
Das ist mein Problem:
Ich baue eine Shared Library und möchte alle Funktionen rausschmeißen,
die ich später nicht brauche.
Man muss der Toolchain also irgendwie sagen, welche Symbole genutzt
werden.
Wer weiß, wie das geht, bitte melden.
olpo schrieb:> Wer weiß, wie das geht, bitte melden.
Es geht nur, indem du sie gar nicht erst einbaust. Eine shared
lib ist ein fix und fertig gelinktes Objekt, aus dem kannst du
(anders als aus einer statischen Library) nicht nachträglich
irgendwas wieder entfernen – genausowenig, wie du aus einer
ausführbaren Datei noch was entfernen könntest.
Hi Jörg,
ich denke, hier gibt's ein Verständnis-Problem.
Wir reden wohl aneinander vorbei.
Jörg W. schrieb:> Es geht nur, indem du sie gar nicht erst einbaust.
Und genau das kann GCC doch.
C_FLAGS = -ffunction-sections -fdata-sections
LD_FLAGS = --gc-sections
Damit scheißt GCC alle Funktionen raus, die von main() nie erreicht
werden können.
Und ich vermute, dass das beim Bau einer Shared-Lib ebenfalls möglich
ist. Man müsste GCC nur beibringen, welche Funktion er hier als
quasi-main() betrachten soll.
Andere Funktionen, die von dort nicht erreicht werden können: Raus!
olpo schrieb:> ich denke, hier gibt's ein Verständnis-Problem.
Das hast du voll erfasst. Zu Shared libs gibt es kein Main. Die sind
dafür gedacht, beliebig vielen und vor allen verschiedenen Programmen
die Funktionalität zur Verfügung zu stellen, die der Programmierer in
sie hineinschreibt. Vom Konzept her ist es daher völlig sinnlos, von
dieser Funktionalität beim linken irgend etwas wieder zu entfernen.
Beim statischen Linken wirft der Linker die ungenutzten Funktionen raus.
Das wäre auch die einzige Variante, die dir eventuell weiterhilft. Bau
halt eine statisch gelinkte Version des Programms, ohne die Lib
dazuzulinken. Der Linker wird dir dann sagen, welche Funktionen er
vermisst.
Die wirst du dann von Hand in der Shared Lib suchen müssen.
Automatisch geht es nicht.
Oliver
olpo schrieb:> Und genau das kann GCC doch.
Nein, kann er nicht.
Wurde dir jetzt schon x-mal erklärt, aber du willst es offensichtlich
nicht wahrhaben.
Das, was du dafür missbrauchen willst, ist wohl ursprünglich mal dazu
geschaffen worden, bei C++ zwangsweise generierte Sachen (wie default
constructors) durch den Linker eliminieren zu lassen, wenn dieser
am Ende bemerkt, dass irgendwas niemals referenziert wird. Für eine
shared lib müsste er dann aber alles rauswerfen, denn in dem Moment,
da der Linker diese baut, wird ja rein gar nichts referenziert.
olpo schrieb:> Jörg W. schrieb:>>> Und genau das kann GCC doch.>>>> Nein, kann er nicht.>> http://www.mikrocontroller.net/articles/GCC:_unbenutzte_Funktionen_entfernen
Nur, weil da einer die Überschrift etwas unglücklich (oder für das
Zielpublikum passend) formuliert hat, macht es das deshalb nicht
korrekt.
Aber du hast ja sowieso deine vorgefertigte Meinung und alle, die dir
sagen, dass das so nicht geht, wissen halt bloß nicht, wie's geht.
Damit kann ich dir leider nicht mehr helfen.
Wenn die ganze Riege der Stänkerer (mittlerweile samt Mod) mal
wenigstens versuchen würde zu verstehen, was OP will, wäre allen
geholfen.
Seine Vorstellungen sind absolut nachvollziehbar.
Kann man unter Linux (bzw. eher bei ELf) wirklich keine Unterscheidung
haben, welche Funktionen exportiert sind?
Bei PE ist das kein Problem, und eben dafür wird __dllexport genutzt.
Und wenn die möglichen Einsprungpunkte damit klar sind, kann auch normal
gestrippt werden. Konzeptionell, ob das unter Linux/ELF geht, weiß ich
nicht.
Dieses ganze Gemäkele von wegen "kann alles aufgerufen werden" ist
entweder ein Verständnisproblem von euch oder eine Restriktion der
Linux-Gegebenheiten. In beiden Fällen wäre ein konzilianterer Ton
angebracht.
The D. schrieb:> Das Problem ist doch, dass die shared library schon fertig gebaut als> binary file vorliegt
Wie kommst du darauf? Er schrieb doch nun schon x-mal, daß er die shared
lib gerade erst bauen will. Und in der shared lib sollen nur benötigte
Symbole auftauchen.
Es geht überhaupt nicht um "ach guck mal, eine fremde Lib auf der
Platte, da soll alles weg, was mein Programm gerade nicht braucht."
Marc schrieb:> Kann man unter Linux (bzw. eher bei ELf) wirklich keine Unterscheidung> haben, welche Funktionen exportiert sind?
Auch, wenn etwas nicht exportiert ist: warum sollte man das entfernen
können? Es kann nur beim Import der DLL dann keiner drauf zugreifen,
aber die Module der DLL selbst können es natürlich noch. (Gut, hier
könnte der Linker natürlich solche Sachen rauswerfen, von denen er
an dieser Stelle bemerkt, dass sie am Ende nicht aufrufbar sind.)
Um das auf deine PE-Denkweise abzubilden: deklariere jedes globale
Symbol als "dllexport", baue eine DLL – dann erwartet er jedoch, dass
einzelne Teile davon, die in seiner späteren Applikation (die beim
Linken der DLL ja nicht bekannt ist) nicht benötigt werden, gar nicht
erst in der DLL drin sind.
Wie soll das gehen? Zu dem Zeitpunkt, da die DLL gebaut wird, sind
die potenziellen Konsumenten ja nicht bekannt.
Genau! An eben der Stelle soll der Linker rauswerfen!
Das ist genau das, wovon OP seit Anbeginn des Threads redet. War es das
nun wert, ihn so rüde anzugehen?
Und du hast es immer nch nicht verstanden. Nicht "alles als exportiert
markieren". Nicht "spätere Applikation". Es gibt noch überhaupt keine
Applikation!
Er baut nur eine Library. Er will gar nicht wissen, wer später diese Lib
nutzt. Aber er weiß, das er in seiner Library ggf. Dinge aus
Fremdcodestücken oder Debugcode oder Code für ganz andere Buildvarianten
drin hat, die überflüssig sind.
Genau das, warum man auch im Nicht-shared-lib-Fall --gc-sections
verwendet. Das ist mitnichten wegen irgendwelcher komischen C++-Dinge im
Linker unterstützt.
Marc schrieb:> Kann man unter Linux (bzw. eher bei ELf) wirklich keine Unterscheidung> haben, welche Funktionen exportiert sind?
Grad mal nachgelesen, ja, kann man dem Linker schon mitteilen.
Aber wie ich schon schrieb, ich denke nicht, dass das sein Problem
lösen würde. So, wie ich olpo verstanden habe, hat er eine Sammlung
von Objekten, die halt bspw. foo(), bar() und mumble() definieren.
Aus diesen baut er eine dynamische Bibliothek.
Zur Laufzeit hat er dann eine Applikation, die gegen diese Bibliothek
gelinkt ist, die daraus aber nur foo() braucht. Nun hätte er gern
die Bibliothek so gebaut, dass bar() und mumble() gar nicht erst drin
sind.
Das geht nur, indem man beim Bauen der Bibliothek bar() und mumble()
nicht nur nicht exportiert (das wäre dein Vergleich mit dem dllexport),
sondern indem man sie gar nicht erst in die Bibliothek hinein linkt.
Ja, machen kann man das, aber eben nicht direkt, sondern mit einem
der anderen genannten Vorschläge, indem man ermitteln lässt, was aus
dieser tatsächlich benötigt wird, um dann in einem zweiten Schritt
eine weitere dyamische Bibliothek zusammenbauen zu lassen, die nur
noch das gewünschte enthält.
Aber das gibt es nicht „aus der Dose raus“, das müsste man sich
zusammenscripten, was ich aber immer noch besser fände als die
eingangs genannte trial&error-Methode (erstmal alles auskommentieren
und dann gucken, worüber der Linker meckert).
Marc schrieb:> Er baut nur eine Library. Er will gar nicht wissen, wer später diese Lib> nutzt. Aber er weiß...
Schön , daß du zu wissen meinst, was der TO will. Er selber schreibt
allerdings was ganz anderes.
Oliver
Marc schrieb:> Genau das, warum man auch im Nicht-shared-lib-Fall --gc-sections> verwendet. Das ist mitnichten wegen irgendwelcher komischen C++-Dinge im> Linker unterstützt.
Meines Wissens ist das der Ursprung gewesen.
Ja, hier die Referenz dazu:
https://sourceware.org/ml/bfd/1998/msg00057.html
Dass es mittlerweile gern mis^H^H^Hbenutzt wird, um seinen Code nicht
extra aufräumen zu müssen, ist ein anderes Ding.
Oliver S. schrieb:> Marc schrieb:>> Er baut nur eine Library. Er will gar nicht wissen, wer später diese Lib>> nutzt. Aber er weiß...>> Schön , daß du zu wissen meinst, was der TO will. Er selber schreibt> allerdings was ganz anderes.>> Oliver
Komisch. Im Urpoating findet sich genau das. Nur eine shared lib, keine
Applikation. Daraufhin hast du ihn unsachlich angegriffen.
Ist mir egal, wie du dich nun windest. Ich kann dem OP inhaltlich nicht
helfen.
Aber es wäre schade, wenn Leute, die wissen wie es geht, vom Krakele der
Mehrheit inklusive Mod abgeschreckt würden oder deren abstruse
Interpretation sich einfach zu eigen machen.
Ich denke, ich habe das Problem klarer formuliert und wenn wer helfen
kann ist gut. Wenn nicht dann halt nicht.
Ich hin hier damit raus.
Marc schrieb:> Ich denke, ich habe das Problem klarer formuliert und wenn wer helfen> kann ist gut. Wenn nicht dann halt nicht.
Hast du nicht.
Der TO hat eine Shared Lib, die eine Menge Funktionen enthält und auch
exportiert. Er weiß (!!!), das seine Anwendung(en) nicht alle von der
Lib angebotenen Funktionen nutzt, und möchte, daß der Linker alle von
seiner Anwendung ungenutzten Funktionen der Lib automatisch entfernt. Da
geht es nicht um interne Überbleibsel von irgendwelchen Debug-Sessions,
sondern um offiziell vom Lib-Programmier vorgesehene Funktionen.
Und das geht halt nicht automatisch, weil es eben dem Sinn und Konzept
einer shared Lib völlig widerspricht.
Oliver
Ich hatte einmal ein Modulladesystem für einen meiner HTTP und WebSocket
Server, dort hatte ich ein Makrobasiertes system wo Shared Libraries als
Modul verwendet wurden und nur eine generierte init funktion exportiert
hat. Darüber wurden dann generierte C-Wrapperfunktionspointer
ausgetauscht, und diese wurden von generierten klassen aufgerufen.
Exceptions wurden auch abgefangen und dort durchgeschleust. Der sinn des
ganzen war eine c kompatible schnitstelle, ein sauberes Interface,
erweiterungsmögluchkeiten ohne verschedene Server/Modul versionen
inkompatiebel zu machen, etc.
Der springende punkt ist, die init funktion war der einzige
Einsprungspunkt (abgesehen von den default symbolen eines so), und code
der über diesen nicht erreichbar war war überflüssig. Es macht sinn
anzunehmen, dass man diesen code vom Linker automatisiert Isolieren und
entfernen lassen kann. Ich werde mal nach dem Ding suchen und etwas mit
dem "used" attribut herumspielen und ein par Compilerflags herumspielen,
um herauszufinden, ob dies möglich ist.
PS: Eventuell gibt es auch bei BusyBox etwas zu finden, die haben dort
konventionen und Dinge im makefile, um die Grösse zu vergleichen &
Optimieren.
Und jetzt machst du das ganze mit drei gleichwertigen und voneinander
unabhängigen exportierten Symbolen, wovon das aufrufende Programm aber
nur eins nutzt. Die anderen beiden sollen durch irgend eine Magie
automatisch entfernt werden. Das wäre das, was der TO will.
Oliver
Was spricht dagegen die library als static library mit
ffunction-sections zu bauen (und vielleicht sogar mit flto) und dann mit
gc-sections (und vielleicht sogar flto) zu linken? Das würde die
Forderungen des Threaderstellers vollumfänglich erfüllen (und im Falle
von LTO sogar übererfüllen).