Page 1 of 1
Breakpoint Exception - How are you doing it?
Posted: Sat Jun 18, 2016 9:23 pm
by tsdnz
Hi, as this link describes,
http://wiki.osdev.org/Exceptions#Breakpoint
You can replace the first byte with INT3.
I guess the handler looks up the address and can replace the byte, and the code will run as intended.
But what happens if you do not want the break point removed?
I can think of two options.
- Save (RIP - 1), set single step, after single step put INT3 back, remove single step
- Have code that knows the instruction and its size, copy this to memory with a jump after the original code.
As you might guess, I am add debugging into user-space programs.
I would love to hear any feed back on how others have handled this.
Regards, Ali.
Re: Breakpoint Exception - How are you doing it?
Posted: Sun Jun 19, 2016 6:22 am
by gerryg400
The first method seems entirely reasonable. I believe that's how gdb does it.
Re: Breakpoint Exception - How are you doing it?
Posted: Sun Jun 19, 2016 4:29 pm
by gerryg400
Just some extra info if it's of interest.
I am currently in the process of getting gdb working in userspace on my OS. I have progressed a little. I can run the debugger. It loads a program, lists code, reads registers. Next I need to supply a kernel interface to stop the process. I expect setting breakpoints will just work because gdb uses a kernel call to write directly to the program's memory. I'll need to add int1 and int3 handlers to send messages back to gdb as well, but one step at a time.
Re: Breakpoint Exception - How are you doing it?
Posted: Sun Jun 19, 2016 4:45 pm
by tsdnz
Thanks gerryg400, I do not know what gdb is......
Googled this,
https://www.gnu.org/software/gdbI assume this is what you mean?
This will not work in my OS.
Cheers mate.
Re: Breakpoint Exception - How are you doing it?
Posted: Sun Jun 19, 2016 4:54 pm
by gerryg400
Yeah that's gdb. It's an open source, very portable debugger. In any event, as I said, the first method you mentioned is the same as it uses so I think that method is okay.
Re: Breakpoint Exception - How are you doing it?
Posted: Sun Jun 19, 2016 5:03 pm
by tsdnz
gerryg400 wrote:Yeah that's gdb. It's an open source, very portable debugger. In any event, as I said, the first method you mentioned is the same as it uses so I think that method is okay.
Cheers, the first is by far the easiest to implement.
Re: Breakpoint Exception - How are you doing it?
Posted: Mon Jun 20, 2016 3:47 am
by Brendan
Hi,
tsdnz wrote:Hi, as this link describes,
http://wiki.osdev.org/Exceptions#Breakpoint
You can replace the first byte with INT3.
I guess the handler looks up the address and can replace the byte, and the code will run as intended.
But what happens if you do not want the break point removed?
I can think of two options.
- Save (RIP - 1), set single step, after single step put INT3 back, remove single step
- Have code that knows the instruction and its size, copy this to memory with a jump after the original code.
Neither of these methods actually work for all cases - for a simple example, imagine this:
Code: Select all
mov eax,[.here]
.here:
add eax,[foo]
If you modify the code (replace "add eax,[foo]" with an "int3"), then you break the code.
The correct method is to use the CPU's debugging (DR0, DR1, DR2, DR3) so that you don't have to modify the code in the first place (and only use "int3" to insert breakpoints at compile time). That way you don't break "copy on write" memory mapped executables, or shared libraries mapped into multiple processes, or suffer "self modifying code" penalties; and as an added bonus it's much more flexible if done right (e.g. you can set a breakpoint for one thread that other threads executing the same code won't trigger).
Sadly, the CPU only provides 4 breakpoints, but you can use "break on control flow changes" to bypass that limit and emulate "infinite breakpoints" (or "no execute" paging protections to emulate "4 breakpoints per page").
Cheers,
Brendan
Re: Breakpoint Exception - How are you doing it?
Posted: Mon Jun 20, 2016 4:02 am
by tsdnz
Brendan wrote:Neither of these methods actually work for all cases - for a simple example, imagine this:
Code: Select all
mov eax,[.here]
.here:
add eax,[foo]
If you modify the code (replace "add eax,[foo]" with an "int3"), then you break the code.
Great stuff, I missed that one, that opens up a world of hurt!
Brendan wrote:but you can use "break on control flow changes" to bypass that limit and emulate "infinite breakpoints".
Not sure how to do this, I read this but nothing came to mind.
Time to have another read.
Thanks, Ali
Re: Breakpoint Exception - How are you doing it?
Posted: Mon Jun 20, 2016 4:19 am
by tsdnz
Brendan wrote:but you can use "break on control flow changes" to bypass that limit and emulate "infinite breakpoints".
Brendan, I am interested to see how it can be done.
Are you talking about this?
I cannot see how it can be done?
AMD64 Architecture Programmer's Manual Volume 2: System Programming.
13.1.6 Control-Transfer Breakpoint Features
A control transfers is accomplished by using one of following instructions:
• JMP, CALL, RET
• Jcc, JrCXZ, LOOPcc
• JMPF, CALLF, RETF
• INTn, INT 3, INTO, ICEBP
• Exceptions, IRET
• SYSCALL, SYSRET, SYSENTER, SYSEXIT
• INTR, NMI, SMI, RSM
Re: Breakpoint Exception - How are you doing it?
Posted: Mon Jun 20, 2016 4:55 am
by tsdnz
Brendan wrote:but you can use "break on control flow changes" to bypass that limit and emulate "infinite breakpoints".
Had a think about it.....
This could be used to narrow down the break point and therefore could use DR0-DR3, or use stepping if more than 4 break points between the "break on control flow changes" instructions.
This sounds like a good idea, is this what you were thinking?
Ali
Re: Breakpoint Exception - How are you doing it?
Posted: Mon Jun 20, 2016 6:59 am
by Brendan
Hi,
tsdnz wrote:Brendan wrote:but you can use "break on control flow changes" to bypass that limit and emulate "infinite breakpoints".
Had a think about it.....
This could be used to narrow down the break point and therefore could use DR0-DR3, or use stepping if more than 4 break points between the "break on control flow changes" instructions.
This sounds like a good idea, is this what you were thinking?
Imagine you have 12345 breakpoints at different addresses. When you get a "trap on (after) control flow instruction" you find the first breakpoint that has an address greater than or equal to the new EIP, set DR0 for that breakpoint and return. When you get a "fault at DR0 reached" you handle the breakpoint, then use EIP to find the first breakpoint that has an address greater than (but not equal to) the new EIP, set DR0 for that breakpoint and return.
Note that "break on control flow changes" was introduced in Pentium 4 (I think), and isn't supported on older CPUs. For older CPUs you'd have to use single stepping to emulate "infinite breakpoints" (check if the instruction is supposed to be a breakpoint after every instruction). You could also emulate "break on control flow changes" with single-stepping if you wanted (by decoding the instruction to see if it's a control flow instruction and setting a flag so you know that the next instruction is after a control flow instruction).
Of course if you have less breakpoints than (free) debug registers you'd just use "one debug register per breakpoint".
Mostly; the kernel can provide support for "infinite breakpoints" and "single step" and "break on control flow changes" (for both "per thread" and "all threads"); and a debugger could use any/all of that without caring if/when kernel is emulating or if CPU supports it.
Cheers,
Brendan
Re: Breakpoint Exception - How are you doing it?
Posted: Mon Jun 20, 2016 11:10 am
by tsdnz
Brendan wrote:.................... Mostly; the kernel can provide support for "infinite breakpoints" and "single step" and "break on control flow changes" (for both "per thread" and "all threads"); and a debugger could use any/all of that without caring if/when kernel is emulating or if CPU supports it.
That is it!
Another read @ 13.1.3
Control-Transfer Breakpoint happen after instruction, much easier to implement as you suggested.
Nice one Brendan!!