بعد قراءة الأخبار "
كود مترجم بيرل الذي تم نقله رسميًا إلى جيثب " على LINUX.ORG.RU ، قررت إلقاء نظرة على مستودع بيرل 5 ، الموجود حاليًا على جيثب.
إنه لأمر مدهش كيف ارتجفوا بالرهبة والجودة ، مع الحفاظ على ليس فقط على مدار تاريخ المشروع بأكمله لمدة 32 عامًا ، ولكن أيضًا على تقارير الأخطاء (حصلت على المشكلات) ، والبقع (حصلت على عناوين العلاقات العامة) ، والإصدارات والفروع. النقش "
قبل 32 عامًا " بجوار الملفات يؤدي إلى ابتسامة لا إرادية.
ما الذي يجب فعله في ليلة الجمعة المليئة هذه ، عندما تسقط الأمطار والثلوج في الشارع بصورة غير سارة ، وكل طرق الشوارع غارقة في طين الخريف؟ هذا صحيح ، أحمر العينين! من أجل التجربة والاهتمام ، قررت أن
أجمع وتجميع Perl القديم على جهاز x86_64 الحديث مع أحدث إصدار من
GCC 9.2.0 كمترجم. هل يمكن لمثل هذا الكود القديم اجتياز اختبار الزمن؟
مظاهرة لـ twm ، أحد مديري النوافذ الأوائل لنظام X Window System ، على توزيع Arch Linux الحديث.ولكي أكون أصيلًا تمامًا و nekromantnenko ، قمت بنشر جهاز افتراضي به X
وعارضة مدير
twm ، والتي تنحدر أيضًا من عام 1987. من يدري ، ربما كتب
لاري وول بيرل له باستخدام
twm بالضبط ، إذا جاز التعبير عن
تكنولوجيا النزيف في ذلك الوقت. التوزيع المستخدم هو Arch Linux. لمجرد وجود بعض الأشياء المفيدة في مستودعه والتي أصبحت مفيدة في وقت لاحق. لذلك دعونا نذهب!
المحتويات:
1. إعداد البيئة2. تكوين شفرة المصدر3. YAC أخطاء ملف القواعد4. أخطاء تجميع التعليمات البرمجية على "C"5. تصحيح بعض الأخطاء خطأ التقسيم6. لتلخيص1. إعداد البيئة
أولاً ، نثبّت على نظام تشغيل منشور في جهاز ظاهري جميع مجموعة الأدوات المساعدة والمُجمِّعين اللازمة
لتكوين وتحرير الكود المصدري:
gcc ، و
make ، و
vim ، و
git ، و
gdb ، إلخ. بعضها مُثبَّت بالفعل ، في حين أن البعض الآخر متوفر في الحزمة التعريفية
الأساس ، يجب تثبيته إذا لم يتم تثبيته. بعد أن تكون البيئة جاهزة للعمل ، نحصل على نسخة من شفرة مصدر بيرل البالغة من العمر 32 عامًا!
$ git clone https://github.com/Perl/perl5/ --depth=1 -b perl-1.0
بفضل ميزات Git ، لا نحتاج إلى سحب مجموعة من الملفات للوصول إلى الإصدار الأول من المشروع:
* 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
نقوم فقط بتنزيل كمية صغيرة من البيانات ، ونتيجة لذلك ، يستغرق المستودع الذي يحتوي على الكود المصدري للإصدار الأول من Perl 150 كيلوبايت فقط.
في ذلك الوقت المظلم والكثيف لم يكن هناك شيء أولي مثل
autotools (
يا له من نعمة! ) ، ومع ذلك ، هناك برنامج نصي
تكوين في جذر المستودع. ما هو الموضوع؟ ولكن الحقيقة هي أن لاري وول هو مخترع هذه النصوص التي سمحت بتوليد Makefiles لأكثر آلات UNIX متنافرة في ذلك الوقت. كما يقول
مقال ويكيبيديا عن البرامج النصية التي تحمل
نفس الاسم ، زود Larry Wall ملف
Configure ببعض برامجه ، على سبيل المثال ، قارئ أخبار ، قبل ثلاث سنوات أخرى من كتابة Perl. بعد ذلك ، لم يكن Perl استثناءً ، وتم استخدام البرنامج النصي الذي تم تشغيله بالفعل على العديد من الأجهزة لإنشاءه. في وقت لاحق ، اختار مطورو البرامج الآخرون ، على سبيل المثال ، المبرمجون من Trolltech ، هذه الفكرة أيضًا. لقد استخدموا نصًا مشابهًا لتكوين بناء إطار عمل Qt الخاص بهم ، والذي يخلط كثير من الناس مع
التكوين من
autotools . كانت حديقة الحيوان لهذه البرامج النصية من مطورين مختلفين بمثابة حافز لإنشاء أدوات لجيلهم المبسط والآلي.
<< تخطى إلى المحتوى2. تكوين شفرة المصدر
البرنامج النصي
لتكوين "المدرسة القديمة" ، والذي يتضح بالفعل من موقع
Shebang الخاص به ، والذي يحتوي على مساحة:
$ cat Configure | head -5
وفقا للتعليق ، اتضح أن هناك قذائف في النصوص التي لم يكن من الممكن ترك التعليقات! يبدو وضع الفضاء غير عادي ، ولكن بمجرد أن كان هذا هو المعيار ، راجع الرابط لمزيد من المعلومات
هنا . الأهم من ذلك ، لا يوجد فرق للمترجمين الفوريين للقذيفة سواء كانت هناك مساحة أم لا.
بما فيه الكفاية من كلمات ، دعونا ننكب على العمل! نبدأ البرنامج النصي ونرى افتراضًا مثيرًا للاهتمام ، والذي اتضح أنه غير صحيح تمامًا:
$ ./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-->
والمثير للدهشة أن البرنامج النصي تفاعلي ويحتوي على مجموعة كبيرة من المعلومات الأساسية المختلفة. يعتمد نموذج تفاعل المستخدم على مربعات الحوار ، ويحلل الإجابات التي يغير البرنامج النصي معالمه إليها ، والتي ستنشئ فيما بعد Makefiles. كنت مهتمًا شخصيًا بالتحقق مما إذا كانت جميع أوامر shell موجودة؟
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.
على ما يبدو قبل هذا كان بعيدا عن القضية. أتساءل عن الأداة المساعدة
Mcc التي لم يتم العثور عليها؟ والشيء المضحك هو أن هذا السيناريو في أفضل تقاليد القراصنة في ذلك الوقت مليء بروح الدعابة. الآن لن ترى هذا بالكاد:
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.
أجبت على معظم الأسئلة بالقيمة الافتراضية ، أو بما قدمه لي النص البرمجي. كان من دواعي سروري وفاجأ بشكل خاص طلب إشارات المترجم والرابط:
Any additional cc flags? [none] Any additional ld flags? [none]
هناك يمكنك كتابة شيء مثير للاهتمام ، على سبيل المثال ، -
m32 لإنشاء ملف قابل للتنفيذ 32 بت أو مكتبة ، وهو أمر مطلوب أثناء الارتباط. على السؤال النصي الأخير:
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
أجبت بشكل إيجابي. استنادا
إلى صفحة ويكيبيديا ، تم إنشاء الأداة المساعدة القديمة القديمة في بداية حياة
مشروع أثينا لتسهيل العمل مع Makefiles. لقد قدم لنا هذا المشروع نظام X Window ، و Kerberos ، و Zephyr ، وأثر على العديد من الأشياء الأخرى المعروفة اليوم. كل هذا رائع ، لكن من أين تأتي هذه الأداة في بيئة Linux الحديثة؟ منذ فترة طويلة تستخدم من قبل لا أحد وفي أي مكان. ولكن إذا نظرت عن كثب إلى جذر المستودع ، اتضح أن لاري وول كتب نسخة البرنامج النصي البديلة ، التي فكّرناها بعناية ونفذناها في برنامج التكوين.
Makedepend إكمال مع بعض الأخطاء الغريبة:
./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
ربما كانوا هم الذين تسببوا في المشكلة بسبب التي تم مضغها Makefiles ولدت قليلا:
$ make make: *** No rule to make target '<built-in>', needed by 'arg.o'. Stop.
لم أكن أرغب مطلقًا في الدخول في غابة الشعرية المعقدة
للأداة المساعدة وقررت أن أنظر بعناية إلى Makefiles ، حيث ظهر نمط غريب:
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 ...
على ما يبدو بعض الأداة المساعدة بشكل غير صحيح إدراج الوسائط الخاصة به في العادم. التقاط الأداة المساعدة
sed ax ، قررت إصلاح هذا الشيء قليلاً:
$ sed -i '/built-in/d' Makefile $ sed -i '/command-line/d' Makefile
من المثير للدهشة أن الحيلة عملت وعمل Makefiles كما ينبغي!
<< تخطى إلى المحتوى3. YAC أخطاء ملف القواعد
سيكون أمرًا لا يصدق إذا أخذ الكود البالغ من العمر 32 عامًا وتجميعه دون أي مشاكل. لسوء الحظ ، المعجزات لا تحدث. أثناء دراسة الشجرة المصدر ،
صادفت ملفًا
perl.y ، وهو وصف لقواعد اللغة لفائدة
yacc ، والتي تم استبدالها بـ
bison في التوزيعات الحديثة. البرنامج النصي الموجود على المسار
/ usr / bin / yacc ببساطة يدعو
bison في وضع التوافق مع
yacc . إن هذا التوافق لم يكتمل ، وعند معالجة هذا الملف ، تتدفق مجموعة كبيرة من الأخطاء ، وهو ما لا أعرف كيفية تصحيحه ولا أريده حقًا ، لأنه يوجد حل بديل تعلمته مؤخرًا.
منذ عام أو عامين فقط ، قام Helio Chissini de Castro ، وهو مطور KDE ، بعمل مماثل وقام بتكييف KDE 1 و 2 و Qt 1، 2 مع البيئات الحديثة والمجمعين. أصبحت مهتمة بعمله ،
وقمت بتنزيل
الكود المصدري للمشروعات ، لكن خلال التجمع
واجهت مشكلة مماثلة بسبب عدم توافق
yacc و
bison ، والتي كانت تستخدم لبناء النسخة القديمة من
metacompiler moc. بعد ذلك ، تمكنت من إيجاد حل لهذه المشكلة في شكل استبدال
bison بأداة مساعدة
byacc (Berkeley
Yacc ) ، والتي اتضح أنها متوافقة مع القواعد النحوية القديمة لـ
yacc وكانت متوفرة في العديد من توزيعات Linux.
ساعدني الاستبدال البسيط لـ
yacc بـ
byacc في نظام
الإنشاء ، ولكن ليس لفترة طويلة ، لأنه بعد ذلك بقليل في الإصدارات الجديدة من
byacc ما زالوا
يكسرون التوافق مع
yacc ، مع إيقاف تصحيح الأخطاء المرتبط بالكيان
yydebug . لذلك ، كان لا بد لي من
إصلاح الأداة المساعدة قليلا.
لذلك ، تم التنبؤ باستراتيجية تصحيح أخطاء البناء في ملف
perl.y بواسطة التجربة السابقة: تثبيت الأداة المساعدة
byacc ، وتغيير
yacc إلى
byacc في جميع Makefiles ، ثم قص
yydebug من كل مكان. هذه الإجراءات حل جميع المشاكل مع هذا الملف ، اختفت الأخطاء واستمرت عملية التجميع.
<< تخطى إلى المحتوى4. أخطاء تجميع التعليمات البرمجية على "C"
كان رمز بيرل القديم مليئًا بالأهوال ، مثل التدوين الذي عفا عليه الزمن والمنسى للتعريفات الوظيفية لنوع 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); }
تم العثور على ميزات مشابهة ، على سبيل المثال ، في رمز
Microsoft Word 1.1a ، وهو أيضًا قديم جدًا. سيظهر المعيار الأول للغة البرمجة "C" ، المسماة "C89" ، في غضون عامين فقط.
المترجمون العصريون قادرون على التعامل مع مثل هذا الكود ، لكن بعض
معرفات IDE لا تسهل تحليل مثل هذه التعريفات وتسليط الضوء عليها كأخطاء في بناء الجملة ، على سبيل المثال ،
Qt Creator أخطأ من قبل قبل تحليل الكود في مكتبة
libclang .
قام المحول البرمجي 9.2.0 لدول مجلس التعاون الخليجي ، الذي قام بعدد كبير من التحذيرات ، بتجميع الكود القديم للنسخة الأولى من بيرل. كانت الأوراق من التحذيرات كبيرة لدرجة أنه من أجل الوصول إلى الخطأ ، اضطررنا إلى التمرير عدة صفحات من العادم. لدهشتي ، كانت معظم أخطاء التجميع نموذجية وترتبط بشكل رئيسي بتعريفات محددة مسبقًا ، والتي لعبت دور الإشارات للتجميع.
عمل برنامج التحويل البرمجي 9.2.0 لدول مجلس التعاون الخليجي و GDB 8.3.1 المصحح في مدير نافذة twm ومحاكي xterm .تحت STDSTDIO
، جرب Larry Wall بعض مكتبة لغة البرمجة القديمة وغير القياسية "C" ، وتحت DEBUGGING كانت
هناك معلومات تصحيح الأخطاء مع
yydebug سيئة السمعة ، والتي ذكرتها أعلاه. بشكل افتراضي ، تم تمكين خانات الاختيار هذه. بإيقاف تشغيلها في ملف
perl.h وإضافة بعض
التعريفات المنسية ، تمكنت من تقليل عدد الأخطاء بشكل كبير.
هناك نوع آخر من الأخطاء هو تجاوز الوظائف القياسية للمكتبة القياسية وطبقة POSIX القياسية. يحتوي المشروع على
malloc () و
setenv () والكيانات الأخرى التي خلقت الصراعات.
زوجان من الأماكن المحددة وظائف ثابتة دون تصريحات. مع مرور الوقت ، بدأ المترجمون في اتباع نهج أكثر صرامة لهذه المشكلة
وتحويل التحذير إلى خطأ . وأخيراً ، بضع رؤوس منسية ، أين تذهب بدونها.
لدهشتي ، اتضح أن رمز الكود البالغ من العمر 32 عامًا صغير جدًا لدرجة أنه يمكن ذكره بالكامل هنا:
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();
نتيجة رائعة لرمز عمره 32 عامًا! تم إصلاح
المرجع غير المحدد إلى علة ربط
'crypt' بإضافة توجيه
-crypt إلى Makefile مع مكتبة
libcrypt المناسبة ، وبعد ذلك حصلت أخيرًا على مترجم Perl المطلوب القابل للتنفيذ:
$ 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
<< تخطى إلى المحتوى5. تصحيح بعض الأخطاء خطأ التقسيم
بعد تجميع خالي من المتاعب تقريباً ، أدار الحظ ظهره لي. بعد بدء تشغيل مترجم بيرل المجمع مباشرة ، حصلت على بعض الأخطاء الغريبة وخطأ في التقسيم في النهاية:
$ ./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)
بعد إهمال النص المصدر لعبارة
Corrupt malloc ، اتضح أنه بدلاً من النظام
malloc () تم استدعاء نوع من المخصص المخصص منذ عام 1982. ومن المثير للاهتمام ، أن
بيركلي مكتوب بأحد السلسلة الحرفية في الكود المصدر ،
وكالتش في تعليق بجانبه. كان التعاون بين هذه الجامعات واضحًا وقويًا جدًا. بشكل عام ، علقت خارج هذا القراصنة مخصص وإعادة بناء التعليمات البرمجية المصدر. اختفت أخطاء تلف الذاكرة ، ولكن ظل خطأ التقسيم. لم تكن هذه هي النقطة ، والآن نحن بحاجة إلى الكشف عن مصحح الأخطاء.
عند تشغيل البرنامج تحت
gdb ، وجدت أن التعطل يحدث عندما يتم استدعاء وظيفة إنشاء ملف مؤقت
mktemp () من libc:
$ 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 ()
بالمناسبة ، أقسم رابط سابقًا في هذه الوظيفة. لا مترجم ، ولكن رابط ، الذي فاجأني:
/usr/bin/ld: perl.o: in function `main': perl.c:(.text+0x978c): warning: the use of `mktemp' is dangerous, better use `mkstemp' or `mkdtemp'
الفكر الأول الذي ربما جاء إلى ذهنك أيضًا هو استبدال
الوظيفة غير الآمنة mktemp () بـ
mkstemp () ، وهو ما قمت به. اختفى تحذير الرابط ، لكن خطأ Segmentation ظل في هذا المكان على أي حال ، الآن فقط في
دالة mkstemp () .
لذلك ، تحتاج الآن إلى النظر بعناية فائقة في جزء الكود المرتبط بهذه الوظيفة. هناك اكتشفت شيئًا غريبًا إلى حد ما تم تسليط الضوء عليه في هذا المقتطف:
char *e_tmpname = "/tmp/perl-eXXXXXX"; int main(void) { mktemp(e_tmpname); e_fp = f_open(e_tmpname, "w"); ... }
اتضح أن
mktemp () يحاول تغيير الحرفي للقناع ، والذي يقع في قسم
.rodata ، والذي من الواضح أنه محكوم عليه بالفشل. أو ، بعد كل شيء ، قبل 32 عامًا ، كان هذا مقبولًا ، وقد تمت الموافقة عليه في الكود ، وحتى أنه نجح بطريقة ما؟
بطبيعة الحال ، فإن استبدال
char * e_tmpname بـ
char e_tmpname [] أصلح هذا الخطأ "Segmentation" وتمكنت من الحصول على ما قتلته طوال المساء:
$ ./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
فحصنا التنفيذ من سطر الأوامر ، لكن ماذا عن الملف؟ قمت بتنزيل أول "Hello World" للغة برمجة بيرل من الإنترنت:
ثم حاولت تشغيله ، لكن ، للأسف ، كان خطأ التقسيم ينتظرني مرة أخرى. هذه المرة في مكان مختلف تمامًا:
$ 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 ()
تم العثور على نقطة الاهتمام التالية في وظيفة
yyerror () ، أقتبس المقتطف الأصلي:
مرة أخرى ، الموقف مشابه للحالة التي كتبت عنها أعلاه. يتم
تعديل البيانات في قسم
.rodata مرة أخرى . ربما يكون الأمر مجرد أخطاء مطبعية بسبب Copy-Paste وبدلاً من
tname أرادوا كتابة
tmpbuf ؟ أم أن هناك حقا نوع من المعنى الخفي وراء ذلك؟ على أي حال ، فإن استبدال
char * tokename [] بـ
char tokename [] [32] يزيل خطأ خطأ Segmentation ويخبرنا Perl بما يلي:
$ ./perl test.pl syntax error in file test.pl at line 7, next token "strict" Execution aborted due to compilation errors.
اتضح أنه لا يحب جميع أنواع
الاستخدام الحديث
صارمة ، وهذا ما يحاول أن يقول لنا! إذا قمت بحذف أو تعليق هذه السطور في الملف ، يبدأ البرنامج:
$ ./perl test.pl Hello, World!
<< تخطى إلى المحتوى6. لتلخيص
في الواقع ، لقد حققت هدفي وجعلت الكود القديم من عام 1987 ليس فقط ترجمة ، ولكن أيضا العمل في بيئة لينكس الحديثة. مما لا شك فيه ، لا يزال هناك كومة كبيرة من أخطاء أخطاء Segmentation المختلفة ، وربما تتعلق بحجم المؤشر على بنية 64 بت. كل هذا يمكن تنظيفه بعد الجلوس في أمسيات قليلة مع المصحح على أهبة الاستعداد. لكن هذه ليست مهمة ممتعة ومملة للغاية. بعد كل شيء ، في البداية تم التخطيط لهذه التجربة للترفيه لأمسية مملة ، وليس كعمل كامل ، والذي سيتم وضع حد له. هل هناك أي فائدة عملية من الإجراءات المتخذة؟ ربما في يوم من الأيام سيصادف عالم الآثار الرقمي هذه المقالة وسيكون مفيدًا له. لكن في العالم الواقعي ، حتى التجربة المكتسبة من هذا البحث ، في رأيي ، ليست ذات قيمة كبيرة.
إذا كان أي شخص مهتمًا ، فقد قمت بنشر مجموعة من بقعتين. الأول بإصلاح أخطاء الترجمة ، والثاني بإصلاح بعض أخطاء خطأ Segmentation.
سكرتير خاص أنا أسارع إلى غضب المشجعين من
لاعبين خط واحد المدمرة ، وهذا لا يعمل هنا. ربما كانت نسخة بيرل قديمة جدًا لمثل هذا الترفيه.
PPS كل خير والحصول على عطلة نهاية أسبوع لطيفة. بفضل
kawaii_neko لإصلاح صغير .
التحديث من 28 أكتوبر إلى 2019: قام مستخدم من منتدى LINUX.ORG.RU ، باستخدام الاسم المستعار
utf8nowhere ، بتوفير روابط مثيرة للاهتمام للغاية
في تعليقه على هذه المقالة ، المعلومات التي لا يوضح منها الموقف مع حرفية السلسلة القابلة للتغيير فحسب ، بل تتناول مشكلة الاستخدام الموضحة أعلاه وظائف
mktemp () ! اسمحوا لي أن أقتبس هذه المصادر ، التي تصف أوجه عدم التوافق المتعددة بين K&R C و GNU C غير الموحد:
عدم توافق دول مجلس التعاون الخليجي
هناك العديد من أوجه عدم التوافق الجديرة بالملاحظة بين إصدارات GNU C و K&R (غير ISO) من C.
دول مجلس التعاون الخليجي عادة ما يجعل ثوابت السلسلة للقراءة فقط. في حالة استخدام العديد من ثوابت السلسلة المتماثلة المظهر ، يخزن GCC نسخة واحدة فقط من السلسلة.
نتيجة واحدة هي أنه لا يمكنك استدعاء mktemp باستخدام وسيطة سلسلة ثابتة. تقوم الدالة mktemp دائمًا بتغيير السلسلة التي تشير إليها الوسيطة.
والنتيجة الأخرى هي أن sscanf لا يعمل على بعض الأنظمة عندما يتم تمرير سلسلة ثابتة كسلسلة تحكم أو إدخال.هذا لأن sscanf يحاول بشكل غير صحيح الكتابة في ثابت السلسلة. وبالمثل fscanf و scanf .
أفضل حل لهذه المشكلات هو تغيير البرنامج لاستخدام متغيرات char -array مع سلاسل التهيئة لهذه الأغراض بدلاً من ثوابت السلسلة. ولكن إذا لم يكن ذلك ممكنًا ، فيمكنك استخدام علامة السلاسل القابلة للكتابة ، والتي توجه مجلس التعاون الخليجي إلى التعامل مع ثوابت السلسلة بالطريقة نفسها التي تفعل بها برامج التحويل البرمجي C.
المصدر: استخدام دليل GNU Compiler Collection (GCC 3.3) .
تم إهمال علامة برنامج التحويل البرمجي -fwritable-strings في GCC 3.4 وإزالتها نهائيًا في 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 .
اقترح المستخدم VarfolomeyKote4ka اختراقًا سيئًا مثيرًا للاهتمام يسمح لك بتجاوز أخطاء أخطاء Segmentation عند محاولة تغيير البيانات في قسم .rodata عن طريق تحويلها إلى قسم .rwdata . منذ وقت ليس ببعيد ، ظهر مقال مثير للإعجاب على الإنترنت ، "من .rodata إلى .rwdata - مقدمة في تعيين الذاكرة ونصوص LD" للمخرج المبرمج guye1296 ، والذي يوضح كيفية القيام بهذه الخدعة. لتسهيل الحصول على النتيجة المرجوة ، أعد مؤلف المقال نصًا كبيرًا نوعًا ما للرابط القياسي ld - rwdata.ld. يكفي تنزيل هذا البرنامج النصي ، ووضعه في جذر دليل مصدر Perl ، وتصحيح علامة LDFLAGS على النحو التالي: LDFLAGS = -T rwdata.ld ، ثم إعادة إنشاء المشروع. نتيجة لذلك ، لدينا ما يلي: $ 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 !
اتضح أنه بفضل هذا الاختراق ، يمكن حذف كل التغييرات تقريبًا من التصحيح الثاني! على الرغم من ، بالطبع ، لا يزال من الأفضل وضع التعليمات البرمجية في طريقة عرض لا تنتهك المعايير.<< تخطى إلى المحتوى