L'histoire des cours

Bonjour Je veux parler de mon cours ou de ce à quoi la curiosité mène.


Pendant longtemps, de rien à faire, j'écris des programmes sous Symbian. Et de temps en temps, je rencontrais des choses étranges lors de l'assemblage. Tout indiquait l'utilitaire elf2e32. Sa tâche est de convertir le fichier binaire d'entrée du format elf en un autre, spécifique pour l'image Symbian-e32. J'ai été longtemps perplexe par curiosité - comment fonctionne cet utilitaire et pourquoi est-il parfois buggé? Un peu plus tard, une autre question a commencé à me harceler - le sujet du cours =) J'ai décidé de combiner affaires et plaisir et j'ai téléchargé son code source. Et c'est parti ...


Le premier commit ne va pas


La seconde - nous incluons des extensions gcc non standard, ajoutons des classes, des fonctions et des constantes manquantes à partir de la source. Le sujet se rassemble et tombe joyeusement. Progrès cependant. Nous commençons sous le débogueur - il place le débogueur dans une classe qui n'en initialise qu'une autre, qui initialise la suivante ... Hourra! Une vraie fonctionnalité! Entrez. Oups Où sommes nous? !!! Arrêtez le débogueur! Chirurgien! Scalpel! De l'alcool! Du concombre! Classes annexe f firebox! Donnez nullptr au lieu de NULL! Nous avons C ++ 14! Wow, quel constructeur terrifiant pour tout initialiser avec des zéros! Et pourtant, et encore et encore - mais avec nous, C ++ 14 appelle l'initialisation par défaut des classes! Ce qui est succinct maintenant ...


D'accord. Nous en fixons autant que possible à la fois. J'ai compris pourquoi le débogueur saute au code source comme un ajustement - les aftars se sont cognés la tête sur l'abstraction, augmentant l'héritage du niveau 80 de la classe UseCaseBase :) Ensuite, les classes constructeur d'instances statiques pour les classes Message & ParameterManager ont volé de mes yeux. Singleton Myers? Non, pas entendu. Abstraction du foyer! Viva revolucion !!! Viva POD !!!


Wow Combien cet arbre a été intéressant. Le travail principal est effectué par la fonction BuildAll (). Si tous les paramètres sont spécifiés, la fonction collecte la bibliothèque d'importation, un fichier spécifiant les noms des fonctions et des variables et l'ordre dans lequel ils sont disponibles dans la bibliothèque d'importation et le binaire lui-même. Tous les descendants d'UseCaseBase ont changé son algorithme par surcharge. Parfois, dans les descendants, nous préparons des données auxiliaires, mais le plus souvent, ils ont simplement désactivé la création de certains fichiers. Par exemple, le nom de fichier pour assembler quelque chose n'est pas spécifié - une nouvelle classe est créée. Idiots. Il suffit d'interrompre l'exécution d'un tel collecteur de fonctions si nécessaire. Il est facile de comprendre mes actions B-)


Nous continuons à supprimer les classes vides, à remplacer NULL par nullptr_t, à remplacer range-iterators par for (auto x: *).


Nous corrigeons les erreurs de traitement des paramètres de ligne de commande.


Il est nécessaire de vérifier le code avec un analyseur statique. Par où commencer? Hmm, sous XP, le choix est petit - cppcheck, et le codeblock le prend en charge dès la sortie de la boîte. Wow, quelle prise! Il y a même suppression pour char []! Merde, je sais où la moitié de la RAM libre est allée =)


Nous ajoutons donc les fichiers générés à partir du fichier elc libcrypto.dll et le fichier lui-même décrivant les paramètres de ligne de commande pour leur création.


Oups CPPCheck était faux ... Ce doit être (a || b) ...


J'essaierai de construire dans Visual Studio 15 et Win10 resterait avec un bâton. Mettez sur une machine virtuelle. Terminé, téléchargez et exécutez le programme d'installation du studio en ligne. Quoi? Vous ne voulez pas enregistrer le saut dans le dossier partagé avec l'hôte?! Oui, étouffez-vous! Téléchargez là où vous avez appris ... Et maintenant, nous transférons le fichier téléchargé dans le dossier et commençons l'installation. Quoi? Ignore à nouveau le dossier partagé ??! Oui, étouffez-vous! Devenez là où on vous a appris ...


En principe, une douzaine de puits bruissent sur un noyau et 3 concerts du cadre. Studio à studio! Réfléchi, mais pas pour longtemps. On ouvre mon projet en studio, et jure encore sur le dossier ... Combien est déjà possible? Oui, vous étouffez ... Nous collectons, jure sur les extensions non standard STL hash_set. À distance. À distance ??? Allumez le cerveau =)
Wow quel code accrocheur:


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; } 

