GDB दूषित स्टैक फ्रेम - डिबग कैसे करें?


113

मेरे पास निम्नलिखित स्टैक ट्रेस हैं। क्या डीबगिंग के लिए इससे कुछ भी उपयोगी बनाना संभव है?

Program received signal SIGSEGV, Segmentation fault.
0x00000002 in ?? ()
(gdb) bt
#0  0x00000002 in ?? ()
#1  0x00000001 in ?? ()
#2  0xbffff284 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb) 

जब हम एक कोड प्राप्त करना शुरू करते हैं Segmentation fault, तो स्टैक ट्रेस इतना उपयोगी नहीं होता है?

नोट: यदि मैं कोड पोस्ट करता हूं, तो एसओ विशेषज्ञ मुझे जवाब देंगे। मैं एसओ से मार्गदर्शन लेना चाहता हूं और इसका जवाब खुद ढूंढना चाहता हूं, इसलिए मैं यहां कोड पोस्ट नहीं कर रहा हूं। क्षमा याचना।


संभवतः आपका कार्यक्रम मातम में कूद गया - क्या आप स्टैक पॉइंटर से कुछ भी पुनर्प्राप्त कर सकते हैं?
कार्ल नॉरम

1
एक और बात पर विचार करना है कि क्या फ्रेम पॉइंटर सही तरीके से सेट है। क्या आप अनुकूलन के बिना निर्माण कर रहे हैं या एक ध्वज की तरह गुजर रहे हैं -fno-omit-frame-pointer? इसके अलावा, स्मृति भ्रष्टाचार के लिए, valgrindएक अधिक उपयुक्त उपकरण हो सकता है, अगर यह आपके लिए एक विकल्प है।
फेटरेल

जवाबों:


155

उन फर्जी आदतों (0x00000002 और इसी तरह) वास्तव में पीसी मान हैं, एसपी मान नहीं। अब, जब आप इस तरह के SEGV को एक फर्जी (बहुत छोटे) पीसी पते के साथ प्राप्त करते हैं, तो 99% समय एक फर्जी फ़ंक्शन पॉइंटर के माध्यम से कॉल करने के कारण होता है। ध्यान दें कि C ++ में वर्चुअल कॉल फ़ंक्शन पॉइंटर्स के माध्यम से कार्यान्वित की जाती हैं, इसलिए वर्चुअल कॉल के साथ कोई भी समस्या उसी तरह से प्रकट हो सकती है।

एक परोक्ष कॉल अनुदेश सिर्फ ढेर पर कॉल के बाद पीसी धक्का और उसके बाद लक्ष्य मूल्य (इस मामले में फर्जी), इसलिए यदि इस के लिए पीसी सेट है कि क्या हुआ, आप आसानी से इसे मैन्युअल रूप से ढेर बंद पीसी पॉपिंग से पूर्ववत कर सकते हैं । 32-बिट x86 कोड में आप बस ऐसा करते हैं:

(gdb) set $pc = *(void **)$esp
(gdb) set $esp = $esp + 4

64-बिट x86 कोड के साथ आपको आवश्यकता है

(gdb) set $pc = *(void **)$rsp
(gdb) set $rsp = $rsp + 8

फिर, आपको एक ऐसा करने में सक्षम होना चाहिए btऔर यह पता लगाना चाहिए कि कोड वास्तव में कहां है।

अन्य 1% समय, स्टैक को ओवरराइट करने के कारण होगा, आमतौर पर स्टैक पर संग्रहीत एक सरणी को ओवरफ्लो करके। इस स्थिति में, आप वेलग्रिंड जैसे उपकरण का उपयोग करके स्थिति पर अधिक स्पष्टता प्राप्त करने में सक्षम हो सकते हैं


5
@ जॉर्ज: gdb executable corefileनिष्पादन योग्य और मुख्य फ़ाइल के साथ जीडीबी खोलेगा, जिस बिंदु पर आप कर सकते हैं bt(या इसके बाद के संस्करण कमांड bt) ...
क्रिस डोड

2
@ एमके .. एआरएम रिटर्न एड्रेस के लिए स्टैक का उपयोग नहीं करता है - इसके बजाय लिंक रजिस्टर का उपयोग करता है। तो यह आम तौर पर इस समस्या नहीं है, या अगर यह करता है, यह आमतौर पर कुछ अन्य ढेर भ्रष्टाचार के कारण है।
क्रिस डोड

