2009-03-17 7 views
14

Ich verwende den Treiber, den ich bei Direct Memory Access in Linux veröffentlicht habe, um einige physische RAM in eine Userspace-Adresse zu kopieren. Allerdings kann ich GDB nicht verwenden, um eine Adresse zu suchen. d. h. x 0x12345678 (0x12345678 ist der Rückgabewert von mmap) schlägt mit einem Fehler fehl "Zugriff auf Speicher bei Adresse 0x12345678 nicht möglich".Untersuchen von Mmap-Adressen mit GDB

Gibt es eine Möglichkeit, GDB mitzuteilen, dass dieser Speicher angezeigt werden kann? Alternativ, gibt es etwas anderes, das ich in der mmap tun kann (entweder der Aufruf oder die Implementierung von foo_mmap dort), die es erlauben, auf diesen Speicher zuzugreifen?

Bitte beachte, dass ich nicht über/dev/mem fragen (wie im ersten Schnipsel gibt), sondern um ein mmap Speicher über ioremap erworben(), virt_to_phys() und remap_pfn_range()

+0

Scheint das ist spezifisch für/dev/mem – jpalecek

+0

könnte sein, aber ich verwende nicht/dev/mem;) – Mikeage

Antwort

11

Ich glaube, Linux macht nicht I/O-Speicher über ptrace() zugänglich. Sie könnten eine Funktion schreiben, die einfach die mmap-Adresse liest und gdb aufrufen. Hier ist eine leicht modifizierte Version Ihres Programms foo-user.c zusammen mit der Ausgabe einer gdb-Sitzung.

#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

Gute Idee. Ich hatte gehofft, dies zu vermeiden (Core-Dumps, identische Verwendung des Debuggers, ob das Modul verwendet wird oder nicht, oder sogar, wenn wir Linux verwenden), aber dies könnte die einzig mögliche Option sein. Wenn ich keinen Weg finde, ptrace zur Arbeit zu bringen, akzeptiere ich diese Lösung. – Mikeage

0

Ich denke, dass Wenn auf diesen Speicher von GDB nicht zugegriffen werden kann, wird er nicht in den Prozessadressraum zugeordnet, und Sie erhalten daher "Zugriff auf Speicher bei Adressen 0x12345678 nicht möglich". Wenn diese Anwendung normal ausgeführt wird, erhalten Sie einen Segmentierungsfehler. Vielleicht ist Ihr Treiber auch verschraubt und Sie sollten überprüfen, ob Sie tatsächlich aus dem Kernel auf den Speicher zugreifen können. Versuchen Sie, mit Beispiel hier:

#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

Nein, die mmap war erfolgreich, und ein printf auf der nächsten Zeile liest erfolgreich die Variable. – Mikeage

+0

Können Sie aktuelle Quellen Ihres Codes angeben? – kyku

+0

Siehe die verknüpfte Frage; nicht das erste kleine Snippet, sondern das lange Modul und die kurze Userspace-Anwendung. Beachten Sie, dass ich in dem Modul virt_to_phys (pt) >> PAGE_SHIFT wie in den Kommentaren vorgeschlagen hinzugefügt. – Mikeage

2

Es ist mein Verständnis, dass GDB wird ptrace verwenden, um Ihren Prozess Speicher herumzustochern in. Vielleicht sollten Sie ein einfaches Programm schreiben, das nur an Ihren Prozess anfügt und ptrace verwendet, um aus diesem Speicher zu lesen. Dies kann helfen, das zugrunde liegende Problem einzugrenzen. Wenn das keine Probleme hat, dann weißt du entweder, dass ich falsch liege :), oder etwas anderes passiert mit GDB.

+1

GDB verwendet ptrace, und Ptrace-Aufrufe, die GDB verwendet, müssen aus irgendeinem Grund fehlschlagen. Der einfachste Weg dies zu beweisen, ist GDB unter strace auszuführen. Ich bin mir ziemlich sicher, das Problem ist, dass der Kernel-Treiber die Unterstützung von ptrace nicht richtig (oder überhaupt) implementiert. –

+0

wahrscheinlich nicht; Ich schrieb das Kernelmodul :) Was muss ich tun, um sicherzustellen, dass ptrace funktioniert? Sie können die Quellen in der verknüpften Frage sehen. – Mikeage

1

you go "info Dateien"

(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 

dies nicht möglich, können Sie "mem" konfigurieren Speicherbereiche verwenden.

0

Wenn Sie einen AF_PACKET-Socket öffnen und ihn mmapen, kann gdb nicht auf diesen Speicher zugreifen. Es gibt also kein Problem mit Ihrem Treiber. Es ist entweder ein Problem mit Ptrace oder mit Gdb.

10

Ich habe die Antwort auf Ihr Rätsel :) Ich habe überall online ohne viel Hilfe gesucht und schließlich debuggte es selbst.

Dieser Beitrag war ein guter Ausgangspunkt für mich. Ich wollte etwas in ähnlichen Linien erreichen, ich hatte einen Char-Treiber mit MMAP implementiert, um meinen benutzerdefinierten verwalteten Speicher einem Userspace-Prozess zuzuordnen. Wenn Sie GDB verwenden, ruft ptrace PEEK access_process_vm() auf, um auf Speicher in Ihrer VMA zuzugreifen. Dies verursacht einen EIO-Fehler, da der generische Zugriff die PA Ihres Speichers nicht abrufen kann. Es stellt sich heraus, dass Sie eine Zugriffsfunktion für diesen Speicher implementieren müssen, indem Sie .access des Vma vm_operations_struct implementieren. Unten ist ein Beispiel:

//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

Um den mmapped Speicher zuzugreifen, wird GDB ptrace rufen, die dann() ruft __access_remote_vm den mmapped Speicher zuzugreifen. Wenn der Speicher mit Flags wie VMIO | VM_PFNMAP (z. B. remap_pfn_range() setzt sie), greift GDB auf den Speicher zu, obwohl die Zugriffsmethode von vm von Benutzern definiert wurde.

Statt unsere eigene Implementierung für den Zugang des Schreibens(), Kernel bietet bereits eine generische Version namens generic_access_phys(), und diese Methode leicht über vm_operations_struct verbunden sein könnte als das/dev/mem Gerät hat:

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

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