Réfléchissons un peu ... Et le tour est joué:


 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; } 

Ma beauté ...


Donc, pourquoi le programme lève-t-il une exception si cet indicateur est mal défini ou pas défini du tout? Pourquoi êtes-vous si cruel, si beau si loin ... Réinitialisons simplement ce drapeau à une valeur sûre. Et ce drapeau serait aussi bien ... Et ça, et ça, et ça. Ou peut-être vaut-il mieux le mettre dans une fonction distincte? Bonne idée! Appelons-le ParameterManager :: CheckOptions ()!


Un pas à gauche est une chute, un pas à droite est une exception inattendue, un saut en place - merci du moins pas BSOD =)


Ennuyeux ... Pépins et courbure ...


Olya-la !!! Émuler CleanUpStack Symbian sur STL?:
Fondamentalement, rien de spécial:


 std::vector<char*> cleanupStack; 

Nettoyage:


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

Une tête brillante au lieu de gauche / droite utilisé l / r. Merci cppcheck.


Ah, paresseux devant le moniteur les journaux de cppcheck à démonter ... Que va nous offrir le github? .. Codacy ... On connecte le projet ... J'ai réfléchi un peu et c'est prêt! Vous pouvez maintenant lire les messages de réussite dans la lutte contre les erreurs allongés sur le canapé ^^


Donc, il semble que ce ne soit pas buggé ... Collectons quelque chose, par exemple libcrypto.dll. Cela fonctionne, bien que le fichier non compressé fasse cent octets de plus que celui créé par l'utilitaire à partir du SDK. Ensuite, les binaires créés par cette version de l'utilitaire et à partir du SDK seront constamment comparés. Les options de ligne de commande sont identiques en elles-mêmes.
Teckel, où trouver l'analogue diff pour les fichiers binaires? Hmm, prenez le script sur le piston. Trop d'informations - vous avez besoin de quelque chose de beaucoup plus simple. DLL de reconnaissance Pdf / djvu - AlternateReaderRecog.dll est une bonne option, l'échappement est inférieur à 4 kilo-octets. Les teckels, les décalages varient dans la section d'importation. Nous les ouvrons dans l'éditeur hexadécimal. Le début est le même, dans ma version les ordures continuent, juste après la fin de la section dans la version originale. Mais dans ma version, la section suivante commence 100 octets plus tard. De la même valeur en octets, les fichiers diffèrent! Les décalages indiquent en outre les bonnes adresses ... Le binaire est correct !!! Ahhhh !!!


Un mois plus tard. D'où viennent donc ces cent octets?
Eh bien, comme il n'est pas clair comment cela fonctionne, nous commençons à casser l'algorithme pour créer E32Image. Continuer à se moquer de AlternateReaderRecog.dll. Nous augmentons la taille du binaire à la sortie - pas du tout, écrasons les sections du memset - pas du tout, réduisons la taille du binaire - pas du tout. Grrrr. Putain !!! Je casse l'échappement dans la version finale et commence le débogage? !!! Salut salaud, recommencez ... La section Soooo a été effacée - d'accord! Augmentation de la taille du binaire! Bon !!! Réduisez la taille de la section d'importation! Il y a !!! Octet identique à la même section dans l'échappement de cet utilitaire du SDK!
Nous examinons le code pour créer cette section. "sizeof (char *)" - quelque chose est revenu à l'article d'Andrey Karpov, l'un des développeurs de Pvs-studio, que les types peuvent occuper différentes quantités de mémoire - et combien d'espace cela prend-il? MinGW - 8 octets, Visual Studio - 4 !!! Nous divisons ces 8 octets en deux, quelque chose de professionnel. FFse! Et comment est la section de code? Cette DLL sans variables globales. Il n'y a pas de variables globales - il n'y a pas de section ... Prenons quelque chose de plus lourd - libcrypto.dll.
Le fichier à la sortie de mon utilitaire est maintenant plus de 100 octets plus petit ... Quoi ??? La section d'importation est identique à l'octet - bonne. Section Code - Non? !!


