Breakpoint Exception - How are you doing it?

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
tsdnz
Member
Member
Posts: 333
Joined: Sun Jun 16, 2013 4:09 am

Breakpoint Exception - How are you doing it?

Post 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.
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: Breakpoint Exception - How are you doing it?

Post by gerryg400 »

The first method seems entirely reasonable. I believe that's how gdb does it.
If a trainstation is where trains stop, what is a workstation ?
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: Breakpoint Exception - How are you doing it?

Post 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.

Image
If a trainstation is where trains stop, what is a workstation ?
tsdnz
Member
Member
Posts: 333
Joined: Sun Jun 16, 2013 4:09 am

Re: Breakpoint Exception - How are you doing it?

Post 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.
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: Breakpoint Exception - How are you doing it?

Post 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.
If a trainstation is where trains stop, what is a workstation ?
tsdnz
Member
Member
Posts: 333
Joined: Sun Jun 16, 2013 4:09 am

Re: Breakpoint Exception - How are you doing it?

Post 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.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Breakpoint Exception - How are you doing it?

Post 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
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
tsdnz
Member
Member
Posts: 333
Joined: Sun Jun 16, 2013 4:09 am

Re: Breakpoint Exception - How are you doing it?

Post 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
tsdnz
Member
Member
Posts: 333
Joined: Sun Jun 16, 2013 4:09 am

Re: Breakpoint Exception - How are you doing it?

Post 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
tsdnz
Member
Member
Posts: 333
Joined: Sun Jun 16, 2013 4:09 am

Re: Breakpoint Exception - How are you doing it?

Post 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
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Breakpoint Exception - How are you doing it?

Post 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
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
tsdnz
Member
Member
Posts: 333
Joined: Sun Jun 16, 2013 4:09 am

Re: Breakpoint Exception - How are you doing it?

Post 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!!
Post Reply