Die Geschichte der Kursarbeit

Guten Tag. Ich möchte über meinen Kurs sprechen oder darüber, wozu Neugier führt.


Lange Zeit schreibe ich aus dem Nichts heraus Programme unter dem Symbian. Und von Zeit zu Zeit stieß ich bei der Montage auf seltsame Dinge. Alles deutete auf das Dienstprogramm elf2e32 hin. Seine Aufgabe ist es, die Eingabe-Binärdatei des Elf-Formats in eine andere zu konvertieren, die für das Symbian-e32-Image spezifisch ist. Ich war lange Zeit verwirrt von Neugier - wie funktioniert dieses Dienstprogramm überhaupt und warum ist es manchmal fehlerhaft? Wenig später begann mich eine andere Frage zu belästigen - das Thema der Kursarbeit =) Ich beschloss, Geschäft mit Vergnügen zu verbinden und lud den Quellcode herunter. Und los geht's ...


Das erste Commit wird nicht


Die zweite - wir schließen nicht standardmäßige gcc-Erweiterungen ein, fügen fehlende Klassen, Funktionen und Konstanten aus der Quelle hinzu. Das Thema sammelt sich freudig und fällt. Fortschritte jedoch. Wir beginnen unter dem Debugger - er ordnet den Debugger einer Klasse zu, die nur eine andere initialisiert, die die nächste initialisiert ... Hurra! Ein echtes Feature! Komm rein. Ups Wo sind wir? !!! Stoppen Sie den Debugger! Chirurg! Skalpell! Alkohol! Gurke! Klassen Anhang f Feuerraum! Geben Sie nullptr anstelle von NULL an! Wir haben C ++ 14! Wow, was für ein schrecklicher Konstruktor, um alles mit Nullen zu initialisieren! Und doch und immer wieder - aber bei uns fordert C ++ 14 eine Standardinitialisierung für Klassen! Was ist jetzt alles prägnant ...


Okay. Wir reparieren so viele wie möglich gleichzeitig. Ich habe herausgefunden, warum der Debugger wie ein Fit zum Quellcode springt - Aftars treffen ihre Köpfe bei der Abstraktion und erhöhen die Vererbung von 80 Level von der UseCaseBase-Klasse :) Dann flogen mir die Konstruktorklassen statischer Instanzen für die Message & ParameterManager-Klassen aus den Augen. Singleton Myers? Nein, nicht gehört. Firebox-Abstraktion! Viva revolucion !!! Viva POD !!!


Wow Wie interessant dieser Baum war. Die Hauptarbeit erledigt die Funktion BuildAll (). Wenn alle Parameter angegeben sind, sammelt die Funktion die Importbibliothek, eine Datei, in der die Namen der Funktionen und Variablen sowie die Reihenfolge angegeben sind, in der sie in der Importbibliothek und in der Binärdatei selbst verfügbar sind. Alle Nachkommen von UseCaseBase haben ihren Algorithmus durch Überladung geändert. Manchmal bereiten wir in den Nachkommen Hilfsdaten vor, aber meistens haben sie einfach die Erstellung einiger Dateien deaktiviert. Beispielsweise wird der Dateiname zum Zusammenstellen von etwas nicht angegeben - eine neue Klasse wird erstellt. Idioten. Es reicht aus, die Ausführung eines solchen Funktionskollektors bei Bedarf zu unterbrechen. Es ist leicht, meine Handlungen zu verstehen. B-)


Wir löschen weiterhin leere Klassen, ersetzen NULL durch nullptr_t und ersetzen Bereichsiteratoren durch for (auto x: *).


Wir beheben Fehler bei der Verarbeitung von Befehlszeilenparametern.


Der Code muss mit einem statischen Analysegerät überprüft werden. Wo soll ich anfangen? Hmm, unter XP ist die Auswahl klein - cppcheck, und Codeblock unterstützt es sofort. Wow, was für ein Haken! Es gibt sogar Löschen für char []! Verdammt, ich weiß, wo die Hälfte des freien Arbeitsspeichers geblieben ist =)


Also fügen wir die aus der elc-Datei libcrypto.dll generierten Dateien und die Datei selbst hinzu, die die Befehlszeilenparameter für ihre Erstellung beschreibt.


Ups CPPCheck war falsch ... Es muss (a || b) sein ...


