Warum lauen Binary Pakete, welche in C anstatt C++ geschrieben sind nahezu auf jeder beliebigen Linux Distribution?
-
Korrektur:
FlipFlop schrieb:
Nicht ganz.
Bei C Code Binarys genügt es, wenn die Versionsnummer genau die gleiche oder höher ist,
dann läuft das Programm.Damit meine ich die Versionsnummer der Bibliotheken.
-
Die C++ ABI wurde öfters mal geändert. Daher ist es wichtig darauf zu achten, dass die Libs die gleiche ABI nutzen.
-
Dafür gibt es mehrere Gründe:
- Die wichtigsten C APIs sind älter und stabiler als die C++ APIs
- In C++ werden auch die Parameter einer Funktion im Funktionssymbol kodiert.
Aus einem void func(int a, double b) wird das C Symbol "func" und das C++ Symbol _Z4funcid. Änderungen an den Parametern sorgen somit schon dafür, dass ein Symbol nicht aufgelöst werden kann. Unter C kann man aber einige sichere Änderungen machen, so dass auch alte Programme weiter laufen. Es laufen aber auch Programme weiter, wenn unsichere Änderungen gemacht wurden. Daraus können dann Fehler entstehen, die schwer zu finden sind. - Das C++ ABI hat sich beim GCC bei nahezu jeder Version in den letzten größeren Releases verändert. Zum ABI gehören zum Beispiel die Kodierung im letzten Punkt und die Art wie man Funktionsparameter übergibt. Mit einem neuen ABI werden damit übersetzte Programme nicht mit Libs einer alten ABI laufen.
-
kingruedi schrieb:
Die C++ ABI wurde öfters mal geändert. Daher ist es wichtig darauf zu achten, dass die Libs die gleiche ABI nutzen.
Und dieses Problem gibt es bei C Programmen nicht?
-
Und warum hat man die C++ ABI geändert?
Bzw. wann hört das auf, bzw. wann ist sie so stabil, daß man sich auf diese verlassen kann?
-
Weil man eingesehen hat, dass die alte kaputt war (genaueres findest Du im Archiv der gcc-mailingliste[n]).
Die aktuelle Version wurde jetzt allerdings schon ueber 3 gcc-Versionen beibehalten (3.4, 4.0, 4.1), sieht also ziemlich stable aus.
-
Bei C++ Programmen besteht auch das Problem des sogenannten 'name manglings'.
Es bestimmt, wie ein Name (z.b. Namespace, Klasse, Funktion, ...) im
eompilierten Endprodukt repräsentiert wird. So macht mein gcc-3.4 z.b. aus 'std::runtime_error' ein 'St13runtime_error'.
Es gibt allerdings keinen Standard, der besagt WIE dieser Name umgewandelt werden soll, was bedeutet, dass eine Library die vom Compiler A erstellt
wurde, wohl kaum mit einer anderen die Compiler B erstellt hat, linken wird,
da die Symbole nicht aufgelöst werden können.In C Programmen ist das ganze viel einfacher, da es ja ansich nur Funktionen gibt, und diese auch nicht überladbar sind. Daher ergibt sich dieses Problem nicht. So heißt 'printf' auch intern 'printf'.
In C++ lässt sich das name-mangling bei Funktionen verhindern, allerdings kann eine solche Funktion nicht überladen werden! Dies ist vorallem dann hilfreich,
wenn man dynamische Bibliotheken (unix: .so, win32: .dll) laden will, da hierfür der Symbolname bekannt sein muss.
Hier ein kleines beispiel:extern "C" { void foo(int i=10) { cout << "i = " << i << endl; } }
In diesem Fall erhält die Funktion den namen "foo".
Ohne 'extern "C"' wäre der Name bei mir
allerdings "_Z3fooi".Es ist also eindeutig ersichtlich, dass die C ABI eine
wesentlich einfachere ist, als jene des C++.Wenn irgendetwas falsch sein sollte, sebbo@reflex.at.
-
sebbothebutcher schrieb:
Bei C++ Programmen besteht auch das Problem des sogenannten 'name manglings'.
Es bestimmt, wie ein Name (z.b. Namespace, Klasse, Funktion, ...) im
eompilierten Endprodukt repräsentiert wird. So macht mein gcc-3.4 z.b. aus 'std::runtime_error' ein 'St13runtime_error'.
Es gibt allerdings keinen Standard, der besagt WIE dieser Name umgewandelt werden soll, was bedeutet, dass eine Library die vom Compiler A erstellt
wurde, wohl kaum mit einer anderen die Compiler B erstellt hat, linken wird,
da die Symbole nicht aufgelöst werden können.Vielen dank für die umfangreiche Antwort, das erklärt einiges.
Aber jetzt habe ich doch eine Frage, wieso standardisiert man das dann nicht?
Das ist doch ein ziemlich übler Zustand so wie das gerade ist.
-
Wie wurde das bei C# und Java gelöst?
Gibt es dort dieses Problem auch?
-
Also bei Java (und bestimmt auch bei C#) gibt es diese Problematik nicht, da es in dem Sinne auch keine Libs gibt. Es gibt zwar in Java JARs (Java Archive) in denen die Class-Dateien (vergleichbar mit Objekt-Dateien aus C++, naja...), aber letztendlich sind diese JARs eigentlich eher mit DLLs oder SOs (so heißen die dynamsichen Libs glaub ich unter Linux).
Bei Java gibts aber das Problem solange nicht, wie die Java-Versionen nicht inkompatibel sind. Also JARs die mit Java1.5 compiliert sind, laufen auch nicht auf einer Java1.4-VM. Also, irgendwo hört halt auch der Spaß auf.
Übrigens, verstehe ich das in diesem Thread genannte Problem nicht wirklich. OK, ich bin jetzt nicht so Linux-bewandert, vielleicht deshalb. Aber ist es tatsächlich so, das z.B. ein C++-Binary nur auf einer bestimmten Distri läuft, obwohl alle Libs statisch gelinkt wurden?
-
Java und C# haben im Prinzip das gleiche Problem. Die Programme sind ja auch von unterschiedlichen Laufzeitumgebungen abhängig.
@Artchi
Mit statischem linken sollte alles klappen
-
Aber jetzt habe ich doch eine Frage, wieso standardisiert man das dann nicht?
Genau das tut man. Und genau das ist der Grund, warum sich das ABI im GCC bewegt (hat?). Jetzt hat hoffentlich alles seinen (standardisierten) Platz gefunden.
Und nun zu Java (mit C# kenne ich mich nicht aus).
Wenn ich mit Java Objekte Instantiiere, tue ich das immer auf dem Heap. In Java hat man eigentlich nie das Objekt selbst, sondern nur eine Referenz. Daher muß der Aufrufer zur Laufzeit nicht wirlich wissen, wie groß das Objekt ist. Verändert man später die Bibliothek (die Jar im Falle von Java), ist das kein Problem. Die Referenz zeigt halt einfach auf einen grössere Speicherbereich.
Funktionen sind in Java grundsätzlich virtuell und werden immer über eine Sprungtabelle aufgerufen (wird zwar zur Laufzeit unter Umständen optimiert, aber das Prinzip bleibt gleich).
In C++ ist das alles ein wenig anders. Instantiiere ich ein Objekt auf dem Stack, muß der Aufrufer zur Compilezeit wissen, wie viele Bytes es dort reservieren muß. Dafür muß er also alle Membervariablen kennen (auch die privaten). Ändere ich meine Membervariablen, kann sich die Größe ändern, was zu Binärinkompatibilität führt.
Memberfunktionen können in C++ virtuell sein, können aber auch nicht-virtuell oder sogar inline sein. Bei virtuellen Membern ist die Reihenfolge wichtig, da diese zur Laufzeit nur per offset in der Sprungtabelle aufgerufen werden. Bei Inline-Membern ist das besonders kritisch, da diese direkt per offset auf die Variablen zugreifen. Das bedeutet, daß schon die Änderung der Reihenfolge der Membervariablen eine Binärinkompatibilität nach sich zieht.
Der Vorteil von Java an der Stelle ist klar. Dafür hat C++ mehr Möglichkeiten, den Code zur Compilezeit zu optimieren. Das spart Speicher und Laufzeit.
Gruß
Tommi
-
tntnet schrieb:
Genau das tut man. Und genau das ist der Grund, warum sich das ABI im GCC bewegt (hat?). Jetzt hat hoffentlich alles seinen (standardisierten) Platz gefunden.
Die haben sich doch nur an den Compiler von Intel angepaßt.
Mehr ist da doch gar nicht passiert, oder etwa doch?Also so den richtigen Binary Standard gibt es immer noch nicht.
-
An den Intel Compiler? Nein! Die derzeitige C++ ABI ist eine GCC eigen Entwicklung, die wohl im Zusammenhang mit dem IA64 Port entstanden ist
-
Ich glaub Hmmmmmmm meint einen ISO-Standard bzgl. ABI. Und den gibt es ja tatsächlich nicht, sondern nur den (wie von dir gesagt) vom GCC.
-
Artchi schrieb:
Ich glaub Hmmmmmmm meint einen ISO-Standard bzgl. ABI. Und den gibt es ja tatsächlich nicht, sondern nur den (wie von dir gesagt) vom GCC.
Ich bezog mich ja auch darauf, dass der GCC an den ICC angepasst wurde. Was aber nicht stimmt und eher andersrum der Fall ist. Das es keinen ISO Standard gibt ist ja klar. Das ist einfach viel zu Platform bezogen. Es gibt ja auch keinen ISO Standard für die C ABI
-
Also hier wird diese ganze Problematik sehr ausführlich anhand von Beispielen beschrieben:
http://en.wikipedia.org/wiki/Name_decoration
(einen Deutschen Wiki Eintrag habe ich leider nicht gefunden, falls den jemand finden sollte dann nur her damit)Und da ist deutlich erkennbar (siehe die Tabelle), daß man beim Versionssprung des GCC von 2.x auf 3.x sich an den Intel Compiler angepaßt hat.
Genaugenommen steht das sogar auch so da:On IA-64, a standard ABI exists (see external links), which defines (among other things) a standard name-mangling scheme, and which is used by all the IA-64 compilers. GNU GCC 3.x, in addition, has adopted the name mangling scheme defined in this standard for use on other, non-Intel platforms.
Und von einer echten Standardisierung kann man leider auch nicht sprechen, oder spielt etwa Borland und Microsoft mit?