I'm working on a "binding" library that allows to use ptrace()
in the OCaml language but my question only relates to ptrace()
.
So, right now, I'm trying to write a small piece of code in order to create a simple hardware breakpoint on Linux x86-64 by using ptrace()
:
#define DR_OFFSET(x) (((struct user *)0)->u_debugreg + x)
typedef struct {
int dr0_local: 1;
int dr0_global: 1;
int dr1_local: 1;
int dr1_global: 1;
int dr2_local: 1;
int dr2_global: 1;
int dr3_local: 1;
int dr3_global: 1;
int reserverd: 8;
break_flag_t dr0_break: 2;
data_length_t dr0_len: 2;
break_flag_t dr1_break: 2;
data_length_t dr1_len: 2;
break_flag_t dr2_break: 2;
data_length_t dr2_len: 2;
break_flag_t dr3_break: 2;
data_length_t dr3_len: 2;
} dr7_t;
CAMLprim value ptrace_breakpoint(value ml_pid, value ml_addr)
{
CAMLparam2(ml_pid, ml_addr);
dr7_t dr7 = {0};
dr7.dr0_local = 1;
dr7.dr0_break = 0; /* break on execution */
dr7.dr0_len = 0x03; /* len 4 */
ptrace(PTRACE_POKEUSER, Int_val(ml_pid), DR_OFFSET(0), (void*)Int64_val(ml_addr));
ptrace(PTRACE_POKEUSER, Int_val(ml_pid), DR_OFFSET(7), (void*)dr7));
ptrace(PTRACE_POKEUSER, Int_val(ml_pid), DR_OFFSET(6), (void*)0);
CAMLreturn0;
}
When I execute this code, I got an Invalid argument
. The value of dr7
is 0xc0001
. In order to find a valid value, I inspected how GDB use ptrace
by using strace
:
ptrace(PTRACE_POKEUSER, 6459, offsetof(struct user, u_debugreg), 0x400519) = 0
ptrace(PTRACE_POKEUSER, 6459, offsetof(struct user, u_debugreg) + 56, 0x101) = 0
ptrace(PTRACE_POKEUSER, 6459, offsetof(struct user, u_debugreg) + 48, 0) = 0
So, GDB set the dr7 register to the value 0x101
. I tried this value and it works. Thus, I'm wondering what is the meaning of the value used by GDB? Is the dr7_t
bit fields that I used before valid?
Thank you.
Edit:
Thanks to Neitsa, here is the solution:
typedef struct {
unsigned int dr0_local: 1;
unsigned int dr0_global: 1;
unsigned int dr1_local: 1;
unsigned int dr1_global: 1;
unsigned int dr2_local: 1;
unsigned int dr2_global: 1;
unsigned int dr3_local: 1;
unsigned int dr3_global: 1;
unsigned int le: 1;
unsigned int ge: 1;
unsigned int reserved_10: 1;
unsigned int rtm: 1;
unsigned int reserved_12: 1;
unsigned int gd: 1;
unsigned int reserved_14_15: 2;
break_flag_t dr0_break: 2;
data_length_t dr0_len: 2;
break_flag_t dr1_break: 2;
data_length_t dr1_len: 2;
break_flag_t dr2_break: 2;
data_length_t dr2_len: 2;
break_flag_t dr3_break: 2;
data_length_t dr3_len: 2;
} dr7_t;
CAMLprim value ptrace_breakpoint(value ml_pid, value ml_addr)
{
CAMLparam2(ml_pid, ml_addr);
dr7_t dr7 = {0};
dr7.dr0_local = 1;
dr7.le = 1;
dr7.ge = 1;
dr7.reserved_10 = 1;
my_ptrace(PTRACE_POKEUSER, Int_val(ml_pid), DR_OFFSET(0), (void*)Int64_val(ml_addr));
my_ptrace(PTRACE_POKEUSER, Int_val(ml_pid), DR_OFFSET(7), (void*)dr7));
my_ptrace(PTRACE_POKEUSER, Int_val(ml_pid), DR_OFFSET(6), (void*)0);
CAMLreturn0;
}
Your structure looks good (I would have used unsigned int
though).
A few remarks (quotes are from Intel Manual chap 17.2: Debug Registers
):
memset
the whole structure to 0 beforehand).Other fields:
LE and GE (local and global exact breakpoint enable) flags (bits 8, 9) — This feature is not supported in the P6 family processors, later IA-32 processors, and Intel 64 processors. [...] , we recommend that the LE and GE flags be set to 1 if exact breakpoints are required
LENn
field must be set to 0):Instruction breakpoint addresses must have a length specification of 1 byte (the LENn field is set to 00). Code breakpoints for other operand sizes are undefined
So, to set a breakpoint on execution:
The processor recognizes an instruction breakpoint address only when it points to the first byte of an instruction. If the instruction has prefixes, the breakpoint address must point to the first prefix.
edit
Technically, 0x701 should be correct: