El cuento de los cursos

Hola Quiero hablar sobre mi curso o hacia qué me lleva la curiosidad.


Durante mucho tiempo sin nada que hacer, escribo programas bajo Symbian. Y de vez en cuando me encontraba con cosas extrañas durante la asamblea. Todo apuntaba a la utilidad elf2e32. Su tarea es convertir el archivo binario de entrada del formato elfo a otro, específico para la imagen Symbian - e32. La curiosidad me dejó perplejo durante mucho tiempo: ¿cómo funciona esta utilidad y por qué a veces tiene errores? Un poco más tarde, otra pregunta comenzó a molestarme: el tema del trabajo del curso =) Decidí combinar negocios con placer y descargué su código fuente. Y lejos nos vamos ...


El primer commit no va a


El segundo: incluimos extensiones gcc no estándar, agregamos clases faltantes, funciones y constantes de la fuente. El sujeto se reúne y cae alegremente. Progreso sin embargo. Comenzamos bajo el depurador: coloca al depurador en una clase que solo inicializa otra que inicializa el siguiente ... ¡Hurra! Una característica real! Adelante Ups Donde estamos Detener depurador! Cirujano! Bisturí! Alcohol! Pepino! Clases apéndice f caja de fuego! ¡Dé nullptr en lugar de NULL! ¡Tenemos C ++ 14! ¡Vaya, qué constructor aterrador para inicializar todo con ceros! Y, sin embargo, una y otra vez, ¡pero con nosotros, C ++ 14 requiere una inicialización predeterminada para las clases! Lo que es todo sucinto ahora ...


Esta bien Arreglamos la mayor cantidad posible a la vez. Me di cuenta de por qué el depurador salta al código fuente como un ataque: los aftars golpean sus cabezas en la abstracción, lo que aumenta la herencia del nivel 80 de la clase UseCaseBase :) Luego, las clases de constructor de instancias estáticas para las clases Message & ParameterManager salieron de mis ojos. Singleton Myers? No, no escuchado. Firebox abstraction! Viva revolucion !!! Viva POD !!!


Wow Qué interesante se levantó este árbol. El trabajo principal lo realiza la función BuildAll (). Si se especifican todos los parámetros, la función recopila la biblioteca de importación, un archivo que especifica los nombres de las funciones y variables y el orden en que están disponibles en la biblioteca de importación y el propio binario. Todos los descendientes de UseCaseBase cambiaron su algoritmo por sobrecarga. A veces, en los descendientes, preparamos datos auxiliares, pero la mayoría de las veces simplemente desactivan la creación de algunos archivos. Por ejemplo, el nombre del archivo para ensamblar algo no se especifica: se crea una nueva clase. Idiotas Es suficiente interrumpir la ejecución de tal colector de funciones si es necesario. Es fácil entender mis acciones B-)


Continuamos eliminando clases vacías, reemplazamos NULL con nullptr_t, reemplazamos range-iterators con for (auto x: *).


Solucionamos errores en el procesamiento de parámetros de línea de comandos.


Es necesario verificar el código con un analizador estático. Por donde empezar Hmm, bajo XP, la elección es pequeña: cppcheck y codeblock lo admite de fábrica. Wow, qué problema! Incluso hay eliminar para char []! Maldición, sé a dónde se ha ido la mitad de la RAM libre =)


Por lo tanto, agregamos los archivos generados a partir del archivo elc libcrypto.dll y el archivo mismo que describe los parámetros de la línea de comandos para su creación.


Ups CPPCheck estaba equivocado ... Debe ser (a || b) ...


Intentaré compilar en Visual Studio 15 y Win10 se quedaría con un palo. Poner en una máquina virtual. Hecho, descargue y ejecute el instalador de estudio en línea. Que? ¿No quiere guardar el salto a la carpeta compartida con el host? ¡Sí, ahógate! Descargue donde le enseñaron ... Y ahora transferimos lo descargado a la carpeta y comenzamos la instalación. Que? Nuevamente ignora la carpeta compartida ??! ¡Sí, ahógate! Conviértete donde te enseñaron ...


En principio, una docena de susurros en un núcleo y 3 gigas del marco. Estudio a estudio! Pensativo, pero no por mucho tiempo. Abrimos mi proyecto en el estudio y nuevamente juramos en la carpeta ... ¿Cuánto ya es posible? Sí, se ahoga ... Recopilamos, jura en extensiones no estándar STL hash_set. Remoto. Remoto? Enciende el cerebro =)
Wow, qué código pegadizo:


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

