2009-03-17 15 views
14

का उपयोग करके एमएमएपीएड पते की जांच करना मैं कुछ भौतिक RAM को उपयोगकर्ता स्पेस में एमएमएपी करने के लिए Direct Memory Access in Linux पर पोस्ट किए गए ड्राइवर का उपयोग कर रहा हूं। हालांकि, मैं किसी भी पते को देखने के लिए जीडीबी का उपयोग नहीं कर सकता; यानी, x 0x12345678 (जहां 0x12345678 mmap का वापसी मान है) त्रुटि के साथ विफल रहता है "पता 0x12345678 पर स्मृति तक नहीं पहुंच सकता"।जीडीबी

क्या जीडीबी को यह बताने का कोई तरीका है कि यह स्मृति देखी जा सकती है? वैकल्पिक रूप से, क्या एमएमएपी में कुछ अलग-अलग कर सकता है (या तो कॉल या foo_mmap का कार्यान्वयन) जो इसे इस स्मृति तक पहुंचने की अनुमति देगा?

ध्यान दें कि मैं (वहाँ पहले टुकड़ा के रूप में), लेकिन ioremap (के माध्यम से प्राप्त करने के लिए एक स्मृति mmap के बारे में के बारे में/dev/mem नहीं कह रहा हूँ), virt_to_phys() और remap_pfn_range()

+0

ऐसा लगता है कि यह/dev/mem – jpalecek

+0

के लिए विशिष्ट है, लेकिन मैं/dev/mem का उपयोग नहीं कर रहा हूं;) – Mikeage

उत्तर

11

मेरा मानना ​​है कि लिनक्स I/O स्मृति को ptrace() के माध्यम से सुलभ नहीं बनाता है। आप एक ऐसा फ़ंक्शन लिख सकते हैं जो बस mmap'ed पते को पढ़ता है और gdb इसे आमंत्रित करता है। यहां एक gdb सत्र से आउटपुट के साथ आपके foo-user.c प्रोग्राम का थोड़ा संशोधित संस्करण दिया गया है।

#include <sys/stat.h> 
#include <fcntl.h> 
#include <unistd.h> 
#include <stdio.h> 
#include <sys/mman.h> 

char *mptr; 

char peek(int offset) 
{ 
    return mptr[offset]; 
} 

int main(void) 
{ 
    int fd; 
    fd = open("/dev/foo", O_RDWR | O_SYNC); 
    if (fd == -1) { 
     printf("open error...\n"); 
     return 1; 
    } 
    mptr = mmap(0, 1 * 1024 * 1024, PROT_READ | PROT_WRITE, 
      MAP_FILE | MAP_SHARED, fd, 4096); 
    printf("On start, mptr points to 0x%lX.\n", (unsigned long) mptr); 
    printf("mptr points to 0x%lX. *mptr = 0x%X\n", (unsigned long) mptr, 
      *mptr); 
    mptr[0] = 'a'; 
    mptr[1] = 'b'; 
    printf("mptr points to 0x%lX. *mptr = 0x%X\n", (unsigned long) mptr, 
      *mptr); 
    close(fd); 
    return 0; 
} 



$ make foo-user CFLAGS=-g 
$ gdb -q foo-user 
(gdb) break 27 
Breakpoint 1 at 0x804855f: file foo-user.c, line 27. 
(gdb) run 
Starting program: /home/me/foo/foo-user 
On start, mptr points to 0xB7E1E000. 
mptr points to 0xB7E1E000. *mptr = 0x61 

Breakpoint 1, main() at foo-user.c:27 
27   mptr[0] = 'a'; 
(gdb) n 
28   mptr[1] = 'b'; 
(gdb) print peek(0) 
$1 = 97 'a' 
(gdb) print peek(1) 
$2 = 98 'b' 
+0

अच्छा विचार। मैं इस से बचने की उम्मीद कर रहा था (कोर डंप, डीबगर का समान उपयोग मॉड्यूल का उपयोग किया जाता है या नहीं (या भले ही हम लिनक्स का उपयोग कर रहे हों)), लेकिन यह एकमात्र संभव विकल्प हो सकता है। अगर मुझे काम करने के लिए कोई रास्ता नहीं मिल रहा है, तो मैं इस समाधान को स्वीकार करूंगा। – Mikeage

0

मुझे लगता है कि लगता है अगर वह मेमोरी जीडीबी द्वारा उपलब्ध नहीं है तो इसे आपकी प्रोसेस एड्रेस स्पेस में मैप नहीं किया गया है और इसलिए आपको "0x12345678 पते पर मेमोरी एक्सेस नहीं किया जा सकता"। यदि वह एप्लिकेशन सामान्य रूप से चलाया जाता है तो आपको सेगमेंटेशन गलती मिल जाएगी। साथ ही, शायद आपका चालक खराब हो गया है और आपको जांच करनी चाहिए कि वास्तव में आप कर्नेल के भीतर से स्मृति तक पहुंच सकते हैं। उदाहरण यहाँ के साथ प्रयास करें:

#include <cstdio> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <unistd.h> 
#include <sys/mman.h> 

int main() { 
    int fd = open("/dev/zero", O_RDONLY); 
    void* addr = mmap(NULL, 1024, PROT_READ, MAP_PRIVATE, fd, 0); 
    for (int x = 0; x < 10; x++) { 
     printf("%X\n", ((char*)addr)[x]); 
    } 
    close(fd); 
    return 0; 
} 
+0

नहीं, mmap सफल हुआ, और अगली पंक्ति पर एक printf सफलतापूर्वक चर को पढ़ता है। – Mikeage

+0

क्या आप अपने कोड के वर्तमान स्रोत प्रदान कर सकते हैं? – kyku

+0

लिंक किए गए प्रश्न देखें; पहले छोटे स्निपेट नहीं, लेकिन लंबे मॉड्यूल और लघु उपयोगकर्ता अंतरिक्ष अनुप्रयोग। ध्यान दें कि मॉड्यूल में, मैंने virt_to_phys (pt) >> PAGE_SHIFT को टिप्पणियों में सुझाए गए अनुसार जोड़ा। – Mikeage

2

यह मेरी समझ है कि GDB अपनी प्रक्रिया की स्मृति में चारों ओर से प्रहार करने के लिए ptrace का उपयोग किया जाएगा है। शायद आपको एक सरल प्रोग्राम लिखना चाहिए जो सिर्फ आपकी प्रक्रिया से जुड़ा हुआ है और उस स्मृति से पढ़ने के लिए ptrace का उपयोग करता है। इससे अंतर्निहित समस्या को कम करने में मदद मिल सकती है। यदि इसमें कोई समस्या नहीं है, तो आप जानते हैं कि मैं गलत हूं :), या जीडीबी के साथ कुछ और हो रहा है।

+1

जीडीबी ptrace का उपयोग करता है, और ptrace कॉल जीडीबी उपयोग कुछ कारणों से विफल होना चाहिए। साबित करने का सबसे आसान तरीका यह है कि जीडीबी को स्ट्रेस के तहत चलाने के लिए क्या हो रहा है। मुझे पूरा यकीन है कि समस्या यह है कि कर्नेल ड्राइवर सही ढंग से ptrace समर्थन को लागू नहीं करता है (या बिल्कुल)। –

+0

शायद नहीं; मैंने कर्नेल मॉड्यूल लिखा है :) यह सुनिश्चित करने के लिए मुझे क्या करना है कि ptrace काम करता है? आप जुड़े प्रश्न में स्रोत देख सकते हैं। – Mikeage

1

तुम जाओ "जानकारी फ़ाइलें"

(gdb) help info files 
Names of targets and files being debugged. 
Shows the entire stack of targets currently in use (including the exec-file, 
core-file, and process, if any), as well as the symbol file name. 
(gdb) info files 
Symbols from "/bin/ls". 
Unix child process: 
     Using the running image of child Thread 4160418656 (LWP 10729). 
     While running this, GDB does not access memory from... 