Ich werde versuchen, Visual Studio 15 einzubauen, und Win10 würde bei einem Stick bleiben. Setzen Sie eine virtuelle Maschine ein. Fertig, laden Sie das Online-Studio-Installationsprogramm herunter und führen Sie es aus. Was? Möchte den Sprung in den freigegebenen Ordner mit dem Host nicht speichern ?! Ja, erstick dich! Laden Sie herunter, wo Sie unterrichtet wurden ... Und jetzt übertragen wir die heruntergeladenen Dateien in den Ordner und starten die Installation. Was? Ignoriert wieder freigegebenen Ordner ??! Ja, erstick dich! Werden Sie dort, wo Sie unterrichtet wurden ...


Im Prinzip raschelt ein Dutzend Brunnen auf einem Kern und 3 Gigs des Rahmens. Studio zu Studio! Nachdenklich, aber nicht lange. Wir öffnen mein Projekt im Studio und schwören wieder auf den Ordner ... Wie viel ist schon möglich? Ja, du würgst ... Wir sammeln, schwört auf nicht standardmäßige Erweiterungen STL hash_set. Fernbedienung. Fernbedienung ??? Schalten Sie das Gehirn ein =)
Wow was für ein eingängiger Code:


int ElfFileSupplied::UnWantedSymbolp(const char * aSymbol) { static hash_set<const char*, hash<const char*>, eqstr> aSymbolSet; int symbollistsize=sizeof(Unwantedruntimesymbols)/sizeof(Unwantedruntimesymbols[0]); static bool FLAG=false; while(!FLAG) { for(int i=0;i<symbollistsize;i++) { aSymbolSet.insert(Unwantedruntimesymbols[i]); } FLAG=true; } hash_set<const char*, hash<const char*>, eqstr>::const_iterator it = aSymbolSet.find(aSymbol); if(it != aSymbolSet.end()) return 1; else return 0; } 

Lass uns ein wenig nachdenken ... Und voila:


 int ElfFileSupplied::UnWantedSymbolp(const char * aSymbol) { int symbollistsize = sizeof(Unwantedruntimesymbols) / sizeof(Unwantedruntimesymbols[0]); for (int i = 0; i<symbollistsize; i++) { if (strstr(Unwantedruntimesymbols[i], aSymbol)) return 1; } return 0; } 

Meine Schönheit ...


Warum löst das Programm eine Ausnahme aus, wenn dieses Flag falsch oder gar nicht gesetzt ist? Warum bist du so grausam, wunderschön in der Ferne ... Lass uns einfach diese Flagge auf einen sicheren Wert zurücksetzen. Und diese Flagge wäre auch schön ... Und dies und das und das. Oder ist es vielleicht besser, es in eine separate Funktion zu stellen? Gute Idee! Nennen wir es ParameterManager :: CheckOptions ()!


Ein Schritt nach links ist ein Sturz, ein Schritt nach rechts ist eine nicht erfasste Ausnahme, ein Sprung an Ort und Stelle - zumindest nicht BSOD =)


Langweilig ... Störungen und Krümmung ...


Olya-la !!! CleanUpStack Symbian auf STL emulieren?:
Grundsätzlich nichts Besonderes:


 std::vector<char*> cleanupStack; 

Reinigung:


 std::vector<char*>::iterator aPos; char *aPtr; aPos = cleanupStack.begin(); while( aPos != cleanupStack.end() ) { aPtr = *aPos; delete[] aPtr; ++aPos; } 

Ein heller Kopf anstelle von links / rechts verwendet l / r. Danke cppcheck.


Ah, faul vor dem Monitor die Cppcheck-Protokolle zu zerlegen ... Was wird uns der Github bieten? .. Codacy ... Wir verbinden das Projekt ... Ich dachte ein wenig und es ist fertig! Jetzt können Sie die Erfolgsbotschaften im Kampf gegen Fehler auf der Couch lesen ^^