Pensemos un poco ... Y 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; } 

Mi belleza ...


Entonces, ¿por qué el programa arroja una excepción si este indicador está configurado incorrectamente o no está configurado? ¿Por qué eres tan cruel, hermosa lejos? Simplemente restablezcamos esta bandera a un valor seguro. Y esta bandera también sería agradable ... Y esto, esto y esto. ¿O tal vez es mejor ponerlo en una función separada? Buena idea! ¡Llamémoslo ParameterManager :: CheckOptions ()!


Un paso a la izquierda es una caída, un paso a la derecha es una excepción no descubierta, un salto en el lugar, gracias al menos no BSOD =)


Aburrido ... Fallas y curvatura ...


Olya-la !!! Emular CleanUpStack Symbian en STL?
Básicamente nada especial:


 std::vector<char*> cleanupStack; 

Limpieza:


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

Alguna cabeza brillante en lugar de izquierda / derecha usó l / r. Gracias cppcheck


Ah, perezoso frente al monitor, los registros de cppcheck se desarman ... ¿Qué nos ofrecerá el github? ... Codacy ... Conectamos el proyecto ... ¡Pensé un poco y está listo! Ahora puedes leer los mensajes de éxito en la lucha contra los errores en el sofá ^^


Entonces, parece que no tiene errores ... Recojamos algo, por ejemplo libcrypto.dll. Funciona, aunque el archivo descomprimido es cien bytes más que el creado por la utilidad desde el SDK. A continuación, los binarios creados por esta versión de la utilidad y desde el SDK se compararán constantemente. Las opciones de línea de comando son idénticas por sí mismas.
Dachshund, ¿dónde obtener el diff analógico para archivos binarios? Hmm, consigue el guión en el pistón. Demasiada información: necesita algo mucho más simple. PDF / djvu reconocimiento dll - AlternateReaderRecog.dll es una buena opción, el escape es inferior a 4 kilobytes. Dachshunds, las compensaciones varían en la sección de importación. Los abrimos en el editor hexadecimal. El comienzo es el mismo, en mi versión la basura está más lejos, justo después del final de la sección en la versión original. Pero en mi versión, la siguiente sección comienza 100 bytes más tarde. ¡Por el mismo valor en bytes, los archivos difieren! Los desplazamientos indican además las direcciones correctas ... ¡El binario es correcto! Ahhhh !!!


Un mes despues. Entonces, ¿de dónde vienen estos cien bytes?
Bueno, como no está claro cómo funciona, comenzamos a romper el algoritmo para crear E32Image. Continuando burlándose de AlternateReaderRecog.dll. Aumentamos el tamaño del binario en la salida, de ninguna manera, sobrescribimos las secciones de memset, de ninguna manera, reducimos el tamaño del binario, de ninguna manera. Grrrr. Que mierda !!! ¡¿Rompo el escape en la versión de lanzamiento y empiezo a depurar ?! Hola bast, empieza de nuevo ... La sección de Soooo fue eliminada, ¡está bien! Aumentó el tamaño del binario! Bueno !!! ¡Reduzca el tamaño de la sección de importación! Hay !!! ¡Byte idéntico a la misma sección en el escape de esta utilidad del SDK!
Buscamos en el código para crear esta sección. "sizeof (char *)": algo volvió al artículo de Andrey Karpov, uno de los desarrolladores de Pvs-studio, que los tipos pueden ocupar diferentes cantidades de memoria, ¿y cuánto espacio ocupa? MinGW - 8 bytes, Visual Studio - 4 !!! Dividimos estos 8 bytes por la mitad, algo comercial. FFse! ¿Y cómo es la sección del código? Este dll sin variables globales. No hay variables globales, no hay una sección ... Tomemos algo más pesado: libcrypto.dll.
El archivo en la salida de mi utilidad ahora es más de 100 bytes más pequeño ... ¿Qué? La sección de importación es idéntica en bytes: buena. Sección de Código - ¡¿No? !!