Local exec file: 
     `/bin/ls', file type elf32-powerpc. 
     Entry point: 0x10002a10 
     0x10000134 - 0x10000141 is .interp 
     0x10000144 - 0x10000164 is .note.ABI-tag 
     0x10000164 - 0x100008f8 is .gnu.hash 
     0x100008f8 - 0x10001728 is .dynsym 
     0x10001728 - 0x100021f3 is .dynstr 
     0x100021f4 - 0x100023ba is .gnu.version 
... 
     0x0ffa8300 - 0x0ffad8c0 is .text in /lib/libacl.so.1 
     0x0ffad8c0 - 0x0ffad8f8 is .fini in /lib/libacl.so.1 
     0x0ffad8f8 - 0x0ffadbac is .rodata in /lib/libacl.so.1 
     0x0ffadbac - 0x0ffadd58 is .eh_frame_hdr in /lib/libacl.so.1 
     0x0ffadd58 - 0x0ffae4d8 is .eh_frame in /lib/libacl.so.1 
     0x0ffbe4d8 - 0x0ffbe4e0 is .ctors in /lib/libacl.so.1 
     0x0ffbe4e0 - 0x0ffbe4e8 is .dtors in /lib/libacl.so.1 
... 

(gdb) info sh 
From  To   Syms Read Shared Object Library 
0xf7fcf960 0xf7fe81a0 Yes   /lib/ld.so.1 
0x0ffd0820 0x0ffd5d10 Yes   /lib/librt.so.1 
0x0ffa8300 0x0ffad8c0 Yes   /lib/libacl.so.1 
0x0ff6a840 0x0ff7f4f0 Yes   /lib/libselinux.so.1 
0x0fdfe920 0x0ff1ae70 Yes   /lib/libc.so.6 
0x0fda23d0 0x0fdb0db0 Yes   /lib/libpthread.so.0 

इस असफल, आप स्मृति पर्वतमाला कॉन्फ़िगर करने के लिए "mem" का उपयोग कर सकते हैं।

(gdb) mem 1 1414 
(gdb) info mem 
Num Enb Low Addr High Addr Attrs 
1 y 0x00000001 0x00000586 rw nocache 
(gdb) disable mem 1 
(gdb) info mem 
Num Enb Low Addr High Addr Attrs 
1 n 0x00000001 0x00000586 rw nocache 
0

आप एक AF_PACKET सॉकेट खोलने के लिए और यह mmap हैं, gdb इस स्मृति का उपयोग नहीं कर सकते। तो आपके ड्राइवर के साथ कोई समस्या नहीं है। यह या तो पीआरटीई या जीडीबी के साथ एक समस्या है।

10

मेरे पास आपके conundrum का जवाब है :) मैंने बिना किसी मदद के ऑनलाइन हर जगह खोज की है और अंत में इसे स्वयं डीबग किया है।

यह पोस्ट मेरे लिए एक अच्छा प्रारंभिक बिंदु था। मैं इसी तरह की चीजों में कुछ हासिल करना चाहता था, मैंने अपनी कस्टम प्रबंधित मेमोरी को उपयोगकर्तास्पेस प्रक्रिया में मैप करने के लिए एमएमएपी के साथ एक चार ड्राइवर लागू किया था। जीडीबी का उपयोग करते समय, Ptrace PECK को आपके VMA में किसी भी मेमोरी तक पहुंचने के लिए access_process_vm() कॉल करता है। यह ईआईओ त्रुटि का कारण बनता है क्योंकि सामान्य पहुंच आपकी स्मृति का पीए नहीं प्राप्त कर सकती है। यह पता चला है कि, आपको अपने वीएमए के vm_operations_struct की क्षमता को कार्यान्वित करके इस मेमोरी के लिए एक्सेस फ़ंक्शन को कार्यान्वित करना होगा।

//Below code needs to be implemented by your driver: 
static struct vm_operations_struct custom_vm_ops = { 
    .access = custom_vma_access, 
}; 

static inline int custom_vma_access(struct vm_area_struct *vma, unsigned long addr, 
      void *buf, int len, int write) 
{ 
    return custom_generic_access_phys(vma, addr, buf, len, write); 
} 

static int custom_generic_access_phys(struct vm_area_struct *vma, unsigned long addr, 
      void *buf, int len, int write) 
{ 
    void __iomem *maddr; 
    //int offset = (addr & (PAGE_SIZE-1)) - vma->vm_start; 
    int offset = (addr) - vma->vm_start; 

    maddr = phys_to_virt(__pa(custom_mem_VA)); 
    if (write) 
     memcpy_toio(maddr + offset, buf, len); 
    else 
     memcpy_fromio(buf, maddr + offset, len); 

    return len; 
} 
0

mmapped स्मृति का उपयोग करने के लिए, GDB ptrace फोन करेगा, जो तब mmapped स्मृति का उपयोग करने के लिए __access_remote_vm फोन करेगा(): नीचे एक उदाहरण है। अगर स्मृति को वीएमआईओ जैसे झंडे से मैप किया गया है VM_PFNMAP (उदाहरण के लिए, remap_pfn_range() उन्हें सेट करता है), जीडीबी उपयोगकर्ताओं द्वारा परिभाषित वीएम की एक्सेस विधि के बावजूद स्मृति तक पहुंच जाएगा।

इसके बजाय पहुँच के लिए अपने स्वयं के कार्यान्वयन लेखन() की

, कर्नेल पहले से ही generic_access_phys() कहा जाता है एक सामान्य संस्करण प्रदान करता है, और/dev/mem युक्ति के रूप में किया था इस विधि आसानी से vm_operations_struct के माध्यम से जोड़ा जा सकता है:

static const struct vm_operations_struct mmap_mem_ops = { 
     .access = generic_access_phys }; 

int mmap_mem() 
{ 
    .... .... 
    vma->vm_ops = &mmap_mem_ops; 
    .... .... 
}