2009-03-17 25 views
14

Tôi đang sử dụng trình điều khiển mà tôi đã đăng tại Direct Memory Access in Linux để chèn một số ram vật lý vào địa chỉ không gian người dùng. Tuy nhiên, tôi không thể sử dụng GDB để xem bất kỳ địa chỉ nào; tức là, x 0x12345678 (trong đó 0x12345678 là giá trị trả về của mmap) không thành công với lỗi "Không thể truy cập bộ nhớ tại địa chỉ 0x12345678".Kiểm tra địa chỉ được tạo hình bằng cách sử dụng GDB

Có cách nào để nói với GDB rằng bộ nhớ này có thể được xem không? Ngoài ra, có một cái gì đó khác nhau tôi có thể làm trong mmap (hoặc là cuộc gọi hoặc thực hiện foo_mmap có) mà sẽ cho phép nó truy cập vào bộ nhớ này?

Lưu ý rằng tôi không yêu cầu về/dev/mem (như trong đoạn đầu tiên ở đó) nhưng khoảng một mmap vào bộ nhớ mua qua ioremap(), virt_to_phys() và remap_pfn_range()

+0

Dường như điều này là cụ thể cho/dev/mem – jpalecek

+0

có thể, nhưng tôi không sử dụng/dev/mem;) – Mikeage

Trả lời

11

Tôi tin rằng Linux không làm cho bộ nhớ I/O có thể truy cập được qua ptrace(). Bạn có thể viết một hàm chỉ cần đọc địa chỉ mmap'ed và có gdb gọi nó. Đây là một phiên bản được sửa đổi đôi chút của chương trình foo-user.c của bạn cùng với đầu ra từ phiên 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' 
+0

Ý tưởng hay. Tôi đã hy vọng để tránh điều này (lõi bãi, sử dụng giống hệt nhau của trình gỡ lỗi cho dù mô-đun được sử dụng (hoặc thậm chí nếu chúng tôi đang sử dụng Linux)), nhưng điều này có thể là lựa chọn duy nhất có thể. Nếu tôi không thể tìm cách để có được ptrace để làm việc, tôi sẽ chấp nhận giải pháp này. – Mikeage

0

Tôi nghĩ rằng nếu bộ nhớ đó không thể truy cập được bởi GDB thì nó không được ánh xạ vào không gian địa chỉ tiến trình của bạn và do đó bạn nhận được "Không thể truy cập bộ nhớ tại địa chỉ 0x12345678". Nếu ứng dụng đó chạy bình thường, bạn sẽ nhận được lỗi phân đoạn. Ngoài ra, có thể trình điều khiển của bạn là hơi say và bạn nên kiểm tra xem bạn thực sự có thể truy cập bộ nhớ từ bên trong hạt nhân hay không. Hãy thử với ví dụ tại đây:

#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

Không, mmap đã thành công và printf trên dòng tiếp theo đọc biến thành công. – Mikeage

+0

Bạn có thể cung cấp mã nguồn hiện tại của mình không? – kyku

+0

Xem câu hỏi được liên kết; không phải là đoạn mã đầu tiên, mà là mô-đun dài và ứng dụng không gian người dùng ngắn. Lưu ý rằng trong mô đun, tôi đã thêm virt_to_phys (pt) >> PAGE_SHIFT như được đề xuất trong phần bình luận. – Mikeage

2

Tôi hiểu rằng GDB sẽ sử dụng ptrace để poke xung quanh trong bộ nhớ của quá trình. Có lẽ bạn nên viết một chương trình đơn giản mà chỉ cần gắn vào quá trình của bạn và sử dụng ptrace để đọc từ bộ nhớ đó. Điều này có thể giúp thu hẹp những vấn đề cơ bản là gì. Nếu điều đó không có vấn đề gì, thì bạn biết tôi là sai :), hoặc điều gì đó khác đang xảy ra với GDB.

+1

GDB sử dụng lệnh ptrace và các cuộc gọi ptrace GDB sử dụng phải không thành công vì một số lý do. Cách đơn giản nhất để chứng minh rằng những gì đang xảy ra là chạy GDB dưới strace. Tôi khá chắc chắn vấn đề là trình điều khiển hạt nhân không thực hiện hỗ trợ ptrace đúng (hoặc ở tất cả). –

+0

có thể không; Tôi đã viết mô-đun hạt nhân :) Tôi phải làm gì để đảm bảo ptrace hoạt động? Bạn có thể xem các nguồn trong câu hỏi được liên kết. – Mikeage

1

bạn đi "info file"

(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 

Không này, bạn có thể sử dụng "mem" để cấu hình dãy bộ nhớ.

(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

Nếu bạn mở ổ AF_PACKET và mmap, gdb không thể truy cập bộ nhớ này. Vì vậy, không có vấn đề gì với tài xế của bạn. Đó là một vấn đề với ptrace hoặc với gdb.

10

Tôi có câu trả lời cho câu hỏi hóc búa của bạn :) Tôi đã tìm kiếm ở mọi nơi trực tuyến mà không cần nhiều trợ giúp và cuối cùng tự sửa lỗi.

Bài đăng này là điểm khởi đầu tốt cho tôi. Tôi muốn đạt được một cái gì đó trong các dòng tương tự, tôi đã thực hiện một trình điều khiển char với MMAP để ánh xạ bộ nhớ được quản lý tùy chỉnh của tôi vào một tiến trình không gian người dùng. Khi sử dụng GDB, ptrace PEEK gọi access_process_vm() để truy cập bất kỳ bộ nhớ nào trong VMA của bạn. Điều này gây ra lỗi EIO vì quyền truy cập chung không thể nhận được PA của bộ nhớ của bạn. Nó chỉ ra, bạn phải thực hiện một chức năng truy cập cho bộ nhớ này bằng cách thực hiện .access của vm_operations_struct của VMA. Dưới đây là một ví dụ:

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

Để truy cập vào bộ nhớ mmapped, GDB sẽ gọi ptrace, sau đó sẽ gọi __access_remote_vm() để truy cập vào bộ nhớ mmapped. Nếu bộ nhớ được ánh xạ với các cờ như VMIO | VM_PFNMAP (ví dụ, remap_pfn_range() đặt chúng), GDB sẽ truy cập bộ nhớ mặc dù phương thức truy cập của vm được xác định bởi người dùng.

Thay vì viết thực hiện riêng của mình cho truy cập(), kernel đã cung cấp một phiên bản generic gọi generic_access_phys(), và phương pháp này có thể dễ dàng liên kết thông qua vm_operations_struct như thiết bị/dev/mem đã làm:

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

int mmap_mem() 
{ 
    .... .... 
    vma->vm_ops = &mmap_mem_ops; 
    .... .... 
} 
Các vấn đề liên quan