It seems to me that signal handlers must run with a copy of the original
EFLAGS stored on the stack. Otherwise, when the handler returned the
former context wouldn't be fully restored. But I don't know enough about
the signal handling code to see how to turn off RF in the stored EFLAGS
image.
Also, what if the signal handler was entered as a result of encountering
an instruction breakpoint? In that case you would want to keep RF on to
prevent an infinite loop.
You're right about wanting to clear RF when changing the PC via ptrace or
when setting a new execution breakpoint (provided the new breakpoint's
address is equal to the current PC value).
Do you know how gdb handles instruction breakpoints, and in particular,
how it resumes execution after a breakpoint?
That may be what I originally had in mind; I no longer remember.
But it doesn't matter. We're up against an API incompatibility here.
gdb doesn't allow you to modify breakpoints; it forces you to delete the
old one and add a new one. It's only an artifact of the x86 architecture
that gdb implements this by reusing debug registers. So even if the
modify_user_hw_breakpoint() routine were kept, gdb wouldn't really want to
make use of it.
Under the circumstances I think we should just leave it out.
On the 386, either GE or LE had to be set for DR breakpoints to work
properly. Later on (I don't remember if it was in the 486 or the Pentium)
this restriction was removed. I don't know whether those bits do anything
at all on modern CPUs.
My 80386 Programmer's Reference Manual says:
... an instruction-address breakpoint exception is a fault.
And:
When it detects a fault, the processor automatically sets
RF in the flags image that it pushes onto the stack.
And:
The processor automatically sets RF in the EFLAGS image
on the stack before entry into any fault handler. Upon
entry into the fault handler for instruction address
breakpoints, for example, RF is set in the EFLAGS image
on the stack...
That seems to be pretty clear. So the behavior can vary according to the
processor type.
I suppose you might register a breakpoint and find that it isn't installed
immediately, but then it could get installed and actually trigger before
you managed to unregister it. Does that count as a "difficult race"?
Presumably the work done by the trigger callback would get ignored.
Punting isn't acceptable, not if the bp in question was present both
before and after the IPI. I'd rather transmogrify it as you described,
awkward though that may be.
Maybe it doesn't have to be so bad. If there were _two_ global copies of
the kernel bp settings, one for the old pre-IPI state and one for the new,
then the handler could simply look up the DR# in the appropriate copy.
This would remove the need to store the settings in the per-CPU area.
It's a relatively minor issue. On machines with fixed-length breakpoints,
the .len field can be ignored. Conversely, leaving it out would require
using bitmasks to extract the type and length values from a combined .bits
field. I don't see any advantage.
Ah, you haven't understood the purpose of the gennum. In fact 8 bits
isn't too small -- far from it! It's too _large_; a single bit would
suffice. I made it an 8-bit value just because that was easier.
Here's the idea. thbi->gennum is at all times either equal to the current
gennum value or is set to -1. That's what notify_all_threads() does; it
sets thbi->gennum to -1 in all tasks currently being debugged whenever a
change to the kernel breakpoints occurs. My assumption is that almost all
of the time there will be very few debuggees.
The main use of gennum is with chbi->gennum, which is at all times equal
to the current gennum value or the previous one (if the CPU hasn't yet
received the update IPI). Hence chbi->gennum needs to distinguish between
only two values: current or previous.
Note that CPUs can never lag behind by more than one update. The
hw_breakpoint_mutex doesn't get released until every CPU has acknowledged
receipt of the IPI.
Yes, that was the idea. However seqcounts may work better in conjunction
with this idea of keeping a global copy of both the old and the new kernel
breakpoints. I'll look into it.
Hmmm, maybe. Those loops would end up looking messy.
No. The 80386 manual says:
Note that the bits of DR6 are never cleared by the processor.
It's important to bear in mind that not all x86 CPUs are made by Intel,
and of those that are, not all are Pentium 4's. This appears to be an
area of high variability so we should be as conservative as possible.
I could do that. I don't know what happens to DR_STEP; a quick test might
be worthwhile.
I'll put together a simple test module for kernel breakpoints. It's
already possible to test user breakpoints just by running gdb.
I'll see what I can do.
In this situation you don't need to worry about how .type and .len are
stored. On powerpc64 we can have a special thbi->dabr field analogous to
the thbi->tdr7 field on x86. All precomputed and ready for quick
switching.
Even if HB_NUM were larger than 1, we could still store two copies of the
address value (the second copy with the low-order type bits set).
Alan Stern
-