Después de leer la noticia "
Código de intérprete de Perl portado oficialmente a GitHub " en LINUX.ORG.RU, decidí echar un vistazo al repositorio de Perl 5, que ahora ya está en GitHub.
Es sorprendente cuán tremendamente y cualitativamente lo transfirieron, preservando no solo absolutamente toda la historia de 32 años del proyecto, sino también informes de errores (se metieron en problemas), parches (se metieron en relaciones públicas), lanzamientos y sucursales. La inscripción "hace
32 años " al lado de los archivos provoca una sonrisa involuntaria.
¿Qué más hacer en este aburrido viernes por la noche, cuando la lluvia y la nieve están lloviznando desagradablemente en la calle, y todos los caminos de la calle están sumidos en el aguanieve del otoño? Así es, ojos rojos! Entonces, en aras de la experimentación y el interés, decidí tomar y ensamblar el antiguo Perl en una máquina moderna x86_64 con la última versión de
GCC 9.2.0 como compilador. ¿Puede un código tan antiguo pasar la prueba del tiempo?
Demostración de twm , uno de los primeros gestores de ventanas para el sistema X Window, en una distribución moderna de Arch Linux.Para ser completamente auténtico y nekromantnenko, implementé una máquina virtual con X desnudo y administrador de ventanas
twm , que también proviene de 1987. Quién sabe, tal vez
Larry Wall escribió su Perl usando exactamente
twm , por así decirlo
, tecnología punta de la época. La distribución utilizada es Arch Linux. Solo porque hay algunas cosas útiles en su repositorio que fueron útiles más adelante. ¡Entonces vamos!
Contenido:
1. Preparación del medio ambiente.2. Configurando el código fuente3. Errores de archivo de gramática Yacc4. Errores de compilación de código en "C"5. Corrección de algunos errores Fallo de segmentación6. Para resumir1. Preparación del medio ambiente.
Primero, instalamos en un sistema operativo desplegado en una máquina virtual todo el conjunto caballeroso de utilidades y compiladores necesarios para ensamblar y editar el código fuente:
gcc ,
make ,
vim ,
git ,
gdb , etc. Algunos de ellos ya están instalados, mientras que otros están disponibles en el metapaquete
base-devel , debe instalarse si no está instalado. Una vez que el entorno está listo para la acción, ¡obtenemos una copia del código fuente de Perl de 32 años!
$ git clone https://github.com/Perl/perl5/ --depth=1 -b perl-1.0
Gracias a las características de Git, no necesitamos arrastrar un montón de archivos para llegar a la primera versión del proyecto:
* commit 8d063cd8450e59ea1c611a2f4f5a21059a2804f1 (grafted, HEAD, tag: perl-1.0) Commit: Larry Wall <lwall@jpl-devvax.jpl.nasa.gov> CommitDate: Fri Dec 18 00:00:00 1987 +0000 a "replacement" for awk and sed
Solo descargamos una pequeña cantidad de datos y, como resultado, el repositorio con el código fuente de la primera versión de Perl toma solo 150 KB.
En ese tiempo oscuro y denso no existían elementos tan elementales como los
autotools (¡
qué bendición! ). Sin embargo, hay un script
Configure en la raíz del repositorio. Cual es el problema Pero el hecho es que Larry Wall es el inventor de dichos scripts que permitieron generar Makefiles para las máquinas UNIX más abigarradas de la época. Como dice el
artículo de Wikipedia sobre los guiones del
mismo nombre , Larry Wall suministró el archivo de
configuración con algunos de sus programas, por ejemplo, un lector de noticias, tres años más antes de escribir Perl. Posteriormente, Perl no fue la excepción, y se utilizó un script que ya se ejecutaba en muchas máquinas para construirlo. Más tarde, otros desarrolladores, por ejemplo, programadores de Trolltech, también recogieron esta idea. Utilizaron un script similar para configurar la compilación de su marco Qt, que muchas personas confunden con la
configuración de las
herramientas automáticas . Fue el zoológico de tales scripts de diferentes desarrolladores que sirvió como ímpetu para crear herramientas para su generación simplificada y automática.
<< Saltar al contenido2. Configurando el código fuente
El script de
configuración de la "vieja escuela", que ya es evidente por su
Shebang ', que tiene un espacio:
$ cat Configure | head -5
¡Según el comentario, resulta que había guiones en los guiones de los cuales no era posible dejar comentarios! La situación espacial parece inusual, pero una vez que esto era la norma, vea el enlace para obtener más información
aquí . Lo más importante, no hay diferencia para los intérpretes de shell modernos si hay un espacio o no.
Basta de letras, ¡vamos al grano! Comenzamos el script y vemos una suposición interesante, que resulta no ser del todo cierto:
$ ./Configure (I see you are using the Korn shell. Some ksh's blow up on Configure, especially on exotic machines. If yours does, try the Bourne shell instead.) Beginning of configuration questions for perl kit. Checking echo to see how to suppress newlines... ...using -n. Type carriage return to continue. Your cursor should be here-->
Sorprendentemente, el script es interactivo y contiene una gran cantidad de información de fondo variada. El modelo de interacción del usuario se basa en diálogos, analizando las respuestas a las cuales el script cambia sus parámetros, de acuerdo con lo cual generará posteriormente Makefiles. ¿Estaba personalmente interesado en verificar si todos los comandos de shell están en su lugar?
Locating common programs... expr is in /bin/expr. sed is in /bin/sed. echo is in /bin/echo. cat is in /bin/cat. rm is in /bin/rm. mv is in /bin/mv. cp is in /bin/cp. tr is in /bin/tr. mkdir is in /bin/mkdir. sort is in /bin/sort. uniq is in /bin/uniq. grep is in /bin/grep. Don't worry if any of the following aren't found... test is in /bin/test. egrep is in /bin/egrep. I don't see Mcc out there, offhand.
Aparentemente antes de esto estaba lejos del caso. Me pregunto de qué es responsable la utilidad
Mcc , que no se pudo encontrar. Lo curioso es que este guión en las mejores tradiciones hacker de la época está lleno de humor amistoso. Ahora apenas verás esto:
Is your "test" built into sh? [n] (OK to guess) OK Checking compatibility between /bin/echo and builtin echo (if any)... They are compatible. In fact, they may be identical. Your C library is in /lib/libc.a. You're normal. Extracting names from /lib/libc.a for later perusal...done Hmm... Looks kind of like a USG system, but we'll see... Congratulations. You aren't running Eunice. It's not Xenix... Nor is it Venix... Checking your sh to see if it knows about # comments... Your sh handles # comments correctly. Okay, let's see if #! works on this system... It does. Checking out how to guarantee sh startup... Let's see if '#!/bin/sh' works... Yup, it does.
Respondí la mayoría de las preguntas con el valor predeterminado o con lo que me ofreció el script. Particularmente complacido y sorprendido fue la solicitud de banderas para el compilador y el enlazador:
Any additional cc flags? [none] Any additional ld flags? [none]
Allí puede escribir algo interesante, por ejemplo,
-m32 para construir un archivo ejecutable de 32 bits o una biblioteca, que se requiere durante el enlace. A la última pregunta del guión:
Now you need to generate make dependencies by running "make depend". You might prefer to run it in background: "make depend > makedepend.out &" It can take a while, so you might not want to run it right now. Run make depend now? [n] y
Le respondí positivamente. A juzgar por
su página de Wikipedia, la antigua utilidad
makedepend se creó al comienzo de
la vida del
proyecto Athena para facilitar el trabajo con Makefiles. Este proyecto nos dio el sistema X Window, Kerberos, Zephyr e influyó en muchas otras cosas que hoy nos son familiares. Todo esto es maravilloso, pero ¿de dónde viene esta utilidad en un entorno Linux moderno? Durante mucho tiempo ha sido utilizado por nadie y en cualquier lugar. Pero si observa de cerca la raíz del repositorio, resulta que Larry Wall escribió su versión de script sustituto, que desempacamos cuidadosamente y ejecutamos el script de configuración.
Makedepend completado con algunos errores extraños:
./makedepend: command substitution: line 82: unexpected EOF while looking for matching `'' ./makedepend: command substitution: line 83: syntax error: unexpected end of file ./makedepend: command substitution: line 82: unexpected EOF while looking for matching `'' ./makedepend: command substitution: line 83: syntax error: unexpected end of file
Quizás fueron ellos quienes causaron el problema debido a que los Makefiles generados se masticaron un poco:
$ make make: *** No rule to make target '<built-in>', needed by 'arg.o'. Stop.
Definitivamente no quería entrar en la jungla de los intrincados fideos de concha de la utilidad
makedepend y decidí mirar cuidadosamente los Makefiles, en los que surgió un patrón extraño:
arg.o: arg.c arg.o: arg.h arg.o: array.h arg.o: <built-in> arg.o: cmd.h arg.o: <command-line> arg.o: config.h arg.o: EXTERN.h ... array.o: arg.h array.o: array.c array.o: array.h array.o: <built-in> array.o: cmd.h array.o: <command-line> array.o: config.h array.o: EXTERN.h ...
Aparentemente, alguna utilidad insertó incorrectamente sus argumentos en el escape. Al recoger la utilidad del
hacha sed, decidí arreglar ligeramente esto:
$ sed -i '/built-in/d' Makefile $ sed -i '/command-line/d' Makefile
Sorprendentemente, el truco funcionó y los Makefiles funcionaron como deberían.
<< Saltar al contenido3. Errores de archivo de gramática Yacc
Sería increíble si el código de 32 años tomara y ensamblara sin ningún problema. Lamentablemente, los milagros no suceden. Al estudiar el árbol de origen, me encontré con un archivo
perl.y , que es una descripción gramatical de la utilidad
yacc , que ha sido reemplazada por
bison en las distribuciones modernas. El script ubicado en la ruta
/ usr / bin / yacc simplemente llama a
bison en modo de compatibilidad con
yacc . Es solo que esta compatibilidad no está completa y cuando procesamos este archivo se está acumulando una gran cantidad de errores, que no sé cómo corregir y realmente no quiero, porque hay una solución alternativa que aprendí recientemente.
Hace solo un año o dos, Helio Chissini de Castro, desarrollador de KDE, realizó un trabajo similar y adaptó KDE 1, 2 y Qt 1, 2 a entornos y compiladores modernos. Me interesé por su trabajo, descargué los códigos fuente de los proyectos, pero durante el ensamblaje me encontré con una
trampa similar debido a la incompatibilidad de
yacc y
bison , que se utilizaron para construir la versión antigua del metacompilador
moc . Posteriormente, logré encontrar una solución a este problema en forma de reemplazar
bison con la utilidad
byacc (Berkeley Yacc), que resultó ser compatible con las viejas gramáticas para
yacc y estaba disponible en muchas distribuciones de Linux.
Un reemplazo simple de
yacc con
byacc en el sistema de compilación me ayudó entonces, aunque no por mucho tiempo, porque un poco más tarde en las nuevas versiones de
byacc todavía rompieron la compatibilidad con
yacc , interrumpiendo la depuración asociada con la entidad
yydebug . Por lo tanto, tuve que
corregir ligeramente
la gramática de la utilidad.
Entonces, la estrategia para corregir errores de construcción en el archivo
perl.y fue predicha por experiencia previa: instale la utilidad
byacc , cambie
yacc a
byacc en todos los Makefiles, luego corte
yydebug de todas partes. Estas acciones resolvieron todos los problemas con este archivo, los errores desaparecieron y la compilación continuó.
<< Saltar al contenido4. Errores de compilación de código en "C"
El antiguo código de Perl estaba lleno de horrores, como la notación obsoleta y olvidada de las definiciones de funciones del tipo K&R:
format(orec,fcmd) register struct outrec *orec; register FCMD *fcmd; { ... } STR * hfetch(tb,key) register HASH *tb; char *key; { ... } fatal(pat,a1,a2,a3,a4) char *pat; { fprintf(stderr,pat,a1,a2,a3,a4); exit(1); }
Se encontraron características similares, por ejemplo, en el código
Microsoft Word 1.1a , que también es bastante antiguo. El primer estándar del lenguaje de programación "C", llamado "C89", aparecerá solo en dos años. Los compiladores modernos pueden trabajar con dicho código, pero algunos IDE no facilitan el análisis de dichas definiciones y las resaltan como errores de sintaxis, por ejemplo,
Qt Creator pecó antes de analizar el código en la biblioteca
libclang .
El compilador GCC 9.2.0, arrojando una gran cantidad de advertencias, se comprometió a compilar el código antiguo de la primera versión de Perl. Las hojas de las advertencias eran tan grandes que para llegar al error, tuvimos que desplazar varias páginas del escape hacia arriba. Para mi sorpresa, la mayoría de los errores de compilación fueron típicos y se relacionaron principalmente con definiciones predefinidas, que desempeñaron el papel de banderas para el ensamblaje.
El trabajo del moderno compilador GCC 9.2.0 y el depurador GDB 8.3.1 en el administrador de ventanas twm y el emulador de terminal xterm .Bajo STDSTDIO
, Larry Wall experimentó con una biblioteca de lenguaje de programación antigua y no estándar "C", y bajo DEPURACIÓN
hubo información de depuración con el notorio
yydebug , que mencioné anteriormente. Por defecto, estas casillas de verificación estaban habilitadas. Al desactivarlos en el archivo
perl.h y agregar algunas definiciones olvidadas, pude reducir significativamente la cantidad de errores.
Otro tipo de error es anular las funciones ahora estandarizadas de la biblioteca estándar y la capa POSIX. El proyecto tiene su propio
malloc () ,
setenv () y otras entidades que crearon conflictos.
Un par de lugares definieron funciones estáticas sin declaraciones. Con el tiempo, los compiladores comenzaron a adoptar un enfoque más estricto para este problema y
convirtieron la advertencia en un error . Y finalmente, un par de encabezados olvidados, ¿a dónde irías sin ellos?
Para mi sorpresa, el parche para el código de 32 años resultó ser tan pequeño que puede citarse completamente aquí:
diff --git a/malloc.cb/malloc.c index 17c3b27..a1dfe9c 100644 --- a/malloc.c +++ b/malloc.c @@ -79,6 +79,9 @@ static u_int nmalloc[NBUCKETS]; #include <stdio.h> #endif +static findbucket(union overhead *freep, int srchlen); +static morecore(register bucket); + #ifdef debug #define ASSERT(p) if (!(p)) botch("p"); else static diff --git a/perl.hb/perl.h index 3ccff10..e98ded5 100644 --- a/perl.h +++ b/perl.h @@ -6,16 +6,16 @@ * */ -#define DEBUGGING -#define STDSTDIO /* eventually should be in config.h */ +//#define DEBUGGING +//#define STDSTDIO /* eventually should be in config.h */ #define VOIDUSED 1 #include "config.h" -#ifndef BCOPY -# define bcopy(s1,s2,l) memcpy(s2,s1,l); -# define bzero(s,l) memset(s,0,l); -#endif +//#ifndef BCOPY +//# define bcopy(s1,s2,l) memcpy(s2,s1,l); +//# define bzero(s,l) memset(s,0,l); +//#endif #include <stdio.h> #include <ctype.h> @@ -183,11 +183,11 @@ double atof(); long time(); struct tm *gmtime(), *localtime(); -#ifdef CHARSPRINTF - char *sprintf(); -#else - int sprintf(); -#endif +//#ifdef CHARSPRINTF +// char *sprintf(); +//#else +// int sprintf(); +//#endif #ifdef EUNICE #define UNLINK(f) while (unlink(f) >= 0) diff --git a/perl.yb/perl.y index 16f8a9a..1ab769f 100644 --- a/perl.y +++ b/perl.y @@ -7,6 +7,7 @@ */ %{ +#include <stdlib.h> #include "handy.h" #include "EXTERN.h" #include "search.h" diff --git a/perly.cb/perly.c index bc32318..fe945eb 100644 --- a/perly.c +++ b/perly.c @@ -246,12 +246,14 @@ yylex() static bool firstline = TRUE; retry: +#ifdef DEBUGGING #ifdef YYDEBUG if (yydebug) if (index(s,'\n')) fprintf(stderr,"Tokener at %s",s); else fprintf(stderr,"Tokener at %s\n",s); +#endif #endif switch (*s) { default: diff --git a/stab.cb/stab.c index b9ef533..9757cfe 100644 --- a/stab.c +++ b/stab.c @@ -7,6 +7,7 @@ */ #include <signal.h> +#include <errno.h> #include "handy.h" #include "EXTERN.h" #include "search.h" diff --git a/util.hb/util.h index 4f92eeb..95cb9bf 100644 --- a/util.h +++ b/util.h @@ -28,7 +28,7 @@ void prexit(); char *get_a_line(); char *savestr(); int makedir(); -void setenv(); +//void setenv(); int envix(); void notincl(); char *getval();
¡Gran resultado para el código de 32 años! La
referencia indefinida al error de enlace
'crypt' se corrigió agregando la directiva
-lcrypt al Makefile con la biblioteca
libcrypt apropiada, después de lo cual finalmente obtuve el ejecutable del intérprete Perl deseado:
$ file perl perl: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=fd952ceae424613568530b3a2ca88ebd6477e0ae, for GNU/Linux 3.2.0, not stripped
<< Saltar al contenido5. Corrección de algunos errores Fallo de segmentación
Después de una compilación casi sin problemas, la suerte me dio la espalda. Inmediatamente después de iniciar el intérprete de Perl ensamblado, obtuve algunos errores extraños y una falla de segmentación al final:
$ ./perl -e 'print "Hello World!\n";' Corrupt malloc ptr 0x2db36040 at 0x2db36000 Corrupt malloc ptr 0x2db36880 at 0x2db36800 Corrupt malloc ptr 0x2db36080 at 0x2db36040 Corrupt malloc ptr 0x2db37020 at 0x2db37000 Segmentation fault (core dumped)
Habiendo roído el texto fuente de la frase
Corrupt malloc , resultó que en lugar del sistema
malloc () se llamó a algún tipo de asignador personalizado desde 1982. Curiosamente,
Berkeley está escrito en uno de los literales de cadena en su código fuente, y
Caltech en un comentario al lado. La colaboración entre estas universidades fue evidente entonces muy fuerte. En general, comenté este asignador de piratas informáticos y reconstruí el código fuente. Los errores de corrupción de memoria desaparecieron, pero el error de segmentación permaneció. Entonces ese no era el punto, y ahora necesitamos descubrir el depurador.
Al ejecutar el programa bajo
gdb, descubrí que el bloqueo ocurre cuando la función para crear un archivo temporal
mktemp () desde libc se llama:
$ gdb --args ./perl -e 'print "Hello, World!\n";' (gdb) r Starting program: /home/exl/perl5/perl -e print\ \"Hello\ World\!\\n\"\; Program received signal SIGSEGV, Segmentation fault. 0x00007ffff7cd20c7 in __gen_tempname () from /usr/lib/libc.so.6 (gdb) bt #0 0x00007ffff7cd20c7 in __gen_tempname () from /usr/lib/libc.so.6 #1 0x00007ffff7d71577 in mktemp () from /usr/lib/libc.so.6 #2 0x000055555556bb08 in main ()
Por cierto, el vinculador juró previamente sobre esta función. No es un compilador, sino un enlazador, lo que me sorprendió:
/usr/bin/ld: perl.o: in function `main': perl.c:(.text+0x978c): warning: the use of `mktemp' is dangerous, better use `mkstemp' or `mkdtemp'
El primer pensamiento que probablemente se te ocurrió también fue reemplazar la
función insegura mktemp () con
mkstemp () , lo cual hice. La advertencia del enlazador desapareció, pero la falla de segmentación permaneció en este lugar de todos modos, solo que ahora estaba en la función
mkstemp () .
Por lo tanto, ahora debe mirar con mucho cuidado el fragmento de código asociado con esta función. Allí descubrí una cosa bastante extraña que se destaca en este fragmento:
char *e_tmpname = "/tmp/perl-eXXXXXX"; int main(void) { mktemp(e_tmpname); e_fp = f_open(e_tmpname, "w"); ... }
Resulta que
mktemp () está tratando de cambiar el literal de la máscara, que se encuentra en la sección
.rodata , que obviamente está condenada al fracaso. ¿O, después de todo, hace 32 años, esto era aceptable, se cumplía en el código e incluso funcionaba de alguna manera?
Por supuesto, al reemplazar
char * e_tmpname con
char e_tmpname [] se solucionó este error de segmentación y pude obtener lo que maté durante toda la noche:
$ ./perl -e 'print "Hello World!\n";' $ Hello, World! $ ./perl -e '$a = 5; $b = 6.3; $c = $a+$b; print $c."\n";' $ 11.3000000000000007 $ ./perl -v $Header: perly.c,v 1.0 87/12/18 15:53:31 root Exp $ Patch level: 0
Verificamos la ejecución desde la línea de comando, pero ¿qué pasa con el archivo? Descargué el primer "Hello World" para el lenguaje de programación Perl de Internet:
Luego traté de ejecutarlo, pero, por desgracia, la falla de segmentación me estaba esperando nuevamente. Esta vez en un lugar completamente diferente:
$ gdb --args ./perl test.pl (gdb) r Starting program: /home/exl/perl5/perl test.pl Program received signal SIGSEGV, Segmentation fault. 0x00007ffff7d1da75 in __strcpy_sse2_unaligned () from /usr/lib/libc.so.6 (gdb) bt #0 0x00007ffff7d1da75 in __strcpy_sse2_unaligned () from /usr/lib/libc.so.6 #1 0x00005555555629ea in yyerror () #2 0x0000555555568dd6 in yyparse () #3 0x000055555556bd4f in main ()
El siguiente punto interesante se encontró en la función
yyerror () , cito el fragmento original:
Nuevamente, la situación es similar a la que escribí anteriormente. Los datos en la sección
.rodata se
modifican nuevamente . ¿Tal vez son solo errores tipográficos debido a Copy-Paste y en lugar de
tname querían escribir
tmpbuf ? ¿O hay realmente algún tipo de significado oculto detrás de esto? En cualquier caso, al reemplazar
char * tokename [] por
char tokename [] [32] se elimina el error de segmentación y Perl nos dice lo siguiente:
$ ./perl test.pl syntax error in file test.pl at line 7, next token "strict" Execution aborted due to compilation errors.
Resulta que no le gusta todo tipo de
uso novedoso
estricto , ¡eso es lo que está tratando de decirnos! Si elimina o comenta estas líneas en el archivo, el programa comienza:
$ ./perl test.pl Hello, World!
<< Saltar al contenido6. Para resumir
De hecho, logré mi objetivo e hice que el antiguo código de 1987 no solo se compilara, sino que también funcionara en un entorno Linux moderno. Sin lugar a dudas, todavía queda una gran pila de varios errores de fallas de segmentación, posiblemente relacionados con el tamaño del puntero en una arquitectura de 64 bits. Todo esto se puede limpiar después de pasar algunas tardes con el depurador listo. Pero esta no es una tarea muy agradable y bastante tediosa. Después de todo, inicialmente este experimento fue planeado como entretenimiento para una noche aburrida, y no como un trabajo completo, que se pondrá fin. ¿Hay algún beneficio práctico de las acciones tomadas? Tal vez algún día algún arqueólogo digital se encuentre con este artículo y le sea útil. Pero en el mundo real, incluso la experiencia obtenida de tal investigación, en mi opinión, no es demasiado valiosa.
Si alguien está interesado, publico un conjunto de dos parches. El primero corrige errores de compilación, y el segundo corrige algunos errores de falla de segmentación.
PD: me apresuro a molestar a los fanáticos de los
destructivos jugadores de una sola línea , esto no funciona aquí. Quizás la versión de Perl es demasiado antigua para tal entretenimiento.
PPS Todo bien y que tengas un buen fin de semana. Gracias a
kawaii_neko por una
pequeña solución .
Actualización 28-oct-2019: un usuario del foro LINUX.ORG.RU, utilizando el apodo
utf8nowhere , proporcionó enlaces bastante interesantes
en su comentario sobre este artículo, cuya información no solo aclara la situación con literales de cadena mutable, sino que incluso considera el problema de uso descrito anteriormente Funciones
mktemp () ! Permítanme citar estas fuentes, que describen diversas incompatibilidades entre K&R C no estandarizado y GNU C:
Incompatibilidades de GCC
Existen varias incompatibilidades notables entre las versiones C de GNU C y K&R (no ISO).
GCC normalmente hace que las constantes de cadena sean de solo lectura. Si se usan varias constantes de cadena de aspecto idéntico, GCC almacena solo una copia de la cadena.
Una consecuencia es que no puede llamar a mktemp con un argumento de cadena constante. La función mktemp siempre altera la cadena a la que apunta su argumento.
Otra consecuencia es que sscanf no funciona en algunos sistemas cuando se pasa una constante de cadena como entrada o cadena de control de formato.Esto se debe a que sscanf intenta escribir incorrectamente en la cadena constante. Del mismo modo fscanf y scanf .
Solución a: El mejor de ellos para la vista Problemas es cambiar el programa para su uso Char -array con variables Un cadenas de inicialización para A estos efectos de una en su lugar: las constantes de cadena. Pero si esto no es posible, puede usar el indicador -fwritable-strings , que dirige a GCC a manejar las constantes de cadena de la misma manera que lo hacen la mayoría de los compiladores de C.
Fuente: Uso del Manual oficial de la Colección del compilador GNU (GCC 3.3) .
El indicador del compilador -fwritable-strings quedó en desuso en GCC 3.4 y se eliminó permanentemente en GCC 4.0.ANSI C rationale | String literals
String literals are specified to be unmodifiable. This specification allows implementations to share copies of strings with identical text, to place string literals in read-only memory, and perform certain optimizations. However, string literals do not have the type array of const char, in order to avoid the problems of pointer type checking, particularly with library functions, since assigning a pointer to const char to a plain pointer to char is not valid. Those members of the Committee who insisted that string literals should be modifiable were content to have this practice designated a common extension (see F.5.5).
Existing code which modifies string literals can be made strictly conforming by replacing the string literal with an initialized static character array. For instance,
char *p, *make_temp(char *str); p = make_temp("tempXXX");
can be changed to:
char *p, *make_temp(char *str); { static char template[ ] = "tempXXX"; p = make_temp( template ); }
: Rationale for American National Standard for Information Systems, Programming Language C .
El usuario VarfolomeyKote4ka propuso un truco sucio interesante que le permite evitar errores de fallas de segmentación al intentar cambiar los datos en la sección .rodata convirtiéndolo en la sección .rwdata . No hace mucho tiempo, apareció un artículo muy interesante en Internet, "De .rodata a .rwdata: introducción al mapeo de memoria y scripts LD" por el programador guye1296 , que explica cómo hacer este truco. Para facilitar la obtención del resultado deseado, el autor del artículo preparó un script bastante voluminoso para el enlazador estándar ld - rwdata.ld. Es suficiente descargar este script, ponerlo en la raíz del directorio fuente de Perl, corregir el indicador LDFLAGS de la siguiente manera: LDFLAGS = -T rwdata.ld , luego reconstruir el proyecto. Como resultado, tenemos lo siguiente: $ make clean && make -j1 $ mv perl perl_rodata $ curl -LOJ https://raw.githubusercontent.com/guye1296/ld_script_elf_blog_post/master/rwdata.ld $ sed -i 's/LDFLAGS =/LDFLAGS = -T rwdata.ld/' Makefile $ make clean && make -j1 $ mv perl perl_rwdata $ objdump -s -j .rodata perl_rodata | grep tmp -2 19da0 21233f5e 7e3d2d25 30313233 34353637 !
Resulta que gracias a este truco, ¡casi todos los cambios desde el segundo parche pueden simplemente omitirse! Aunque, por supuesto, es preferible llevar el código a una vista que no viole los estándares.<< Saltar al contenido