使用GDB检查mmaped地址

18
我正在使用我在Direct Memory Access in Linux发布的驱动程序,将某些物理内存映射到用户空间地址。但是,我无法使用GDB查看任何地址; 即,x 0x12345678(其中0x12345678是mmap的返回值)失败,并显示错误“Cannot access memory at address 0x12345678”。
是否有办法告诉GDB可以查看这个内存?或者,在mmap中是否可以执行某些不同的操作(无论是调用还是foo_mmap的实现),使其能够访问此内存?
请注意,我并不是在询问/dev/mem(如第一个代码片段中所述),而是关于通过ioremap(),virt_to_phys()和remap_pfn_range()获取的内存进行mmap。

似乎这是特定于/dev/mem的。 - jpalecek
可能可以,但我没有使用/dev/mem ;) - Mikeage
7个回答

13

我有你难题的答案 :) 我在网上搜索了很多,没有得到太多帮助,最终自己解决了问题。

这篇文章是我的起点。我想要实现类似的东西,我已经使用一个字符驱动程序和MMAP实现了将我的自定义受控内存映射到用户空间进程中。当使用GDB时,ptrace PEEK调用access_process_vm() 访问VMA中的任何内存。由于通用访问无法获取内存的PA,因此会导致EIO错误。事实证明,您必须通过实现VMA的vm_operations_struct中的.access来实现对该内存的访问功能。下面是一个示例:

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

13

我认为Linux不允许使用ptrace()访问I/O内存。您可以编写一个简单的函数来读取mmap映射的地址,并让gdb调用它。以下是稍微修改过的foo-user.c程序以及gdb会话输出的版本。

#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'

好主意。我本来希望能避免这种情况(核心转储,无论模块是否被使用都要使用相同的调试器(甚至如果我们正在使用Linux)),但这可能是唯一的可行选择。如果我找不到让ptrace工作的方法,我会接受这个解决方案。 - Mikeage

3
为了访问映射内存,GDB将调用ptrace,然后调用__access_remote_vm()来访问映射内存。 如果内存以VMIO | VM_PFNMAP等标志进行映射(例如,remap_pfn_range()设置它们),则GDB将通过由用户定义的vm访问方法访问内存。
而不是编写自己的access()实现,内核已经提供了一个称为generic_access_phys()的通用版本,这个方法可以通过vm_operations_struct轻松链接到/dev/mem设备。
static const struct vm_operations_struct mmap_mem_ops = {
        .access = generic_access_phys };

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

2
据我所知,GDB将使用ptrace来查看您的进程内存。也许您应该编写一个简单的程序,只需附加到您的进程并使用ptrace从该内存中读取。这可能有助于缩小潜在问题的范围。如果没有问题,那么要么是我错了:),要么是GDB出现了其他问题。

1
GDB确实使用ptrace,而且GDB使用的ptrace调用必须出了某些问题。证明这就是发生的事情最简单的方法是在strace下运行GDB。我非常确定问题在于内核驱动程序没有正确(或根本)实现ptrace支持。 - Employed Russian
可能不需要,我写了内核模块 :)我需要做什么来确保ptrace正常工作?您可以在链接的问题中查看源代码。 - Mikeage

1

你输入 "info files"

(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

我认为如果GDB无法访问该内存,则该内存未映射到您的进程地址空间中,因此会出现“无法访问地址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;
}

1
没有问题,mmap成功了,下一行的printf成功读取了变量。 - Mikeage
请查看链接的问题;不是第一个小片段,而是长模块和短用户空间应用程序。请注意,在模块中,我按照评论中的建议添加了virt_to_phys(pt) >> PAGE_SHIFT。 - Mikeage

0
如果您打开一个AF_PACKET套接字并将其映射,gdb无法访问此内存。因此,您的驱动程序没有问题。这可能是ptrace或gdb的问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接