F
@hustbaer sagte in Tortoise Git, Dateien und Icons:
Probier nochmal puts statt printf. Ich würde mich nicht wundern wenn's damit nochmal kleiner wird.
Ich meine mich zu erinnern dass der Hauptgrund warum Echsen von Miniprogrammen mit modernen Compilern so gross werden der ist, dass es da kreuz und quer Dependencies beim ganzen IO Zeugs gibt, inklusive Locale Support. Ich vermute dass man das besser entkoppeln könnte, wenn man gezielt auf kleine Programme optimieren will. Nur dass zahlt sich bei Runtime Libs für moderne Compiler halt kaum aus mMn. - und daher wundert es mich nicht dass ein "Hello World" Programm vergleichbar riesig wird.
Das ist jetzt hoffentlich noch kein "Nekroposting", aber ich habe gerade wieder etwas an meinem Hobbyprojekt gearbeitet, und ein printf-basiertes "Hallo Welt" mittlerweile auf 13.440 Bytes herunter bekommen.
Das beinhaltet:
Loader mit etwa 4KiB: Speicher detektieren über BIOS Memory Map, simpler Allocator für "Prozesspeicher" der vom BIOS reservierte Bereiche berücksichtigt, Interrupt Thunks für transparente Verwendung von INT-Instruktionen zu Real Mode Interrupts aus dem Protected Mode und umgekehrt, Programm-Argumente parsen und argv-Array erzeugen, Implementation OS-spezifischer Funktionen für die (pico)libc (sbrk, open, close, read, write, getenv, etc.), Protected Mode Setup und Laden des Hauptprogramms (vom Linker-Skript an die 16-bit DOS .exe "angehängt") in erweiterten Speicher.
Stack Unwinder der libgcc für C++ Exception Handling.
malloc- und printf-Implementierungen der (pico)libc.
das eigentliche "Hallo Welt"-Programm
Dafür sind knapp 13 KiB echt okay finde ich
Mit <iostreams> in Form von std::cout oder std::println sind es dann etwa 650 KiB (war vorher doppelt so viel), weil wie du richtig schreibst, eine Menge Funktionen und Objekte mit herein gezogen werden, auch wenn sie nicht benötigt werden. Da sind dann z.B. so Funktionen drin wie (mal willkürlich ein paar aus der Linker map herauskopiert):
Klassen wie std::__cxx11::time_get<wchar_t, std::istreambuf_iterator<wchar_t, std::char_traits<wchar_t> > mit ihren Daten- und Funktions-Membern in verschiedensten Spezialisierungen (Template-Bloat?).
std::num_get<char, std::istreambuf_iterator<char, std::char_traits<char> >
std::money_get<char, std::istreambuf_iterator<char, std::char_traits<char> >
std::istreambuf_iterator<wchar_t, std::char_traits<wchar_t> >
Und das, obwohl ich nur einen fixen String ausgebe (das da oben sind ja alles Input-Funktionen).
Ich habe den Verdacht, dass sich std::format eventuell besser entkoppeln lässt. Das ist nicht auf <iostreams> angewiesen und kann auch in einen std::output_iterator (Concept) ausgeben. Ich werde damit mal was experimentieren. Vielleicht sind Ranges und std::output_iterator-Objekte ja eine gute Alternative, wenn man "leichtgewichtige Streams" benötigt. Eine Klasse, die das Konzept erfüllt, ließe sich recht leicht für die Standardausgabe implementieren und ein eigenes println ist dank std::format_to dann auch ein Kinderspiel.
Eine vielleicht interessante Erkenntnis hab ich noch gemacht, was die Größe der Binary aus C++-Code angeht: Exception- und RTTI-Informationen aus statischen Bibliotheken können durchaus auch mit -fno-exceptions und -fno-rtti noch in der Binary landen, wenn die Bibliothek selbst nicht ebenfalls mit diesen Flags kompiliert wurde. Ich habe daher Multilib-Kombinationen der Runtimes für diese gebaut (libstdc++ und mein Loader), die dann gelinkt werden, wenn die Binary mit diesen Flags gebaut wird. Das war letztendlich der Durchbruch, um das printf-"Hallo Welt" von über 200KiB auf die jetzige Größe zu bringen.
Auch habe ich -ffunction-sections und -fdata-sections sowie -Wl,--gc-sections fest in der Toolchain verdrahtet. Damit erzeugt der Compiler für jede Funktion und jedes Objekt eine eigene Section in der Objektdatei, da der Linker (ohne LTO) nur ganze Sections verwerfen kann, wenn diese nicht referenziert werden. Compiler Flags wie -fvtable-gc helfen auch beim eliminieren nicht-verwendeter virtueller Funktionen und Konfigurations-Flags für die libstdc++ wie --disable-libstdcxx-verbose. Letzteres führt zu weniger ausführlichen Fehlermeldungen der Standardbibliothek (kürzere Strings) und (sehr wichtig) dass kein C++-Demangler dazu gelinkt wird.
Wie ich schonmal erwähnte, sobald ich die Kanten alle abgeschliffen hab, werd' ich das mal für Fans der Retroprogrammierung veröffentlichen. Idee ist, dass man modernes C++ hat und direkt mit den "coolen" Sachen wie Mode 13h loslegen kann, ohne sich mit dem ätzenden Kram wie Real-Mode Speichersegmentierung und anderen Details auseinandersetzen zu müssen