2
एआरएम में भी, मुझे लगता है, सभी सामान्य प्रयोजन रजिस्टर और एलआर को स्टैक में संग्रहीत किया जाता है, जब तक कि फ़ंक्शन को निष्पादित करना शुरू नहीं होता है। एक बार फ़ंक्शन समाप्त हो जाता है, LR का मान पीसी में पॉप होता है और इसलिए फ़ंक्शन वापस लौटता है। इसलिए यदि स्टैक दूषित है, तो हम देख सकते हैं कि एक गलत मान पीसी सही है? इस मामले में स्टैक पॉइंटर को समायोजित किया जा सकता है जिससे उचित स्टैक हो जाएगा और समस्या को डीबग करने में मदद मिलेगी। तुम क्या सोचते हो? pls मुझे अपने विचार बताएं। धन्यवाद।
एमके ..

1
फर्जी का मतलब क्या है?
डैनी लो

5
ARM x86 नहीं है - इसके स्टैक पॉइंटर को कहा जाता है sp, नहीं espया नहीं rsp, और इसके कॉल इंस्ट्रक्शन lrरजिस्टर में रिटर्न एड्रेस को स्टोर करते हैं , स्टैक पर नहीं। इसलिए एआरएम के लिए, आपको वास्तव में कॉल को पूर्ववत करना होगा set $pc = $lr। यदि $lrअमान्य है, तो आपके पास खोलना बहुत कठिन समस्या है।
क्रिस डोड

44

यदि स्थिति काफी सरल है, तो क्रिस डोड का जवाब सबसे अच्छा है। ऐसा लगता है कि यह एक NULL पॉइंटर के माध्यम से कूद गया।

हालांकि, यह संभव है कि दुर्घटना से पहले कार्यक्रम को पैर, घुटने, गर्दन और आंख में गोली मार दी गई हो - स्टैक को ओवरवोट किया, फ्रेम पॉइंटर और अन्य बुराइयों को गड़बड़ कर दिया। यदि ऐसा है, तो हैश को खोलना आपको आलू और मांस दिखाने की संभावना नहीं है।

अधिक कुशल समाधान डिबगर के तहत प्रोग्राम को चलाने के लिए होगा, और प्रोग्राम के क्रैश होने तक फ़ंक्शन पर कदम रखेगा। एक बार एक क्रैश फ़ंक्शन की पहचान हो जाने के बाद, फिर से शुरू करें और उस फ़ंक्शन में कदम रखें और निर्धारित करें कि यह किस फ़ंक्शन को कॉल करता है क्रैश। तब तक दोहराएं जब तक आपको कोड की एकल ऑफ़ेंडिंग लाइन न मिल जाए। 75% समय, फिर फिक्स स्पष्ट होगा।

अन्य 25% स्थितियों में, कोड की तथाकथित आक्रामक रेखा एक लाल हेरिंग है। यह (अमान्य) स्थितियों पर प्रतिक्रिया दे रहा होगा पहले कई लाइनें लगाता था - शायद पहले हजारों लाइनें। अगर ऐसा है, तो चुना गया सबसे अच्छा कोर्स कई कारकों पर निर्भर करता है: ज्यादातर कोड की आपकी समझ और इसके साथ अनुभव:

  • शायद एक डिबगर वॉचपॉइंट सेट करना या printfमहत्वपूर्ण चर पर डायग्नोस्टिक को सम्मिलित करना आवश्यक ए हा को जन्म देगा !
  • हो सकता है कि अलग-अलग इनपुट के साथ परीक्षण की स्थिति बदलने से डिबगिंग की तुलना में अधिक जानकारी मिलेगी।
  • हो सकता है कि आँखों की एक दूसरी जोड़ी आपको अपनी मान्यताओं की जाँच करने या अनदेखे सबूत इकट्ठा करने के लिए मजबूर कर दे।
  • कभी-कभी, यह सब लेता है रात का खाना और इकट्ठा किए गए सबूत के बारे में सोच रहा है।

सौभाग्य!


13
यदि आंखों की एक दूसरी जोड़ी उपलब्ध नहीं है, तो रबर बतख विकल्प के रूप में अच्छी तरह से साबित होते हैं।
मैट

