Nachdem ich die Nachricht „
Perl-Interpreter-Code offiziell auf GitHub portiert “ auf LINUX.ORG.RU gelesen hatte, beschloss ich, mir das Perl 5-Repository anzusehen, das jetzt bereits auf GitHub verfügbar ist.
Es ist erstaunlich, wie sie vor Ehrfurcht und Qualität zitterten und nicht nur die gesamte 32-jährige Geschichte des Projekts absolut bewahrten, sondern auch Fehlerberichte (Probleme), Patches (PRs), Veröffentlichungen und Zweige. Die Inschrift "vor
32 Jahren " neben den Akten sorgt für ein unwillkürliches Lächeln.
Was kann man an diesem trüben Freitagabend noch tun, wenn Regen und Schnee unangenehm auf die Straße nieseln und alle Straßenwege im Herbstbrei versinken? Das stimmt, rotäugig! Aus Gründen des Experiments und des Interesses habe ich mich entschlossen, das alte Perl auf einem modernen x86_64-Computer mit der neuesten Version von
GCC 9.2.0 als Compiler zu
installieren . Kann solch ein alter Code den Test der Zeit bestehen?
Demonstration von twm , einem der ersten Fenstermanager für das X Window System, auf einer modernen Arch Linux-Distribution.Um absolut authentisch und nekromantnenko zu sein, habe ich eine virtuelle Maschine mit Bare X und Window Manager
Twm bereitgestellt , die ebenfalls aus dem Jahr 1987 stammt. Wer weiß, vielleicht hat
Larry Wall sein Perl mit genau
Twm geschrieben , sozusagen mit der
neuesten Technologie dieser Zeit. Die verwendete Distribution ist Arch Linux. Nur weil es einige nützliche Dinge in seinem Repository gibt, die sich später als nützlich erwiesen haben. Also lass uns gehen!
Inhalt:
1. Vorbereitung der Umwelt2. Konfigurieren Sie den Quellcode3. Fehler in der Yacc-Grammatikdatei4. Fehler beim Kompilieren von Code auf "C"5. Korrektur einiger Fehler Segmentierungsfehler6. Zusammenfassend1. Vorbereitung der Umwelt
Zuerst installieren wir auf einem bereitgestellten Betriebssystem in einer virtuellen Maschine alle Gentleman-Dienstprogramme und Compiler, die zum Zusammenstellen und Bearbeiten des Quellcodes erforderlich sind:
gcc ,
make ,
vim ,
git ,
gdb usw. Einige von ihnen sind bereits installiert, während andere im Metapaket verfügbar sind
base-devel muss installiert werden, wenn es nicht installiert ist. Nachdem die Umgebung einsatzbereit ist, erhalten wir eine Kopie des 32 Jahre alten Perl-Quellcodes!
$ git clone https://github.com/Perl/perl5/ --depth=1 -b perl-1.0
Dank der Funktionen von Git müssen wir nicht eine Reihe von Dateien ziehen, um zur ersten Version des Projekts zu gelangen:
* 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
Wir laden nur eine kleine Datenmenge herunter und daher benötigt das Repository mit dem Quellcode der ersten Version von Perl nur 150 KB.
Zu dieser dunklen und dichten Zeit gab es keine elementaren Dinge wie
Autotools (
was für ein Segen! ). Es gibt jedoch ein
Konfigurationsskript im Stammverzeichnis des Repositorys. Was ist los Tatsache ist jedoch, dass Larry Wall der Erfinder solcher Skripte ist, mit denen Makefiles für die buntesten UNIX-Maschinen dieser Zeit generiert werden konnten. Wie der Wikipedia-
Artikel über die gleichnamigen Skripte besagt, hat Larry Wall die
Configure- Datei drei weitere Jahre vor dem Schreiben von Perl mit einem Teil seiner Software geliefert, beispielsweise einem Newsreader. In der Folge war Perl keine Ausnahme, und ein Skript, das bereits auf vielen Computern ausgeführt wurde, wurde zum Erstellen verwendet. Später haben auch andere Entwickler, zum Beispiel Programmierer von Trolltech, diese Idee aufgegriffen. Sie verwendeten ein ähnliches Skript, um den Build ihres Qt-Frameworks zu konfigurieren, das viele Leute mit der
Konfiguration von
Autotools verwechseln. Es war der Zoo solcher Skripte von verschiedenen Entwicklern, der als Anstoß für die Erstellung von Tools für ihre vereinfachte und automatische Generierung diente.
<< Zum Inhalt springen2. Konfigurieren Sie den Quellcode
Das
Konfigurationsskript der „alten Schule“, das bereits aus dem
Shebang hervorgeht , der ein Leerzeichen hat:
$ cat Configure | head -5
Dem Kommentar zufolge gab es Muscheln in den Skripten, deren Kommentare nicht hinterlassen werden konnten! Die Weltraumsituation sieht ungewöhnlich aus, aber sobald dies die Norm war, finden Sie hier den Link für weitere Informationen. Vor allem gibt es für moderne Shell-Interpreten keinen Unterschied, ob es einen Raum gibt oder nicht.
Genug der Texte, lasst uns zur Sache kommen! Wir starten das Skript und sehen eine interessante Annahme, die sich als nicht ganz richtig herausstellt:
$ ./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-->
Überraschenderweise ist das Skript interaktiv und enthält eine Vielzahl verschiedener Hintergrundinformationen. Das Benutzerinteraktionsmodell basiert auf Dialogen und analysiert die Antworten, auf die das Skript seine Parameter ändert, und generiert anschließend Makefiles. Ich war persönlich daran interessiert zu überprüfen, ob alle Shell-Befehle vorhanden sind.
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.
Anscheinend war dies vorher weit davon entfernt. Ich frage mich, wofür das Dienstprogramm
Mcc verantwortlich ist, das nicht gefunden werden konnte. Das Lustige ist, dass dieses Drehbuch in den besten Hacker-Traditionen der Zeit voller freundlicher Humor ist. Jetzt werden Sie das kaum noch sehen:
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.
Ich beantwortete die meisten Fragen mit dem Standardwert oder mit dem, was mir das Skript bot. Besonders erfreut und überrascht war die Anfrage nach Flags für den Compiler und Linker:
Any additional cc flags? [none] Any additional ld flags? [none]
Dort können Sie etwas Interessantes schreiben, z. B.
-m32 , um eine ausführbare 32-Bit-Datei oder eine Bibliothek zu erstellen, die beim Verknüpfen erforderlich ist. Zur letzten Skriptfrage:
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
Ich antwortete positiv. Gemessen an
der Wikipedia-
Seite wurde das alte
Makedepend- Dienstprogramm zu Beginn des
Athena-Projektlebens erstellt , um die Arbeit mit Makefiles zu erleichtern. Dieses Projekt gab uns das X Window System, Kerberos, Zephyr und beeinflusste viele andere Dinge, die heute bekannt sind. Das alles ist wunderbar, aber woher kommt dieses Dienstprogramm in einer modernen Linux-Umgebung? Es wurde lange Zeit von niemandem und irgendwo benutzt. Wenn Sie sich jedoch das Stammverzeichnis des Repositorys genau ansehen, stellt sich heraus, dass Larry Wall seine Ersatzskriptversion geschrieben hat, die wir sorgfältig entpackt und das Konfigurationsskript ausgeführt haben.
Makedepend mit einigen seltsamen Fehlern abgeschlossen:
./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
Vielleicht haben sie das Problem verursacht, aufgrund dessen die generierten Makefiles etwas gekaut wurden:
$ make make: *** No rule to make target '<built-in>', needed by 'arg.o'. Stop.
Ich wollte absolut nicht in den Dschungel der komplizierten Shell-Nudeln des
Makedepend- Dienstprogramms gehen und beschloss, mir die Makefiles genau anzusehen, in denen ein seltsames Muster auftauchte:
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 ...
Anscheinend hat ein Dienstprogramm seine Argumente falsch in den Auspuff eingefügt. Ich nahm das
Axt- Dienstprogramm
sed und beschloss, dieses Problem leicht zu beheben:
$ sed -i '/built-in/d' Makefile $ sed -i '/command-line/d' Makefile
Überraschenderweise funktionierte der Trick und die Makefiles funktionierten wie sie sollten!
<< Zum Inhalt springen3. Fehler in der Yacc-Grammatikdatei
Es wäre unglaublich, wenn der 32 Jahre alte Code ohne Probleme aufgenommen und zusammengestellt würde. Leider geschehen keine Wunder. Beim Studium des
Quellbaums stieß ich auf eine
perl.y- Datei, die eine Grammatikbeschreibung für das Dienstprogramm
yacc darstellt , das in modernen Distributionen seit langem durch
Bison ersetzt wurde. Das Skript im Pfad
/ usr / bin / yacc ruft
Bison einfach im Kompatibilitätsmodus mit
yacc auf . Es ist nur so, dass diese Kompatibilität nicht vollständig ist und bei der Verarbeitung dieser Datei eine Vielzahl von Fehlern auftreten, die ich nicht korrigieren kann und nicht wirklich möchte, da es eine alternative Lösung gibt, die ich kürzlich kennengelernt habe.
Noch vor ein oder zwei Jahren hat Helio Chissini de Castro, der Entwickler von KDE, ähnliche Arbeiten durchgeführt und KDE 1, 2 und Qt 1, 2 an moderne Umgebungen und Compiler angepasst. Ich interessierte mich für seine Arbeit, lud die Quellcodes der Projekte herunter, aber während der Montage stieß ich auf eine ähnliche
Gefahr aufgrund der Inkompatibilität von
Yacc und
Bison , die zum Erstellen der alten Version des
Moc- Metacompilers verwendet wurden. Anschließend gelang es mir, eine Lösung für dieses Problem zu finden, indem
Bison durch
das Dienstprogramm
byacc (Berkeley Yacc) ersetzt wurde, das sich als kompatibel mit alten Grammatiken für
yacc herausstellte und in vielen Linux-Distributionen verfügbar war.
Ein einfaches Ersetzen von
yacc durch
byacc im Build-System hat mir dann geholfen, wenn auch nicht lange, da sie etwas später in neuen Versionen von
byacc immer noch die Kompatibilität mit
yacc brachen und das mit der
yydebug- Entität verbundene Debugging
abbrachen . Daher musste ich
die Utility-
Grammatik etwas korrigieren.
Die Strategie zur Korrektur von Konstruktionsfehlern in der Datei
perl.y wurde daher aufgrund früherer Erfahrungen vorhergesagt: Installieren Sie das Dienstprogramm
byacc , ändern Sie
yacc in
byacc in allen Makefiles und schneiden Sie
yydebug von überall aus. Diese Aktionen lösten alle Probleme mit dieser Datei, die Fehler verschwanden und die Kompilierung wurde fortgesetzt.
<< Zum Inhalt springen4. Fehler beim Kompilieren von Code auf "C"
Perls alter Code war voller Schrecken, wie die längst veraltete und vergessene Notation von Funktionsdefinitionen vom Typ 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); }
Ähnliche Funktionen wurden beispielsweise in
Microsoft Word 1.1a- Code gefunden, der ebenfalls ziemlich alt ist. Der erste Standard der Programmiersprache „C“, genannt „C89“, wird erst in zwei Jahren erscheinen. Moderne Compiler können mit solchem Code arbeiten, aber einige IDEs machen es nicht einfach, solche Definitionen zu analysieren und sie als Syntaxfehler hervorzuheben, z. B. hat
Qt Creator zuvor gesündigt, bevor der darin enthaltene Code in die
libclang- Bibliothek
analysiert wurde .
Der GCC 9.2.0-Compiler, der eine Vielzahl von Warnungen ausspuckte, verpflichtete sich, den alten Code der ersten Version von Perl zu kompilieren. Die Blätter aus den Warnungen waren so groß, dass wir mehrere Seiten des Auspuffs nach oben scrollen mussten, um zum Fehler zu gelangen. Zu meiner Überraschung waren die meisten Kompilierungsfehler typisch und bezogen sich hauptsächlich auf vordefinierte Definitionen, die die Rolle von Flags für die Assembly spielten.
Die Arbeit des modernen GCC 9.2.0-Compilers und des GDB 8.3.1-Debuggers im twm- Fenstermanager und im xterm- Terminalemulator.Unter STDSTDIO experimentierte Larry Wall mit einer alten und nicht standardmäßigen Programmiersprachenbibliothek „C“, und unter DEBUGGING
gab es Debugging-Informationen mit dem berüchtigten
Yydebug , den ich oben erwähnte. Standardmäßig waren diese Kontrollkästchen aktiviert. Durch
Deaktivieren in der Datei
perl.h und Hinzufügen einiger vergessener
Definitionen konnte ich die Anzahl der Fehler erheblich reduzieren.
Eine andere Art von Fehler besteht darin, die jetzt standardisierten Funktionen der Standardbibliothek und der POSIX-Schicht zu überschreiben. Das Projekt hat seine eigenen
malloc () ,
setenv () und andere Entitäten, die Konflikte verursacht haben.
Einige Stellen definierten statische Funktionen ohne Deklarationen. Im Laufe der Zeit gingen die Compiler dieses Problems strenger an und
verwandelten die Warnung in einen Fehler . Und zum Schluss noch ein paar vergessene Überschriften, wohin würden Sie ohne sie gehen?
Zu meiner Überraschung stellte sich heraus, dass der Patch für den 32 Jahre alten Code so klein war, dass er hier vollständig zitiert werden kann:
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();
Tolles Ergebnis für 32 Jahre alten Code! Der
undefinierte Verweis auf den "Crypt" -Verbindungsfehler wurde behoben, indem die Anweisung
-lcrypt mit der entsprechenden
libcrypt- Bibliothek zum Makefile hinzugefügt wurde.
Danach erhielt ich endlich die gewünschte ausführbare Perl-Interpreter-Datei:
$ 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
<< Zum Inhalt springen5. Korrektur einiger Fehler Segmentierungsfehler
Nach einer fast problemlosen Zusammenstellung drehte mir das Glück den Rücken zu. Unmittelbar nach dem Starten des zusammengebauten Perl-Interpreters bekam ich einige seltsame Fehler und am Ende einen Segmentierungsfehler:
$ ./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)
Nachdem der Quelltext für die Phrase
Corrupt malloc genagt worden war, stellte sich heraus, dass ab 1982 anstelle des Systems
malloc () eine Art benutzerdefinierter Allokator aufgerufen wurde. Interessanterweise ist
Berkeley in einem der String-Literale im Quellcode und
Caltech in einem Kommentar daneben geschrieben. Die Zusammenarbeit zwischen diesen Universitäten war damals sehr stark. Im Allgemeinen habe ich diesen Hacker-Allokator auskommentiert und den Quellcode neu erstellt. Speicherbeschädigungsfehler verschwanden, aber der Segmentierungsfehler blieb bestehen. Das war also nicht der Punkt, und jetzt müssen wir den Debugger aufdecken.
Beim Ausführen des Programms unter
gdb stellte ich fest, dass der Absturz auftritt, wenn die Funktion zum Erstellen einer temporären Datei
mktemp () aus libc aufgerufen wird:
$ 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 ()
Übrigens hat der Linker zuvor auf diese Funktion geschworen. Kein Compiler, sondern ein Linker, was mich überrascht hat:
/usr/bin/ld: perl.o: in function `main': perl.c:(.text+0x978c): warning: the use of `mktemp' is dangerous, better use `mkstemp' or `mkdtemp'
Der erste Gedanke, der Ihnen wahrscheinlich auch in den Sinn kam, war, die
unsichere Funktion mktemp () durch
mkstemp () zu ersetzen, was ich auch getan habe. Die Linker-Warnung verschwand, aber der Segmentierungsfehler blieb trotzdem an dieser Stelle, nur jetzt war er in der Funktion
mkstemp () .
Daher müssen Sie sich jetzt den Code, der dieser Funktion zugeordnet ist, sehr genau ansehen. Dort habe ich eine ziemlich seltsame Sache entdeckt, die in diesem Ausschnitt hervorgehoben wird:
char *e_tmpname = "/tmp/perl-eXXXXXX"; int main(void) { mktemp(e_tmpname); e_fp = f_open(e_tmpname, "w"); ... }
Es stellt sich heraus, dass
mktemp () versucht, das Literal für die Maske zu ändern, die sich im Abschnitt
.rodata befindet , der offensichtlich zum Scheitern verurteilt ist. Oder war das vor 32 Jahren akzeptabel, traf sich im Code und funktionierte sogar irgendwie?
Das Ersetzen von
char * e_tmpname durch
char e_tmpname [] hat diesen Segmentierungsfehler behoben und ich konnte das bekommen, was ich den ganzen Abend getötet habe:
$ ./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
Wir haben die Ausführung über die Befehlszeile überprüft, aber was ist mit der Datei? Ich habe die erste „Hallo Welt“ für die Programmiersprache Perl aus dem Internet heruntergeladen:
Dann habe ich versucht, es auszuführen, aber leider wartete der Segmentierungsfehler wieder auf mich. Diesmal an einem ganz anderen Ort:
$ 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 ()
Der folgende interessante Punkt wurde in der Funktion
yyerror () gefunden , ich zitiere das Original-Snippet:
Wieder ist die Situation ähnlich der, über die ich oben geschrieben habe. Die Daten im Abschnitt
.rodata werden erneut geändert . Vielleicht sind es nur Tippfehler wegen Copy-Paste und statt
tname wollten sie
tmpbuf schreiben? Oder steckt wirklich eine versteckte Bedeutung dahinter? In jedem Fall wird durch Ersetzen von
char * tokename [] durch
char tokename [] [32] der Segmentierungsfehler
behoben, und Perl teilt uns Folgendes mit:
$ ./perl test.pl syntax error in file test.pl at line 7, next token "strict" Execution aborted due to compilation errors.
Es stellt sich heraus, dass er nicht alle Arten von neuem
Gebrauch streng mag, das versucht er uns zu sagen! Wenn Sie diese Zeilen in der Datei löschen oder auskommentieren, startet das Programm:
$ ./perl test.pl Hello, World!
<< Zum Inhalt springen6. Zusammenfassend
Tatsächlich habe ich mein Ziel erreicht und den alten Code von 1987 nicht nur kompilieren lassen, sondern auch in einer modernen Linux-Umgebung arbeiten lassen. Zweifellos gibt es immer noch einen großen Stapel verschiedener Segmentierungsfehler, die möglicherweise mit der Größe des Zeigers auf einer 64-Bit-Architektur zusammenhängen. All dies kann nach einigen Abenden mit dem bereitstehenden Debugger gereinigt werden. Dies ist jedoch keine sehr angenehme und ziemlich mühsame Aufgabe. Schließlich war dieses Experiment zunächst als Unterhaltung für einen langweiligen Abend geplant und nicht als vollwertige Arbeit, die beendet werden soll. Gibt es einen praktischen Nutzen aus den ergriffenen Maßnahmen? Vielleicht wird eines Tages ein digitaler Archäologe auf diesen Artikel stoßen und er wird ihm nützlich sein. Aber in der realen Welt sind selbst die Erfahrungen aus solchen Forschungen meiner Meinung nach nicht allzu wertvoll.
Wenn jemand interessiert ist, poste ich einen Satz von zwei Patches. Der erste behebt Kompilierungsfehler und der zweite behebt einige Segmentierungsfehler.
PS Ich beeile mich, Fans von
destruktiven Single-Line-Spielern zu verärgern, das funktioniert hier nicht. Vielleicht ist Perls Version für solche Unterhaltung zu alt.
PPS Alles gut und ein schönes Wochenende. Vielen Dank an
kawaii_neko für eine
kleine Lösung .
Update 28.10.2019
: Ein Benutzer des LINUX.ORG.RU-Forums, der den Spitznamen
utf8nowhere verwendet , hat
in seinem Kommentar zu diesem Artikel recht interessante Links bereitgestellt, deren Informationen nicht nur die Situation mit veränderlichen Zeichenfolgenliteralen verdeutlichen, sondern sogar das oben beschriebene Verwendungsproblem berücksichtigen
mktemp () funktioniert! Lassen Sie mich diese Quellen zitieren, die verschiedene Inkompatibilitäten zwischen dem nicht standardisierten K & R C und dem GNU C beschreiben:
Inkompatibilitäten von GCC
Es gibt einige bemerkenswerte Inkompatibilitäten zwischen GNU C- und K & R-Versionen (Nicht-ISO) von C.
GCC macht String-Konstanten normalerweise schreibgeschützt. Wenn mehrere identisch aussehende Zeichenfolgenkonstanten verwendet werden, speichert GCC nur eine Kopie der Zeichenfolge.
Eine Konsequenz ist, dass Sie mktemp nicht mit einem String-Konstanten-Argument aufrufen können . Die Funktion mktemp ändert immer die Zeichenfolge, auf die das Argument zeigt.
Eine weitere Konsequenz ist, dass sscanf auf einigen Systemen nicht funktioniert, wenn eine Zeichenfolgenkonstante als Formatsteuerzeichenfolge oder Eingabe übergeben wird. Dies liegt daran, dass sscanf fälschlicherweise versucht, in die Zeichenfolgenkonstante zu schreiben.Ebenso fscanf und scanf .
Die beste Lösung für diese Probleme besteht darin, das Programm so zu ändern, dass für diese Zwecke char- Array-Variablen mit Initialisierungszeichenfolgen anstelle von Zeichenfolgenkonstanten verwendet werden. Ist dies jedoch nicht möglich, können Sie das Flag -fwritable-strings verwenden, mit dem GCC angewiesen wird, Zeichenfolgenkonstanten wie die meisten C-Compiler zu behandeln.
Quelle: Verwenden des offiziellen Handbuchs zur GNU Compiler Collection (GCC 3.3) .
Das Compiler-Flag -fwritable-strings wurde in GCC 3.4 veraltet und in GCC 4.0 dauerhaft entfernt.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 .
Benutzer VarfolomeyKote4ka angeboten interessant gryaznenky Hack , die Sie in den Bypass - Fehler Segmentation Fehler erlaubt , wenn Sie Änderungsdaten in Abschnitt versuchen .rodata indem sie sie in einen Abschnitt Umwandlung .rwdata . Vor nicht allzu langer Zeit erschien im Internet ein sehr interessanter Artikel des Programmierers guye1296 mit dem Titel „Von .rodata zu .rwdata - Einführung in die Speicherzuordnung und LD-Skripte“ , in dem erklärt wird, wie dieser Trick ausgeführt wird. Um das gewünschte Ergebnis zu erzielen , hat der Autor des Artikels ein ziemlich umfangreiches Skript für den Standard-Linker ld - rwdata.ld erstellt. Es reicht aus, dieses Skript herunterzuladen, im Stammverzeichnis des Perl-Quellverzeichnisses abzulegen , das LDFLAGS- Flag wie folgt zu korrigieren : LDFLAGS = -T rwdata.ld , und dann das Projekt neu zu erstellen . Als Ergebnis haben wir Folgendes: $ 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 !
Es stellt sich heraus, dass dank dieses Hacks fast alle Änderungen gegenüber dem zweiten Patch einfach weggelassen werden können! Obwohl es natürlich immer noch vorzuziehen ist, den Code in eine Ansicht zu bringen, die nicht gegen Standards verstößt.<< Zum Inhalt springen