使用 GDB 检查映射地址

发布于 2024-07-15 17:55:55 字数 437 浏览 2 评论 0原文

我正在使用我在 Linux 中的直接内存访问 发布的驱动程序来映射一些物理内存到用户空间地址。 但是,我无法使用 GDB 查看任何地址; 即,x 0x12345678(其中 0x12345678 是 mmap 的返回值)失败并出现错误“无法访问地址 0x12345678 处的内存”。

有什么办法告诉GDB这块内存可以查看吗? 或者,我可以在 mmap 中做一些不同的事情(其中 foo_mmap 的调用或实现),以允许它访问此内存吗?

请注意,我不是在询问 /dev/mem (如第一个片段中所示),而是在询问通过 ioremap()、virt_to_phys() 和 remap_pfn_range() 获取的到内存的 mmap

I'm using the driver I posted at Direct Memory Access in Linux to mmap some physical ram into a userspace address. However, I can't use GDB to look at any of the address; i.e., x 0x12345678 (where 0x12345678 is the return value of mmap) fails with an error "Cannot access memory at address 0x12345678".

Is there any way to tell GDB that this memory can be viewed? Alternatively, is there something different I can do in the mmap (either the call or the implementation of foo_mmap there) that will allow it to access this memory?

Note that I'm not asking about /dev/mem (as in the first snippet there) but about a mmap to memory acquired via ioremap(), virt_to_phys() and remap_pfn_range()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

谷夏 2024-07-22 17:55:55

我相信 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'

I believe Linux does not make I/O memory accessible via ptrace(). You could write a function that simply reads the mmap'ed address and have gdb invoke it. Here's a slightly modified version of your foo-user.c program along with the output from a gdb session.

#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'
疑心病 2024-07-22 17:55:55

我有你的难题的答案:)我在网上到处搜索,没有太多帮助,最后自己调试了它。

这篇文章对我来说是一个很好的起点。 我想实现类似的目标,我使用 MMAP 实现了一个字符驱动程序,将我的自定义托管内存映射到用户空间进程。 使用 GDB 时,ptrace PEEK 调用 access_process_vm() 来访问 VMA 中的任何内存。 这会导致 EIO 错误,因为通用访问无法获取内存的 PA。 事实证明,您必须通过实现 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;
}

I have the answer to your conundrum :) I've searched everywhere online without much help and finally debugged it myself.

This post was a good starting point for me. I wanted to achieve something in similar lines, I had implemented a char driver with MMAP to map my custom managed memory to a userspace process. When using GDB, ptrace PEEK calls access_process_vm() to access any memory in your VMA. This causes a EIO error since the generic access cannot get the PA of your memory. It turns out, you have to implement an access function for this memory by implementing .access of your VMA's vm_operations_struct. Below is an example:

//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;
}
明月松间行 2024-07-22 17:55:55

为了访问映射内存,GDB 将调用 ptrace,然后调用 __access_remote_vm() 来访问映射内存。 如果内存映射有 VMIO | 等标志 VM_PFNMAP(例如,remap_pfn_range()设置它们),GDB将通过用户定义的vm访问方法来访问内存。

内核已经提供了一个名为 generic_access_phys() 的通用版本,而不是编写我们自己的 access() 实现,并且可以像 /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;
    .... ....
}

To access the mmapped memory, GDB will call ptrace, which will then call __access_remote_vm() to access the mmapped memory. If the memory is mapped with flags such as VMIO | VM_PFNMAP (for example, remap_pfn_range() sets them), GDB will access the memory though vm's access method defined by users.

Instead of writing our own implementation for access(), kernel already provides a generic version called generic_access_phys(), and this method could be easily linked via vm_operations_struct as the /dev/mem device did:

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

int mmap_mem()
{
    .... ....
    vma->vm_ops = &mmap_mem_ops;
    .... ....
}
菩提树下叶撕阳。 2024-07-22 17:55:55

据我了解,GDB 将使用 ptrace 来探索您的进程记忆。 也许您应该编写一个简单的程序,仅附加到您的进程并使用 ptrace 从该内存中读取数据。 这可能有助于缩小根本问题的范围。 如果没有问题,那么您就知道要么我错了:),要么 GDB 发生了其他可疑的事情。

It is my understanding that GDB will be using ptrace to poke around in your process's memory. Perhaps you should write a simple program that just attaches to your process and uses ptrace to read from that memory. This might help narrow down what the underlying problem is. If that has no issues, then you know either I'm wrong :), or something else fishy is happening with GDB.

不疑不惑不回忆 2024-07-22 17:55:55

如果

(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

you go "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

Failing this, you can use "mem" to configure memory ranges.

(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
祁梦 2024-07-22 17:55:55

我认为,如果 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;
}

I think that if that memory is not accessible by GDB then it is not mapped into your process address space and so you get "Cannot access memory at addresss 0x12345678". If that application were run normally you would get a segmentation fault. Also, maybe your driver is screwed and you should check you actually can access the memory from within the kernel. Try with example here:

#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;
}
南…巷孤猫 2024-07-22 17:55:55

如果打开 AF_PACKET 套接字并将其映射,gdb 将无法访问该内存。 所以你的驱动没有问题。 要么是 ptrace 的问题,要么是 gdb 的问题。

If you open an AF_PACKET socket and mmap it, gdb can't access this memory. So there isn't a problem with your driver. It's either a problem with ptrace or with gdb.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文