【问题标题】:Examining mmaped addresses using GDB使用 GDB 检查映射地址
【发布时间】:2010-10-13 20:30:12
【问题描述】:

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

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

请注意,我不是在询问 /dev/mem(如第一个 sn-p 中那样),而是询问通过 ioremap()、virt_to_phys() 和 remap_pfn_range() 获取的内存的 mmap

【问题讨论】:

  • 似乎这是特定于 /dev/mem
  • 可能是,但我没有使用 /dev/mem ;)

标签: gdb mmap


【解决方案1】:

为了访问映射的内存,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;
    .... ....
}

【讨论】:

    【解决方案2】:

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

    这篇文章对我来说是一个很好的起点。我想以类似的方式实现某些目标,我已经使用 MMAP 实现了一个 char 驱动程序,以将我的自定义托管内存映射到用户空间进程。使用 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;
    }
    

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

        你去“信息文件”

        (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
        

        【讨论】:

          【解决方案5】:

          我相信 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 工作的方法,我会接受这个解决方案。
          【解决方案6】:

          据我了解,GDB 将使用ptrace 在您的进程内存中四处寻找。也许您应该编写一个简单的程序,该程序仅附加到您的进程并使用ptrace 从该内存中读取。这可能有助于缩小潜在问题的范围。如果这没有问题,那么您知道我错了:),或者 GDB 发生了其他可疑的事情。

          【讨论】:

          • GDB 确实使用了 ptrace,并且 GDB 使用的 ptrace 调用由于某种原因一定会失败。证明发生了什么的最简单方法是在 strace 下运行 GDB。我很确定问题在于内核驱动程序没有正确(或根本没有)实现 ptrace 支持。
          • 可能不会;我编写了内核模块 :) 我必须做什么才能确保 ptrace 正常工作?您可以在链接的问题中查看来源。
          【解决方案7】:

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

          【讨论】:

          • 不,mmap成功了,下一行的printf成功读取了变量。
          • 查看链接问题;不是第一个小sn-p,而是长模块和短用户空间的应用程序。请注意,在模块中,我按照 cmets 中的建议添加了 virt_to_phys(pt) >> PAGE_SHIFT。
          猜你喜欢
          • 2018-01-09
          • 2010-09-16
          • 2018-08-19
          • 1970-01-01
          • 2012-02-04
          • 2023-03-03
          • 2018-07-31
          • 2013-09-18
          • 2016-10-20
          相关资源
          最近更新 更多