A l'oeil, je ne peux pas comparer un tel mur de texte ... Je vais chercher un diff pour une comparaison d'octets ...
Après quelques jours, j'ai toujours trouvé des jeux avec la recherche Google. vbindiff est un utilitaire de console avec une interface ala Norton Commander, montrant la différence entre deux fichiers dans deux panneaux horizontaux. Pour aller au lieu de la différence, appuyez sur entrée. Bon! Vous pouvez faire glisser deux fichiers sur l'icône pour comparaison et le programme les ouvrira! Super !!!
Comparer - soooo dans l'en-tête son crc et son temps de création sont différents. Rien de tel. Ici l'octet est différent, en voici une autre centaine ... Wow !!! Des dizaines, des centaines, des milliers d'octets de différence? !!! Alors, regardez, voyez à quelle section ils appartiennent ... Nous regardons les décalages ... Oui, la section des données ...
On fait l'astuce, comme pour la section import ... On réinitialise le memset, il y en a. Augmentez la taille de la section ... Chute ... Augmentez. Offre la main et le cœur d'un débogueur ... Merde. Nous ouvrons la fonction créant la section - bouillie à partir des fonctions ... Grr.
... Ah, demain ... Jusqu'à ce que je fixe autre chose ...


Par exemple, j'ajouterai des tests, mais il y a un tel gâchis qu'il est impossible de diviser le programme en petits modules. Vous ne pouvez pas insérer des tests directement dans le code - vous comprendrez alors le raifort. L'idée! Lancements constants de programmes avec différents arguments - J'ai testé le programme tout le temps ... Et il vaut mieux en faire un script séparé en python. Oui, bonne idée, tout simplement génial. Le script doit continuer à fonctionner lors du signalement des erreurs de test, en les signalant mais sans se bloquer. C'est fait!


Nous retournons à nos béliers ... Cette fonction appelle ça, puis ça, nous allons ici ... Alors, monsieur, où ça m'a amené? Ugh, confus ... ... Ah, demain ... Jusqu'à ce que je répare autre chose ...


Et donc deux mois se sont écoulés ...


Merde, où est formée cette section de code? J'ai dû partir en congé académique, donc au moins je vais le découvrir avec vous !!! Soooo Ici, les symboles de la section vont à l'entrée ... Que montrera printf? Tout ne rentre pas dans le tampon de la console ... Sauvegardons l'échappement dans un fichier ... Sooo, jusqu'à présent rien de spécial ... Arrêtez! Mêmes lignes !!! Beaucoup de lignes identiques !!! D'où?! Ajoutez printf à chaque source de données (il y avait assez de patience pour 3 sur cinq, ha). C'est vide! Nous regardons l'un des appels de fonction restants ... Taak. Incrément de l'itérateur après la boucle ??? Et l'avertissement de codacy de todo ??? Nous transférons au cycle. Lancez !!! Il y a une correspondance de taille! Il y a une correspondance d'octets !!! Fixé !!! git blame refuse de nommer le héros ... Nous regardons l'original - ce n'est pas ce que j'ai fait. Ou peut-être que c'était une "bombe" pour les développeurs non affiliés à Nokia? Grrr.


Vérifiez soigneusement les tests d'échappement, vérifiez les fichiers d'octets. Tout fonctionne comme il se doit! En sortie!


Olya! Il est temps pour un grand nettoyage !!! Il est temps de déraciner l'arbre UseCaseBase avec la racine !!!
La plupart des descendants étant déjà épuisés, nous supprimons les fonctions utiles de la classe générateur. Seuls UseCaseBase et son descendant ElfFileSupplied restent. UseCaseBase - est un wrapper sur une classe qui traite les paramètres de commande et déclare plusieurs fonctions purement virtuelles pour la classe ElfFileSupplied. Bref, le violoniste n'est pas nécessaire ... Quel ciel est bleu, bon ... Encore une heure ... Je vais m'occuper de ce cours et tu peux aller te promener ... Et respirer l'air, réchauffer, bon ... Allons-y! Alors, commentez cette fonctionnalité. Collectionner! Soooo, vous devez penser à comment le refaire magnifiquement ... Terminé !!! La prochaine fonctionnalité! C'est fait! Prochain! C'est fait! C'est fait! Oui! Oui! Oui! Dernière fonction ... Ufff. Courir après l'assemblage ... Accélération du travail sept fois? !!! L'échappement est correct ... Drôle. La version de débogage est également réduite de 2 mètres? !!! Wow !!! Vous pouvez vous promener. La nuit? !!! Kaak ??? Où est ma journée? !!! Je vais lancer des tests et me détendre ... Les tests ont fonctionné tranquillement - vous pouvez vous détendre ...


Permettez-moi d'écrire mon propre quelque chose maintenant ... Oh, la classe travaillant avec des fonctions et des variables disponibles de l'extérieur semble effrayante. Le principe de fonctionnement: lecture d'un fichier, analyse des lignes et sauvegarde dans un fichier. Pour l'analyse des lignes, toute une classe de nouilles sélectionnées a été isolée dans S ... Sooooh ... Pensons ... Quelle beauté est sortie:
nous lisons la ligne std :: getline (), supprimons les espaces des bords des lignes et le parsim.


À suivre ... Code source - https://github.com/fedor4ever/elf2e32

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


All Articles