2009-03-03 32 views
8

Tôi muốn có thể phát hiện khi ghi vào địa chỉ bộ nhớ xảy ra - ví dụ bằng cách đặt một cuộc gọi lại được gắn vào ngắt. Có ai biết làm thế nào?Có thể bẫy ghi địa chỉ (x86 - linux)

Tôi muốn có thể thực hiện việc này khi chạy (có thể gdb có tính năng này, nhưng ứng dụng cụ thể của tôi khiến gdb bị lỗi).

Trả lời

14

Nếu bạn muốn chặn ghi vào một dải địa chỉ, bạn có thể sử dụng mprotect() để đánh dấu bộ nhớ được đề cập là không thể ghi và cài đặt bộ xử lý tín hiệu bằng cách sử dụng sigaction() để bắt SIGSEGV, ghi nhật ký hoặc bất kỳ thứ gì và đánh dấu trang là có thể ghi lại.

7

gì bạn cần là truy cập vào các thanh ghi debug X86: http://en.wikipedia.org/wiki/Debug_register

Bạn sẽ cần phải thiết lập địa chỉ breakpoint trong một trong DR0 để DR3, và sau đó là điều kiện (ghi dữ liệu) trong DR7. Việc ngắt sẽ xảy ra và bạn có thể chạy mã gỡ lỗi để đọc DR6 và tìm thấy nguyên nhân gây ra điểm ngắt.

Nếu GDB không hoạt động, bạn có thể thử trình gỡ lỗi đơn giản/nhỏ hơn như http://sourceforge.net/projects/minibug/ - nếu điều đó không hoạt động, ít nhất bạn có thể xem mã và hiểu cách sử dụng phần cứng gỡ lỗi trên bộ xử lý .

Ngoài ra, có một IBM tài nguyên phát triển tuyệt vời trên thạo kỹ thuật linux gỡ lỗi mà nên cung cấp một số tùy chọn bổ sung:

http://www.ibm.com/developerworks/linux/library/l-debug/

Một bài báo khá tốt về việc này là cửa sổ là ở đây (tôi biết bạn đang chạy trên linux, nhưng những người khác có thể đi cùng cho câu hỏi này muốn làm điều đó trong cửa sổ):

http://www.codeproject.com/KB/debug/hardwarebreakpoint.aspx

-Adam

+1

Trình đăng ký gỡ lỗi chỉ có thể được truy cập ở mức đặc quyền 0, tức là trong hạt nhân. Xem http://pdos.csail.mit.edu/6.828/2008/readings/i386/s12_02.htm –

4

GDB không có tính năng đó: nó được gọi là watchpoints phần cứng, và nó rất tốt được hỗ trợ trên Linux/x86:

(gdb) watch *(int *)0x12345678 

Nếu ứng dụng của bạn bị treo GDB, xây dựng hiện hành GDB từ CVS Head.

Nếu GDB đó vẫn không thành công, hãy gửi GDB bug.

Có thể chúng tôi có thể sửa GDB nhanh hơn bạn có thể hack xung quanh bộ xử lý SIGSEGV (cung cấp một trường hợp kiểm tra tốt) và sửa lỗi cho GDB cũng giúp bạn khắc phục các vấn đề trong tương lai.

+1

+1, tôi vừa tìm thấy một cách sử dụng mới cho GDB :) –

2

mprotect không có bất lợi: bộ nhớ của bạn phải được căn chỉnh theo ranh giới trang. Tôi đã có bộ nhớ có vấn đề của tôi trên ngăn xếp và không thể sử dụng mprotect().

Như Adam đã nói, điều bạn muốn là thao tác các thanh ghi gỡ lỗi. Trên cửa sổ, tôi đã sử dụng: http://www.morearty.com/code/breakpoint/ và nó hoạt động rất tốt. Tôi cũng chuyển nó sang Mach-O (Mac OS X), và nó cũng hoạt động rất tốt. Nó cũng dễ dàng, bởi vì Mach-O có thread_set_state(), tương đương với SetThreadContext().

Vấn đề với linux là nó không có tương đương như vậy. Tôi tìm thấy ptrace, nhưng tôi nghĩ, điều này không thể được, phải có một cái gì đó đơn giản hơn. Nhưng không có. Chưa. Tôi nghĩ rằng họ đang làm việc trên một API hw_breakpoint cho cả hạt nhân và không gian người dùng.(xem http://lwn.net/Articles/317153/)

Nhưng khi tôi tìm thấy điều này: http://blogs.oracle.com/nike/entry/memory_debugger_for_linux Tôi đã thử nó và nó không phải là xấu. Phương thức ptrace hoạt động bởi một số "quy trình bên ngoài" hoạt động như một "trình gỡ lỗi", gắn vào chương trình của bạn, tiêm các giá trị mới cho thanh ghi gỡ lỗi và kết thúc với chương trình của bạn tiếp tục với một bộ break break hw mới. Vấn đề là, bạn có thể tự tạo "quá trình bên ngoài" này bằng cách sử dụng fork(), (tôi đã không thành công với một pthread) và thực hiện các bước đơn giản này trong mã của bạn.

Mã addwatchpoint phải được điều chỉnh để làm việc với Linux 64 bit, nhưng đó chỉ là thay đổi USER_DR7 v.v. thành offsetof (struct user, u_debugreg [7]). Một điều nữa là sau một PTRACE_ATTACH, bạn phải đợi cho debuggee thực sự dừng lại. Nhưng thay vì thử lại POKEUSER trong một vòng lặp bận rộn, điều chính xác để làm sẽ là một waitpid() trên pid của bạn.

Phương pháp duy nhất với phương pháp ptrace là chương trình của bạn chỉ có thể có một "trình gỡ lỗi" được đính kèm tại một thời điểm. Vì vậy, một đính kèm ptrace sẽ thất bại nếu chương trình của bạn đã chạy dưới sự kiểm soát gdb. Nhưng giống như mã ví dụ, bạn có thể đăng ký trình xử lý tín hiệu cho SIGTRAP, chạy không có gdb và khi bạn bắt tín hiệu, hãy nhập một vòng lặp bận chờ gdb đính kèm. Từ đó bạn có thể thấy ai đã cố gắng để viết bộ nhớ của bạn.

Các vấn đề liên quan