2013-11-20 9 views
6

मैं लिनक्स बॉक्स पर चल रहे सी में लिखे गए एप्लिकेशन के लिए कुछ सीमित रिमोट डीबगिंग कार्यक्षमता को कार्यान्वित कर रहा हूं। लक्ष्य एप्लिकेशन के साथ संवाद करना और मनमानी चर के मूल्य को देखना या मनमाने ढंग से कार्य करना है।क्या यह निर्धारित करना संभव है कि प्रतीक एक चर या सी में फ़ंक्शन है या नहीं?

मैं dlsym() कॉल के माध्यम से प्रतीकों को देखने में सक्षम हूं, लेकिन मैं यह निर्धारित करने में असमर्थ हूं कि वापस भेजा गया पता किसी फ़ंक्शन या चर को संदर्भित करता है या नहीं। क्या इस प्रतीक तालिका के माध्यम से टाइपिंग जानकारी निर्धारित करने का कोई तरीका है?

+4

, लेकिन आप कोड (ट्रैम्पोलाइंस, आदि) –

+0

या 3. जानकारी बाहर खींच शुरू कर कुछ विशेष समारोह की तलाश द्वारा 1. पते की जांच (स्थान), या 2. के साथ भाग प्राप्त कर सकते हैं उपलब्ध होने पर डीडब्ल्यूएआरएफ डीबगिंग जानकारी (जो गैर-तुच्छ है) – nos

+0

डीबगिंग जानकारी इस एप्लिकेशन के लिए उपलब्ध नहीं है; एप्लिकेशन इतना बड़ा है कि डीबगिंग जानकारी के साथ संकलन करने का प्रयास करने से कुछ भी क्रैश हो जाता है जो इसे पढ़ने की कोशिश करता है (gdb) – dykeag

उत्तर

2

आप फ़ाइल पढ़ सकते हैं के रूप में /proc/self/maps और पार्स पहले तीन प्रत्येक पंक्ति के क्षेत्र:

<begin-addr>-<end-addr> rwxp ... 

तो फिर तुम लाइन है कि पता आप देख रहे हैं शामिल हैं खोज और अनुमतियों की जाँच करें:

  • r-x: यह कोड है;
  • rw-: यह लिखने योग्य डेटा है;
  • r--: यह केवल पढ़ने के लिए डेटा है;
  • कोई अन्य संयोजन: कुछ अजीब (rwxp: जेनरेट कोड, ...)।

उदाहरण के लिए निम्नलिखित कार्यक्रम:

#include <stdio.h> 

void foo() {} 
int x; 

int main() 
{ 
    int y; 
    printf("%p\n%p\n%p\n", foo, &x, &y); 
    scanf("%*s"); 
    return 0; 
} 

... अपने सिस्टम में इस उत्पादन देता है:

0x400570 
0x6009e4 
0x7fff4c9b4e2c 

... और इन /proc/<pid>/maps से प्रासंगिक पंक्तियां हैं:

00400000-00401000 r-xp 00000000 00:1d 641656  /tmp/a.out 
00600000-00601000 rw-p 00000000 00:1d 641656  /tmp/a.out 
.... 
7fff4c996000-7fff4c9b7000 rw-p 00000000 00:00 0 [stack] 
.... 

तो पते हैं: कोड, डेटा और डेटा

+1

महान उत्तर! अन्य पाठकों के लिए स्पष्टीकरण के लिए, '/ proc//maps में संख्याओं का पहला स्तंभ _range_ पता है। तो यह निर्धारित करने के लिए कि कोई प्रतीक एक फ़ंक्शन है, देखें कि यह सूचक 'x' के साथ चिह्नित पते की एक श्रृंखला के भीतर आता है या नहीं। एक चर का पता एक श्रेणी में होगा जो 'x' के साथ चिह्नित नहीं है। – dykeag

+0

@rodrigo क्या आप मुझे बता सकते हैं कि '% * s' क्या करता है? – phyrrus9

+0

@ phyrrus9: यह मानक इनपुट ('% s') से एक स्ट्रिंग पढ़ता है लेकिन फिर इसे कहीं भी सहेजने के बिना इसे छोड़ देता है (' * ')। ध्यान दें कि 'scanf()' के लिए कॉल में कोई अतिरिक्त पैरामीटर नहीं है। मैंने लिखा है कि कार्यक्रम को रोकने के लिए ENTER दबाए जाने तक ताकि फ़ाइल '/ proc//maps' को पढ़ा जा सके। कुछ लोग 'getchar() 'का उपयोग करना पसंद करते हैं ... – rodrigo

3

