Setelah membaca berita "
Kode penerjemah Perl secara resmi porting ke GitHub " di LINUX.ORG.RU, saya memutuskan untuk melihat repositori Perl 5, yang sekarang sudah ada di GitHub.
Sungguh menakjubkan bagaimana mereka gemetar dengan kekaguman dan kualitas, menjaga tidak hanya benar-benar seluruh sejarah 32 tahun proyek, tetapi juga laporan bug (masuk ke Masalah), tambalan (masuk ke PR), rilis dan cabang. Tulisan "
32 tahun yang lalu " di sebelah file menyebabkan senyum tanpa disengaja.
Apa lagi yang harus dilakukan pada Jumat malam yang membosankan ini, ketika hujan dan salju gerimis tidak menyenangkan di jalan, dan semua jalan setapak terperosok dalam lumpur musim gugur? Itu benar, mata merah! Jadi demi percobaan dan minat, saya memutuskan untuk mengambil dan merakit Perl kuno pada mesin x86_64 modern dengan versi terbaru
GCC 9.2.0 sebagai kompiler. Bisakah kode lama seperti itu melewati ujian waktu?
Demonstrasi twm , salah satu manajer jendela pertama untuk Sistem X Window, pada distribusi Arch Linux modern.Untuk sepenuhnya otentik dan nekromantnenko, saya menggunakan mesin virtual dengan bare X dan window manager
twm , yang juga berasal dari tahun 1987. Siapa tahu, mungkin
Larry Wall menulis Perl menggunakan tepat
twm , sehingga untuk berbicara
teknologi pendarahan saat itu. Distribusi yang digunakan adalah Arch Linux. Hanya karena ada beberapa hal berguna dalam repositori yang berguna nanti. Jadi ayo pergi!
Konten:
1. Persiapan lingkungan2. Mengkonfigurasi kode sumber3. Kesalahan tata bahasa file Yacc4. Kesalahan penyusunan kode pada "C"5. Koreksi beberapa kesalahan Segmentasi kesalahan6. Untuk meringkas1. Persiapan lingkungan
Pertama, kita menginstal pada sistem operasi yang dikerahkan dalam mesin virtual semua set utilitas dan kompiler yang diperlukan untuk merakit dan mengedit kode sumber:
gcc ,
make ,
vim ,
git ,
gdb , dll. Beberapa dari mereka sudah diinstal, sementara yang lain tersedia dalam paket meta
base-devel , itu harus diinstal jika tidak diinstal. Setelah lingkungan siap beraksi, kami mendapatkan salinan kode sumber Perl yang berusia 32 tahun!
$ git clone https://github.com/Perl/perl5/ --depth=1 -b perl-1.0
Berkat fitur Git, kami tidak perlu menyeret banyak file untuk sampai ke rilis pertama proyek:
* 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
Kami hanya mengunduh sejumlah kecil data dan sebagai hasilnya, repositori dengan kode sumber versi pertama Perl hanya membutuhkan 150 KB.
Pada waktu yang gelap dan padat itu tidak ada hal mendasar seperti
autotool (
betapa berkahnya! ), Namun, ada skrip
Konfigurasi di root repositori. Ada apa? Tetapi kenyataannya adalah bahwa Larry Wall adalah penemu skrip semacam itu yang memungkinkan menghasilkan Makefile untuk mesin UNIX yang paling beraneka ragam pada waktu itu. Seperti
artikel Wikipedia tentang skrip
dengan nama yang sama mengatakan, Larry Wall menyediakan file
Configure dengan beberapa perangkat lunaknya, misalnya, pembaca berita, tiga tahun lagi sebelum menulis Perl. Selanjutnya, Perl tidak terkecuali, dan skrip yang sudah berjalan pada banyak mesin digunakan untuk membangunnya. Kemudian, pengembang lain, misalnya, programmer dari Trolltech, juga mengambil ide ini. Mereka menggunakan skrip yang serupa untuk mengonfigurasi pembuatan kerangka Qt mereka, yang membingungkan banyak orang dengan
konfigurasi dari
autotools . Itu adalah kebun binatang dari skrip semacam itu dari pengembang yang berbeda yang berfungsi sebagai dorongan untuk membuat alat untuk generasi mereka yang disederhanakan dan otomatis.
<< Lompat ke konten2. Mengkonfigurasi kode sumber
Skrip
Konfigurasi "sekolah lama", yang sudah terbukti dari
Shebang ', yang memiliki spasi:
$ cat Configure | head -5
Menurut komentar, ternyata ada kerang di skrip yang tidak mungkin untuk meninggalkan komentar! Situasi ruang terlihat tidak biasa, tetapi begitu ini adalah norma, lihat tautan untuk informasi lebih lanjut di
sini . Yang paling penting, tidak ada perbedaan bagi penafsir shell modern apakah ada ruang atau tidak.
Cukup dengan liriknya, mari kita mulai bisnis! Kami memulai skrip dan melihat asumsi yang menarik, yang ternyata tidak sepenuhnya benar:
$ ./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-->
Anehnya, skripnya interaktif dan berisi banyak sekali informasi latar belakang yang beragam. Model interaksi pengguna dibangun di atas dialog, menganalisis jawaban di mana skrip mengubah parameternya, yang menurutnya selanjutnya akan menghasilkan Makefiles. Saya pribadi tertarik untuk memeriksa apakah semua perintah shell sudah ada?
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.
Rupanya sebelum ini jauh dari kasus. Saya ingin tahu apa yang menjadi tanggung jawab utilitas
PKS , yang tidak dapat ditemukan? Yang lucu adalah bahwa skrip ini dalam tradisi hacker terbaik saat itu penuh dengan humor ramah. Sekarang Anda hampir tidak akan melihat ini:
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.
Saya menjawab sebagian besar pertanyaan dengan nilai default, atau dengan apa yang ditawarkan skrip kepada saya. Yang terutama senang dan terkejut adalah permintaan bendera untuk penyusun dan tautan:
Any additional cc flags? [none] Any additional ld flags? [none]
Di sana Anda dapat menulis sesuatu yang menarik, misalnya,
-m32 untuk membangun file yang dapat dieksekusi 32-bit atau perpustakaan, yang diperlukan selama menghubungkan. Untuk pertanyaan skrip terakhir:
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
Saya menjawab dengan positif. Dilihat oleh
halaman Wikipedia
-nya , utilitas kuno yang diciptakan diciptakan pada awal kehidupan
proyek Athena untuk memfasilitasi pekerjaan dengan Makefiles. Proyek ini memberi kami Sistem X Window, Kerberos, Zephyr dan memengaruhi banyak hal lain yang akrab saat ini. Semua ini luar biasa, tetapi dari mana datangnya utilitas ini di lingkungan Linux modern? Sudah lama digunakan oleh siapa pun dan di mana saja. Tetapi jika Anda melihat dengan dekat pada akar repositori, ternyata Larry Wall menulis versi skrip penggantinya, yang kami bungkus dengan hati-hati dan mengeksekusi skrip konfigurasi.
Makedepend dilengkapi dengan beberapa kesalahan aneh:
./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
Mungkin merekalah yang menyebabkan masalah karena Makefile yang dihasilkan sedikit dikunyah:
$ make make: *** No rule to make target '<built-in>', needed by 'arg.o'. Stop.
Saya benar-benar tidak ingin pergi ke hutan mie kerang yang rumit dari utilitas yang
tergantung pada dan saya memutuskan untuk hati-hati melihat Makefiles, di mana pola aneh muncul:
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 ...
Rupanya beberapa utilitas salah memasukkan argumennya ke knalpot. Mengambil
sed utilitas
kapak , saya memutuskan untuk sedikit memperbaiki hal ini:
$ sed -i '/built-in/d' Makefile $ sed -i '/command-line/d' Makefile
Secara mengejutkan triknya berhasil dan Makefiles bekerja sebagaimana mestinya!
<< Lompat ke konten3. Kesalahan tata bahasa file Yacc
Akan sulit dipercaya jika kode 32 tahun itu diambil dan dikumpulkan tanpa masalah. Sayangnya, mukjizat tidak terjadi. Mempelajari pohon sumber, saya menemukan file
perl.y , yang merupakan deskripsi tata bahasa untuk utilitas
yacc , yang telah lama digantikan oleh
bison dalam distribusi modern. Script yang terletak di path
/ usr / bin / yacc cukup memanggil
bison dalam mode kompatibilitas dengan
yacc . Hanya saja kompatibilitas ini tidak lengkap dan ketika memproses file ini banyak sekali kesalahan yang terjadi, yang saya tidak tahu cara memperbaikinya dan tidak benar-benar ingin melakukannya, karena ada solusi alternatif yang saya pelajari baru-baru ini.
Hanya satu atau dua tahun yang lalu, Helio Chissini de Castro, yang merupakan pengembang KDE, melakukan pekerjaan serupa dan mengadaptasi KDE 1, 2 dan Qt 1, 2 dengan lingkungan dan kompiler modern. Saya menjadi tertarik pada karyanya, mengunduh kode sumber proyek, tetapi selama perakitan saya menemukan
perangkap yang sama karena ketidakcocokan
yacc dan
bison , yang digunakan untuk membangun versi kuno dari metacompiler
moc . Selanjutnya, saya berhasil menemukan solusi untuk masalah ini dalam bentuk mengganti
bison dengan utilitas
byacc (Berkeley Yacc), yang ternyata kompatibel dengan tata bahasa lama untuk
yacc dan tersedia di banyak distribusi Linux.
Penggantian sederhana
yacc dengan
byacc dalam sistem build membantu saya saat itu, meskipun tidak lama, karena sedikit kemudian dalam versi baru
byacc mereka masih merusak kompatibilitas dengan
yacc , menghentikan debugging yang terkait dengan entitas
yydebug . Karena itu, saya harus sedikit
memperbaiki tata bahasa utilitas.
Jadi, strategi untuk memperbaiki kesalahan konstruksi dalam file
perl.y diprediksi oleh pengalaman sebelumnya: instal utilitas
byacc , ubah
yacc menjadi
byacc di semua Makefiles, lalu potong
yydebug dari mana-mana. Tindakan ini menyelesaikan semua masalah dengan file ini, kesalahannya hilang dan kompilasi berlanjut.
<< Lompat ke konten4. Kesalahan penyusunan kode pada "C"
Kode kuno Perl penuh dengan kengerian, seperti notasi fungsi yang sudah usang dan terlupakan dari tipe 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); }
Fitur serupa ditemukan, misalnya, dalam kode
Microsoft Word 1.1a , yang juga cukup kuno. Standar pertama bahasa pemrograman "C", yang disebut "C89", hanya akan muncul dalam dua tahun. Kompiler modern dapat bekerja dengan kode seperti itu, tetapi beberapa IDE tidak membuatnya mudah untuk mengurai definisi seperti itu dan menyorotnya sebagai kesalahan sintaksis, misalnya,
Qt Creator berdosa sebelumnya sebelum menguraikan kode di dalamnya ke perpustakaan
libclang .
Kompiler GCC 9.2.0, yang memuntahkan sejumlah besar peringatan, berupaya mengkompilasi kode kuno versi pertama Perl. Lembar dari peringatan itu sangat besar sehingga untuk mendapatkan kesalahan, kami harus menggulir beberapa halaman knalpot ke atas. Yang mengejutkan saya, sebagian besar kesalahan kompilasi adalah khas dan terutama terkait dengan definisi yang telah ditetapkan, yang memainkan peran bendera untuk perakitan.
Pekerjaan kompiler GCC 9.2.0 modern dan debugger GDB 8.3.1 di manajer jendela twm dan emulator terminal xterm .Di bawah STDSTDIO
, Larry Wall bereksperimen dengan beberapa perpustakaan bahasa pemrograman kuno dan non-standar "C", dan di bawah DEBUGGING
ada informasi debug dengan
yydebug terkenal, yang saya sebutkan di atas. Secara default, flag ini diaktifkan. Dengan mematikannya di file
perl.h dan menambahkan beberapa definisi yang terlupakan, saya dapat secara signifikan mengurangi jumlah kesalahan.
Jenis kesalahan lain adalah mengesampingkan fungsi standar sekarang dari pustaka standar dan lapisan POSIX. Proyek ini memiliki
malloc () ,
setenv () dan entitas lain yang menciptakan konflik.
Beberapa tempat mendefinisikan fungsi statis tanpa deklarasi. Seiring waktu, penyusun mulai mengambil pendekatan yang lebih ketat untuk masalah ini dan
mengubah peringatan menjadi kesalahan . Dan akhirnya, beberapa header yang terlupakan, ke mana Anda pergi tanpa mereka.
Yang mengejutkan saya, tambalan untuk kode 32 tahun itu ternyata sangat kecil sehingga dapat sepenuhnya dikutip di sini:
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();
Hasil hebat untuk kode berusia 32 tahun!
Referensi yang tidak terdefinisi untuk bug penghubung
`crypt ' diperbaiki dengan menambahkan direktif
-lcrypt ke Makefile dengan pustaka
libcrypt yang sesuai, setelah itu saya akhirnya mendapatkan interpreter Perl yang diinginkan dapat dieksekusi:
$ 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
<< Lompat ke konten5. Koreksi beberapa kesalahan Segmentasi kesalahan
Setelah kompilasi yang nyaris tanpa kerumitan, keberuntungan berbalik pada saya. Segera setelah memulai interpreter Perl yang dirakit, saya mendapatkan beberapa kesalahan aneh dan kesalahan Segmentasi pada akhirnya:
$ ./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)
Setelah menggerogoti teks sumber untuk frasa
Corrupt malloc , ternyata alih-alih sistem
malloc () semacam alokasi kustom dipanggil dari 1982. Menariknya,
Berkeley ditulis dalam salah satu string literal dalam kode sumbernya, dan
Caltech dalam komentar di sebelahnya. Kolaborasi antara universitas-universitas ini terbukti sangat kuat. Secara umum, saya berkomentar pengalokasi hacker ini dan membangun kembali kode sumber. Kesalahan kerusakan memori hilang, tetapi kesalahan Segmentasi tetap. Jadi bukan itu intinya, dan sekarang kita perlu mengungkap debugger.
Menjalankan program di bawah
gdb, saya menemukan bahwa crash terjadi ketika fungsi untuk membuat file sementara
mktemp () dari libc disebut:
$ 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 ()
By the way, linker sebelumnya bersumpah pada fungsi ini. Bukan kompiler, tapi tautan, yang mengejutkan saya:
/usr/bin/ld: perl.o: in function `main': perl.c:(.text+0x978c): warning: the use of `mktemp' is dangerous, better use `mkstemp' or `mkdtemp'
Pikiran pertama yang mungkin terlintas di pikiran Anda juga adalah mengganti
fungsi mktemp () yang tidak aman dengan
mkstemp () , yang saya lakukan. Peringatan linker menghilang, tetapi kesalahan Segmentasi tetap di tempat ini, hanya saja sekarang berada di fungsi
mkstemp () .
Oleh karena itu, sekarang Anda perlu melihat dengan sangat hati-hati pada potongan kode yang terkait dengan fungsi ini. Di sana saya menemukan hal yang agak aneh yang disorot dalam cuplikan ini:
char *e_tmpname = "/tmp/perl-eXXXXXX"; int main(void) { mktemp(e_tmpname); e_fp = f_open(e_tmpname, "w"); ... }
Ternyata
mktemp () sedang mencoba mengubah literal untuk topeng, yang terletak di bagian
.rodata , yang jelas-jelas ditakdirkan untuk gagal. Atau, setelah semua, 32 tahun yang lalu, ini dapat diterima, bertemu dalam kode, dan bahkan berhasil?
Tentu saja, mengganti
char * e_tmpname dengan
char e_tmpname [] memperbaiki kesalahan Segmentasi ini dan saya bisa mendapatkan apa yang saya bunuh sepanjang malam:
$ ./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
Kami memeriksa eksekusi dari baris perintah, tetapi bagaimana dengan file? Saya mengunduh "Hello World" pertama untuk bahasa pemrograman Perl dari Internet:
Kemudian saya mencoba menjalankannya, tetapi, sayangnya, kesalahan Segmentasi menunggu saya lagi. Kali ini di tempat yang sama sekali berbeda:
$ 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 ()
Poin menarik berikut ditemukan dalam fungsi
yyerror () , saya kutip cuplikan asli:
Sekali lagi, situasinya mirip dengan yang saya tulis di atas. Data di bagian
.rodata dimodifikasi lagi . Mungkin itu hanya kesalahan ketik karena Copy-Paste dan bukannya
tname mereka ingin menulis
tmpbuf ? Atau benar-benar ada semacam makna tersembunyi di baliknya? Bagaimanapun, mengganti
char * tokename [] dengan
char tokename [] [32] menghapus kesalahan kesalahan Segmentasi dan Perl memberi tahu kami hal berikut:
$ ./perl test.pl syntax error in file test.pl at line 7, next token "strict" Execution aborted due to compilation errors.
Ternyata dia tidak suka segala macam
penggunaan bermodel
ketat , itulah yang dia coba sampaikan kepada kita! Jika Anda menghapus atau mengomentari baris-baris ini dalam file, program dimulai:
$ ./perl test.pl Hello, World!
<< Lompat ke konten6. Untuk meringkas
Bahkan, saya mencapai tujuan saya dan membuat kode kuno dari 1987 tidak hanya mengkompilasi, tetapi juga bekerja di lingkungan Linux modern. Tidak diragukan lagi, masih ada banyak kesalahan Segmentasi kesalahan, mungkin terkait dengan ukuran pointer pada arsitektur 64-bit. Semua ini dapat dibersihkan setelah duduk beberapa malam dengan debugger di siap. Tapi ini bukan tugas yang sangat menyenangkan dan agak membosankan. Lagipula, awalnya eksperimen ini direncanakan sebagai hiburan untuk malam yang membosankan, dan bukan sebagai pekerjaan penuh, yang akan diakhiri. Apakah ada manfaat praktis dari tindakan yang diambil? Mungkin suatu hari beberapa arkeolog digital akan menemukan artikel ini dan itu akan bermanfaat baginya. Tetapi di dunia nyata, bahkan pengalaman yang ditarik dari penelitian seperti itu, menurut saya, tidak terlalu berharga.
Jika ada yang tertarik, saya memposting satu set dua tambalan. Yang pertama memperbaiki kesalahan kompilasi, dan yang kedua memperbaiki beberapa kesalahan kesalahan Segmentasi.
PS Saya cepat-cepat membuat marah para penggemar
pemain lini tunggal yang merusak , ini tidak bekerja di sini. Mungkin versi Perl terlalu tua untuk hiburan seperti itu.
PPS Semua baik dan selamat berakhir pekan. Berkat
kawaii_neko untuk
perbaikan kecil .
Pembaruan 28-Okt-2019: Pengguna forum LINUX.ORG.RU, menggunakan nama panggilan
utf8nowhere , memberikan tautan yang cukup menarik
dalam komentarnya pada artikel ini, informasi yang darinya tidak hanya mengklarifikasi situasi dengan string string yang dapat diubah, tetapi bahkan mempertimbangkan masalah penggunaan yang dijelaskan di atas fungsi
mktemp () ! Izinkan saya mengutip sumber-sumber ini, yang menggambarkan berbagai ketidakcocokan antara K&R C yang tidak standar dan GNU C:
Ketidakcocokan GCC
Ada beberapa ketidaksesuaian yang patut dicatat antara GNU C dan K&R (non-ISO) versi C.
GCC biasanya membuat konstanta string hanya-baca. Jika beberapa konstanta string yang tampak identik digunakan, GCC hanya menyimpan satu salinan string.
Salah satu konsekuensinya adalah Anda tidak dapat memanggil mktemp dengan argumen string konstan. Fungsi mktemp selalu mengubah string yang ditunjukkan argumennya.
Konsekuensi lain adalah bahwa sscanf tidak berfungsi pada beberapa sistem ketika melewati string konstan sebagai string kontrol atau input formatnya.Ini karena sscanf salah mencoba untuk menulis ke dalam konstanta string. Demikian juga fscanf dan scanf .
Solusi terbaik untuk masalah ini adalah mengubah program untuk menggunakan variabel char -array dengan string inisialisasi untuk keperluan ini alih-alih konstanta string. Tetapi jika ini tidak memungkinkan, Anda dapat menggunakan flag -fwritable-string , yang mengarahkan GCC untuk menangani konstanta string dengan cara yang sama seperti kebanyakan kompiler C lakukan.
Sumber: Menggunakan Manual Resmi GNU Compiler Collection (GCC 3.3) .
Bendera compiler -fwritable-string tidak lagi digunakan dalam GCC 3.4 dan dihapus secara permanen di 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 .
Pengguna VarfolomeyKote4ka mengusulkan peretasan kotor yang menarik yang memungkinkan Anda untuk memotong kesalahan Segmentasi ketika mencoba mengubah data di bagian .rodata dengan mengonversinya ke bagian .rwdata . Belum lama ini, sebuah artikel yang sangat menarik muncul di Internet, "Dari .rodata ke .rwdata - pengantar pemetaan memori dan skrip LD" oleh programmer guye1296 , yang menceritakan bagaimana melakukan trik ini. Untuk memfasilitasi mendapatkan hasil yang diinginkan, penulis artikel menyiapkan skrip yang agak tebal untuk tautan standar ld - rwdata.ld. Cukup mengunduh skrip ini, letakkan di root direktori sumber Perl, perbaiki tanda LDFLAGS sebagai berikut: LDFLAGS = -T rwdata.ld , lalu bangun kembali proyek. Sebagai hasilnya, kami memiliki yang berikut: $ 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 !
Ternyata berkat peretasan ini, hampir semua perubahan dari tambalan kedua dapat dihilangkan! Meskipun, tentu saja, membawa kode ke tampilan yang tidak melanggar standar masih lebih disukai.<< Lompat ke konten