A simple vista, no puedo comparar ese muro de texto ... Buscaré diff para una comparación de bytes ...
Después de un par de días, todavía encontré juegos con la búsqueda de Google. vbindiff es una utilidad de consola con una interfaz ala Norton Commander, que muestra la diferencia entre dos archivos en dos paneles horizontales. Para ir al lugar de diferencia, presione enter. Bueno! ¡Puede arrastrar dos archivos al icono para comparar y el programa los abrirá! Genial !!!
Compare: muuuy en el encabezado, su crc y el tiempo de creación son diferentes. Nada de eso. Aquí el byte es diferente, aquí hay otros cien ... ¡Guau! ¿Decenas, cientos, miles de bytes de diferencia? Entonces, miren, vean a qué sección pertenecen ... Veamos las compensaciones ... Sí, la sección de datos ...
Hacemos el truco, en cuanto a la sección de importación ... Restablecemos el memset, lo hay. Aumentar el tamaño de la sección ... Caer ... Aumentar. Ofrece la mano y el corazón de un depurador ... Maldición. Abrimos la función creando la sección - gachas de las funciones ... Grr.
... Ah, mañana ... Hasta que arregle algo más ...


Por ejemplo, agregaré pruebas, pero hay tanto desorden que es imposible dividir el programa en pequeños módulos. No puede insertar pruebas directamente en el código, entonces comprenderá el rábano picante. La idea! Lanzamientos constantes de programas con diferentes argumentos. He estado probando el programa todo el tiempo ... Y mejor hagamos de todo esto un script separado en Python. Sí, gran idea, simplemente genial. El script debe seguir funcionando cuando se informan errores de prueba, se informan pero no se bloquean. Hecho


Volvemos a nuestros carneros ... Esta función llama a esto, luego esto, vamos aquí ... Entonces, señor, ¿dónde me trajo? Ugh, confundido ... ... Ah, mañana ... Hasta que arregle algo más ...


Y así pasaron dos meses ...


Maldición, ¿dónde se forma esta sección del código? Tuve que irme de licencia académica, ¡así que al menos lo resolveré contigo! Taaaan Aquí los símbolos de la sección van a la entrada ... ¿Qué mostrará printf? Todo no cabe en el búfer de la consola ... Guardemos el escape en un archivo ... De modo que, hasta ahora, nada especial ... ¡Alto! Mismas líneas !!! ¡Muchas líneas idénticas! ¿De dónde? Agregue printf a cada fuente de datos (había suficiente paciencia para 3 de cada cinco, ha). Esta vacio! Observamos una de las llamadas de función restantes ... Taak. ¿Incremento de iterador después del ciclo? ¿Y la advertencia de codacy de todo? Nos trasladamos al ciclo. Lanzamiento !!! ¡Hay una coincidencia de tamaño! ¡Hay una coincidencia de bytes! Arreglado !!! culpa git se niega a nombrar al héroe ... Veamos el original, esto no es lo que hice. ¿O tal vez fue una "bomba" para desarrolladores no afiliados a Nokia? Grrr.


Verifique cuidadosamente las pruebas de escape, verifique los archivos de bytes. ¡Todo funciona como debería! En lanzamiento!


Olya! ¡Es hora de una gran limpieza! ¡Es hora de arrancar de raíz el árbol UseCaseBase con la raíz!
La mayoría de los descendientes ya se han desgastado, eliminamos funciones útiles para la clase de generador. Solo quedan UseCaseBase y su descendiente ElfFileSupplied. UseCaseBase: es un contenedor sobre una clase que procesa parámetros de comando y declara varias funciones puramente virtuales para la clase ElfFileSupplied. En resumen, el violinista no es necesario ... Qué cielo es azul, bueno ... Otra hora ... Me ocuparé de esta clase y puedes salir a caminar ... Y respirar el aire, calentar, bien ... ¡Vamos! Entonces, comente esta característica. Coleccionando! Soooo, debes pensar en cómo rehacerlo maravillosamente ... ¡Listo! La próxima característica! Hecho ¡Siguiente! Hecho Hecho Si! Si! Si! Última función ... Ufff. Ejecutar después del montaje ... ¡¿¡¿¡¿¡¿¡¿Una aceleración de trabajo siete veces mayor !!! El escape es correcto ... Gracioso. La versión de depuración también se reduce en 2 metros. Wow !!! Puedes dar un paseo. Por la noche? Kaak ??? ¿Dónde está mi día? Lanzaré pruebas y me relajaré ... Las pruebas funcionaron en silencio, puedes relajarte ...


Déjame escribir mi propio algo ahora ... Oh, la clase que trabaja con funciones y variables disponibles desde el exterior se ve espeluznante. El principio de funcionamiento: leer desde un archivo, analizar líneas y guardar en un archivo. Para el análisis de las líneas, se aisló una clase completa de fideos seleccionados en S ... Sooooh ... Pensemos ... Qué belleza salió:
leemos la línea std :: getline (), eliminamos espacios de los bordes de las líneas y parsim.


Continuará ... Código fuente - https://github.com/fedor4ever/elf2e32

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


All Articles