2
एक बफर के अंत लेखन भी कर सकते हैं। यह क्रैश नहीं हो सकता है जहां आप बफर के अंत को लिखते हैं, लेकिन जब आप फ़ंक्शन से बाहर निकलते हैं, तो यह मर जाता है।
phyatt

उपयोगी हो सकता है: GDB: Automatic 'Next'ing
user202729

28

यह मानते हुए कि स्टैक पॉइंटर वैध है ...

यह जानना असंभव हो सकता है कि जहां पीछे से SEGV होता है - मुझे लगता है कि पहले दो स्टैक फ्रेम पूरी तरह से ओवरराइट किए गए हैं। 0xbffff284 एक मान्य पते की तरह लगता है, लेकिन अगले दो नहीं हैं। स्टैक पर करीब से देखने के लिए, आप निम्नलिखित प्रयास कर सकते हैं:

gdb $ x / 32ga $ rsp

या एक संस्करण (एक अन्य संख्या के साथ 32 को बदलें)। यह विशाल (g) आकार के स्टैक पॉइंटर से शुरू होने वाले कुछ शब्दों (32) को प्रिंट (ए) के रूप में स्वरूपित करेगा। प्रारूप पर अधिक जानकारी के लिए 'मदद x' टाइप करें।

कुछ सेंटिनल 'प्रिंटफ' के साथ अपने कोड को इंस्ट्रूमेंट करना एक बुरा विचार नहीं हो सकता है, इस मामले में।


अविश्वसनीय रूप से उपयोगी, धन्यवाद - मेरे पास एक स्टैक था जो केवल तीन फ्रेम वापस चला गया और फिर "बैकट्रेस बंद हो गया: पिछले फ्रेम इस फ्रेम (भ्रष्ट स्टैक?)" के समान था; मैंने पहले सीपीयू अपवाद हैंडलर में कोड में इस तरह से कुछ किया है, लेकिन info symbolजीडीबी में ऐसा करने के अलावा अन्य याद नहीं कर सकता ।
leander

22
32-बिट एआरएम उपकरणों पर x/256wa $sp
एफडब्ल्यूआईडब्ल्यू

2
@ क्या आप मुझे बता सकते हैं कि X / 256wa क्या है? मुझे 64-बिट एआरएम के लिए इसकी आवश्यकता है। सामान्य तौर पर यह उपयोगी होगा यदि आप बता सकते हैं कि यह क्या है।
एमके ..

5
उत्तर के अनुसार, 'x' = मेमोरी लोकेशन की जाँच करें; यह कई 'w' = शब्द (इस मामले में, 256) प्रिंट करता है, और उन्हें 'a' = पते के रूप में व्याख्या करता है। Sourceware.org/gdb/current/onbuildocs/gdb/Memory.html#Memory पर GDB मैनुअल में अधिक जानकारी है ।
leander

7

अपने कुछ अन्य रजिस्टरों को देखें कि क्या उनमें से एक में स्टैक पॉइंटर पॉकेटेड है या नहीं। वहां से, आप एक स्टैक को पुनः प्राप्त करने में सक्षम हो सकते हैं। इसके अलावा, यदि यह एम्बेडेड है, तो बहुत बार स्टैक को एक विशेष पते पर परिभाषित किया जाता है। इसका उपयोग करके, आप कभी-कभी एक सभ्य स्टैक भी प्राप्त कर सकते हैं। यह सब मानता है कि जब आप हाइपरस्पेस पर कूदते थे, तो आपका कार्यक्रम रास्ते भर स्मृति में नहीं आता था ...


3

यदि यह स्टैक ओवरराइट है, तो मान कार्यक्रम से पहचाने जाने योग्य कुछ के अनुरूप हो सकते हैं।

उदाहरण के लिए, मैंने केवल खुद को स्टैक को देखते हुए पाया

(gdb) bt
#0  0x0000000000000000 in ?? ()
#1  0x000000000000342d in ?? ()
#2  0x0000000000000000 in ?? ()

और 0x342d13357 है, जो तब नोड-आईडी के रूप में बदल गया जब मैंने इसके लिए एप्लिकेशन लॉग को पकड़ लिया। यह तुरंत उम्मीदवार साइटों को संकीर्ण करने में मदद करता है जहां स्टैक ओवरराइट हो सकता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.