Es scheint also nicht fehlerhaft zu sein ... Sammeln wir etwas, zum Beispiel libcrypto.dll. Es funktioniert, obwohl die unkomprimierte Datei hundert Bytes mehr enthält als die vom Dienstprogramm aus dem SDK erstellte. Als Nächstes werden Binärdateien, die mit dieser Version des Dienstprogramms und aus dem SDK erstellt wurden, ständig verglichen. Befehlszeilenoptionen sind für sich identisch.
Dackel, wo bekommt man das Diff-Analog für Binärdateien? Hmm, hol das Drehbuch auf den Kolben. Zu viele Informationen - Sie brauchen etwas viel Einfacheres. Pdf / djvu Erkennung dll - AlternateReaderRecog.dll ist eine gute Option, der Auspuff ist weniger als 4 Kilobyte. Dackel, Offsets variieren im Importbereich. Wir öffnen sie im Hex-Editor. Der Anfang ist der gleiche, in meiner Version ist der Müll weiter, kurz nach dem Ende des Abschnitts in der Originalversion. In meiner Version beginnt der nächste Abschnitt jedoch 100 Byte später. Durch den gleichen Wert in Bytes unterscheiden sich die Dateien! Die Offsets geben weiter die richtigen Adressen an ... Die Binärdatei ist korrekt !!! Ahhhh !!!


Einen Monat später. Woher kamen diese hundert Bytes?
Nun, da nicht klar ist, wie es funktioniert, beginnen wir, den Algorithmus zum Erstellen von E32Image zu brechen. Weiter verspotten AlternateReaderRecog.dll. Wir erhöhen die Größe der Binärdatei am Ausgang - auf keinen Fall, überschreiben die Memset-Abschnitte - auf keinen Fall, reduzieren die Größe der Binärdatei - auf keinen Fall. Grrrr. Was für?!!! Ich breche den Auspuff in der Release-Version und starte das Debuggen? !!! Hallo Bast, fang von vorne an ... Soooo Abschnitt ausgelöscht - okay! Die Größe der Binärdatei wurde erhöht! Gut !!! Reduzieren Sie die Größe des Importbereichs! Es gibt !!! Byte identisch mit dem gleichen Abschnitt im Auspuff dieses Dienstprogramms aus dem SDK!
Wir untersuchen den Code zum Erstellen dieses Abschnitts. "sizeof (char *)" - Artikel von Andrei Karpov, einem der Entwickler von Pvs-studio, haben ergeben, dass Typen unterschiedlich viel Speicher beanspruchen können - und wie viel Speicherplatz benötigt sie? MinGW - 8 Bytes, Visual Studio - 4 !!! Wir teilen diese 8 Bytes in zwei Hälften, geschäftlich etwas. FFse! Und wie ist der Codeabschnitt? Diese DLL ohne globale Variablen. Es gibt keine globalen Variablen - es gibt keinen Abschnitt ... Nehmen wir etwas Schweres - libcrypto.dll.
Die Datei am Ausgang meines Dienstprogramms ist jetzt mehr als 100 Byte kleiner ... Was ??? Der Importbereich ist byteidentisch - gut. Codeabschnitt - Nein? !!


Mit dem Auge kann ich eine solche Textwand nicht vergleichen ... Ich werde nach Diff suchen, um einen Byte-Vergleich durchzuführen ...
Nach ein paar Tagen fand ich immer noch Spiele mit der Google-Suche. vbindiff ist ein Konsolendienstprogramm mit einer ala Norton Commander-Oberfläche, das den Unterschied zwischen zwei Dateien in zwei horizontalen Bedienfeldern anzeigt. Drücken Sie die Eingabetaste, um zum Ort der Differenz zu gelangen. Gut! Sie können zwei Dateien zum Vergleich auf das Symbol ziehen und das Programm öffnet sie! Großartig !!!
Vergleichen - soooo im Header sind CRC und Erstellungszeit unterschiedlich. Nichts dergleichen. Hier ist das Byte anders, hier sind noch hundert ... Wow !!! Zehn, Hunderte, Tausende von Bytes Unterschied? !!! Also, schauen Sie, sehen Sie, zu welchem ​​Abschnitt sie gehören ... Wir schauen uns die Offsets an ... Ja, der Datenabschnitt ...
Wir machen den Trick, was den Importabschnitt betrifft ... Wir setzen das Memset zurück, das es gibt. Vergrößern Sie den Abschnitt ... Fallen ... Vergrößern. Bietet die Hand und das Herz eines Debuggers ... Verdammt. Wir öffnen die Funktion, die den Abschnitt - Brei aus den Funktionen erstellt ... Grr.
... Ah, morgen ... Bis ich etwas anderes repariere ...