x86 प्लेटफ़ॉर्म पर, आप फ़ंक्शन के लिए स्टैक सेट अप करने के लिए उपयोग किए गए निर्देशों की जांच कर सकते हैं यदि आप इसकी पता स्थान देख सकते हैं। यह आमतौर पर है:

push ebp 
mov ebp, esp 

मैं x64 प्लेटफार्मों के बारे में सकारात्मक नहीं हूँ, लेकिन मुझे लगता है कि इसी तरह की है:

push rbp 
mov rbp, rsp 

This का वर्णन सी बुला सम्मेलन

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

+1

जब तक कि अनुकूलन के बिना कोड संकलित नहीं किया जाता है, फ्रेम पॉइंटर संभवतः छोड़ा जा सकता है। तो यह भरोसेमंद नहीं होगा। –

+0

ओह, यह सच है। मुझे यकीन है कि वह उस अनुकूलन को अक्षम कर सकता है। मैं अपना जवाब संपादित करूंगा, धन्यवाद – chbaker0

2

nm utility के आउटपुट को पार्स करके एप्लिकेशन के लिए प्रतीक तालिका निकालने का एक संभावित समाधान है। एनएम प्रतीक प्रकार पर जानकारी शामिल है। टी (वैश्विक पाठ) प्रकार के साथ प्रतीक कार्य हैं।

इस समाधान के साथ समस्या यह है कि आपको यह सुनिश्चित करना होगा कि आपकी प्रतीक तालिका लक्ष्य से मेल खाती है (विशेष रूप से यदि आप इसे पते निकालने के लिए उपयोग करने जा रहे हैं, हालांकि इसे dlsym() के संयोजन में उपयोग करना सुरक्षित होगा)। जिस विधि को मैंने यह सुनिश्चित करने के लिए उपयोग किया है कि निर्माण प्रक्रिया के प्रतीक तालिका निर्माण भाग को पोस्ट-प्रोसेसिंग चरण के रूप में बनाना है।

1

मुझे लगता है कि यह एक बहुत ही विश्वसनीय तरीका नहीं है, लेकिन यह काम हो सकता है:

एक प्रसिद्ध समारोह का पता, जैसे main() और एक प्रसिद्ध वैश्विक चर का पता है।

अब अज्ञात प्रतीक का पता लें और इस पते और अन्य दो के बीच के अंतर के पूर्ण मूल्य की गणना करें। सबसे छोटा अंतर यह इंगित करेगा कि अज्ञात पता किसी फ़ंक्शन या वैश्विक चर के करीब है, जिसका अर्थ है कि शायद यह एक और फ़ंक्शन या कोई अन्य वैश्विक चर है।

यह विधि इस धारणा के तहत काम करती है कि संकलक/लिंकर सभी वैश्विक चर को एक विशिष्ट मेमोरी ब्लॉक में पैक करेगा, और सभी कार्यों को अन्य मेमोरी ब्लॉक में पैक किया जाएगा। माइक्रोसॉफ्ट कंपाइलर, उदाहरण के लिए, सभी वैश्विक चर पहले (वर्चुअल मेमोरी में निचले पते) कार्यों को डाल दें।

मैं तुम्हें संभालने हूँ नहीं, स्थानीय चर के लिए जाँच करने के लिए तैयार हो जिसका पता एक समारोह से वापस नहीं जा सकती है (समारोह समाप्त होता है, स्थानीय चर खो दिया है)

1

यह dlsym() और dladdr1() के संयोजन से किया जा सकता है।

#define _GNU_SOURCE 

#include <dlfcn.h> 
#include <link.h> 
#include <stdio.h> 

int symbolType(void *sym) { 
    ElfW(Sym) *pElfSym; 
    Dl_info i; 

    if (dladdr1(sym, &i, (void **)&pElfSym, RTLD_DL_SYMENT)) 
     return ELF32_ST_TYPE(pElfSym->st_info); 

    return 0; 
} 

int main(int argc, char *argv[]) { 
    for (int i=1; i < argc; ++i) { 
     printf("Symbol [%s]: ", argv[i]); 

     void *mySym = dlsym(RTLD_DEFAULT, argv[i]); 

     // This will not work with symbols that have a 0 value, but that's not going to be very common 
     if (!mySym) 
      puts("not found!"); 
     else { 
      int type = symbolType(mySym); 
      switch (type) { 
       case STT_FUNC: puts("Function"); break; 
       case STT_OBJECT: puts("Data"); break; 
       case STT_COMMON: puts("Common data"); break; 
       /* get all the other types from the elf.h header file */ 
       default: printf("Dunno! [%d]\n", type); 
      } 
     } 
    } 

    return 0; 
} 
मंच पर निर्भर
संबंधित मुद्दे

 संबंधित मुद्दे