使用GDB检查mmaped地址

使用GDB检查mmaped地址,gdb,mmap,Gdb,Mmap,我正在使用我发布的驱动程序将一些物理ram映射到用户空间地址。但是,我不能使用GDB查看任何地址;i、 例如,x 0x12345678(其中0x12345678是mmap的返回值)失败,并出现错误“无法访问地址0x12345678处的内存” 有没有办法告诉GDB可以查看这个内存?或者,我可以在mmap中做一些不同的事情(调用或foo_mmap的实现)来允许它访问这个内存吗 请注意,我不是在问/dev/mem(如第一个代码片段中所述),而是在问通过ioremap()、virt_to_phys()和

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

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


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

我认为,如果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;
}
#包括
#包括
#包括
#包括
#包括
#包括
int main(){
int fd=打开(“/dev/zero”,仅限ordu);
void*addr=mmap(NULL,1024,PROT\u READ,MAP\u PRIVATE,fd,0);
对于(int x=0;x<10;x++){
printf(“%X\n”,((char*)addr)[X]);
}
关闭(fd);
返回0;
}

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

我相信Linux不会通过ptrace()访问I/O内存。您可以编写一个函数,只读取mmap'ed地址并让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'
#包括
#包括
#包括
#包括
#包括
char*mptr;
字符峰值(整数偏移)
{
返回mptr[offset];
}
内部主(空)
{
int-fd;
fd=打开(“/dev/foo”,O|RDWR | O|u SYNC);
如果(fd==-1){
printf(“打开错误…\n”);
返回1;
}
mptr=mmap(0,1*1024*1024,保护读取,保护写入,
MAP|U文件| MAP|U共享,fd,4096);
printf(“开始时,mptr指向0x%lX。\n”,(无符号长)mptr);
printf(“mptr指向0x%lX.*mptr=0x%X\n”,(无符号长)mptr,
*邮电部);
mptr[0]=“a”;
mptr[1]=“b”;
printf(“mptr指向0x%lX.*mptr=0x%X\n”,(无符号长)mptr,
*邮电部);
关闭(fd);
返回0;
}
$make foo user CFLAGS=-g
$gdb-Qfoo用户
(gdb)打破27
断点1位于0x804855f:文件foo user.c,第27行。
(gdb)运行
启动程序:/home/me/foo/foo用户
启动时,mptr指向0xB7E1E000。
mptr指向0xB7E1E000*mptr=0x61
foo user.c处的断点1,main():27
27 mptr[0]=“a”;
(gdb)n
28 mptr[1]=“b”;
(gdb)打印峰值(0)
$1=97‘a’
(gdb)打印预览(1)
$2=98“b”
你去“信息文件”

否则,您可以使用“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

如果打开AF_数据包套接字并对其进行mmap,gdb将无法访问此内存。所以你的司机没有问题。这要么是ptrace的问题,要么是gdb的问题。

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

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

要访问mmapped内存,GDB将调用ptrace,然后ptrace将调用uu access_remote_vm()来访问mmapped内存。如果内存映射有诸如VMIO | VM(PFNMAP)之类的标志(例如,remap(pfn)range()设置它们),GDB将通过用户定义的VM访问方法访问内存

内核没有为access()编写自己的实现,而是提供了一个名为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;
    .... ....
}

不,mmap成功,下一行的printf成功读取变量;不是第一个小片段,而是长模块和短用户空间应用程序。请注意,在模块中,我按照注释中的建议将virt_添加到了_phys(pt)>>PAGE_SHIFT。这似乎是针对/dev/membe的,但我没有使用/dev/mem;)GDB确实使用ptrace,GDB使用的ptrace调用肯定由于某种原因失败了。证明这一点的最简单方法是在strace下运行GDB。我很确定问题在于内核驱动程序没有正确地实现ptrace支持(或者根本没有);我编写了内核模块:)我必须做什么才能确保ptrace工作?你可以在链接的问题中看到来源。好主意。我希望避免这种情况(核心转储,无论是否使用该模块(或者即使我们使用的是linux),调试器的相同使用),但这可能是唯一可能的选择。如果我找不到让ptrace工作的方法,我会接受这个解决方案。
static const struct vm_operations_struct mmap_mem_ops = {
        .access = generic_access_phys };

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