Zum Beispiel werde ich Tests hinzufügen, aber es gibt ein solches Durcheinander, dass es unmöglich ist, das Programm in kleine Module zu unterteilen. Sie können keine Tests direkt in den Code einfügen - dann werden Sie den Meerrettich verstehen. Die Idee! Ständiger Start von Programmen mit unterschiedlichen Argumenten - ich habe das Programm die ganze Zeit getestet ... Und machen wir dies alles besser zu einem separaten Skript in Python. Ja, tolle Idee, einfach toll. Das Skript sollte weiterhin funktionieren, wenn Testfehler gemeldet werden, diese gemeldet werden, aber nicht abstürzen. Fertig!


Wir kehren zu unseren Widdern zurück ... Diese Funktion nennt dies, dann gehen wir hierher ... Also, Sir, wohin hat es mich gebracht? Ugh, verwirrt ... Ah, morgen ... Bis ich etwas anderes repariere ...


Und so vergingen zwei Monate ...


Verdammt, wo ist dieser Codeabschnitt gebildet? Ich musste in den akademischen Urlaub, also werde ich es zumindest mit dir herausfinden !!! Soooo Hier gehen die Symbole für den Abschnitt zur Eingabe ... Was wird printf anzeigen? Es passt nicht alles in den Konsolenpuffer ... Speichern wir den Auspuff in einer Datei ... Sooo, bisher nichts Besonderes ... Stop! Gleiche Zeilen !!! Viele identische Zeilen !!! Woher ?! Fügen Sie jeder Datenquelle printf hinzu (es gab genug Geduld für 3 von fünf ha). Es ist leer! Wir sehen uns einen der verbleibenden Funktionsaufrufe an ... Taak. Iteratorinkrement nach Schleife ??? Und todos Codacy-Warnung ??? Wir wechseln zum Zyklus. Start !!! Es gibt eine Größenübereinstimmung! Es gibt eine Byte-Übereinstimmung !!! Behoben !!! Git-Schuld weigert sich, den Helden zu nennen ... Wir schauen uns das Original an - das habe ich nicht getan. Oder war es eine "Bombe" für Entwickler, die nicht mit Nokia verbunden sind? Grrr.


Überprüfen Sie sorgfältig die Abgastests und die Byte-Dateien. Alles funktioniert wie es sollte! In der Veröffentlichung!


Olya! Es ist Zeit für eine großartige Reinigung !!! Es ist Zeit, den UseCaseBase-Baum mit der Wurzel zu entwurzeln !!!
Die meisten Nachkommen sind bereits abgenutzt, wir entfernen nützliche Funktionen in die Generatorklasse. Nur UseCaseBase und sein Nachkomme ElfFileSupplied bleiben übrig. UseCaseBase - ist ein Wrapper über eine Klasse, die Befehlsparameter verarbeitet und mehrere rein virtuelle Funktionen für die ElfFileSupplied-Klasse deklariert. Kurz gesagt, der Geiger wird nicht gebraucht ... Welcher Himmel ist blau, gut ... Noch eine Stunde ... Ich werde mich um diesen Kurs kümmern und du kannst spazieren gehen ... Und die Luft einatmen, aufwärmen, gut ... Lass uns gehen! Kommentieren Sie diese Funktion aus. Sammeln! Soooo, du musst darüber nachdenken, wie du es wunderschön neu machen kannst ... Fertig !!! Das nächste Feature! Fertig! Nächster! Fertig! Fertig! Ja! Ja! Ja! Letzte Funktion ... Ufff. Nach der Montage laufen lassen ... Siebenfache Beschleunigung der Arbeit? !!! Der Auspuff ist richtig ... lustig. Die Debug-Version ist auch um 2 Meter geschrumpft? !!! Wow!!! Sie können einen Spaziergang machen. Nachts? !!! Kaak ??? Wo ist mein Tag? !!! Ich starte Tests und entspanne mich ... Die Tests verliefen leise - Sie können sich entspannen ...


Lassen Sie mich jetzt mein eigenes etwas schreiben ... Oh, die Klasse, die mit Funktionen und Variablen arbeitet, die von außen verfügbar sind, sieht gruselig aus. Das Funktionsprinzip: Lesen aus einer Datei, Parsen von Zeilen und Speichern in einer Datei. Für die Analyse der Linien wurde eine ganze Klasse ausgewählter Nudeln in S ... Sooooh ... Denken wir mal ... Was für eine Schönheit kam heraus:
Wir lesen die Zeile std :: getline (), entfernen Leerzeichen von den Kanten von Zeilen und parsim.


Fortsetzung folgt ... Quellcode - https://github.com/fedor4ever/elf2e32

Source: https://habr.com/ru/post/de430794/


All Articles