Reputation: 1283
In Visual Studio while debugging a C++ project, some data breakpoints never hit.
So I wrote some code for testing:
#include <iostream>
#include <stdint.h>
void test(uint32_t* p)
{
*p = 0;
// set a data breakpoint on p
*((char*)p + 2) = 0x1;
std::cout << *p << std::endl;
}
uint32_t* alloc(size_t offset)
{
char* p = new char[sizeof(uint32_t) + offset];
p = p + offset;
return (uint32_t*)p;
}
int main()
{
test(alloc(0)); // test #1
test(alloc(2)); // test #2
}
As you see, in function test, the value of *p will be zeroed first, then it will be changed implicitly, I got a litte-endian CPU so it must be 65536.
If you set a data breakpoint on p (4 bytes) to detect the changing, you'll get two different results: hit or not. It depends on the address of that p pointed at.
In my testing code above, test #1 will hit and test #2 will not, the difference between #1 and #2 are the addresses returned by alloc(0) and alloc(2).
This article How to: Set a Data Breakpoint on MSDN does not talk about this.
Does data breakpoint not work on an unaligned address ?
Upvotes: 6
Views: 2326
Reputation: 195
Detailed explanation of why this happens:
Data breakpoints use the CPU's debug registers. On x86 these debug registers get aligned to their data size by masking the address' lower bits:
(addr & -2)
.(addr & -4)
.(addr & -8)
.When the x86 CPU access memory it compares the address by masking it in the same way to the debug register address and if the two are equal it triggers a breakpoint.
This is a trick to simplify the electronic circuit in the debug breakpoint comparator: Only the two aligned addresses need to be compared.
Electronically it translates to the pseudo-code:
if(!((address ^ debug_address) & debug_mask))
breakpoint();
Instead of:
if((address >= debug_address) & (address < debug_address_plus_length))
breakpoint();
Which would be much more complex to implement in silicon and slow down the CPU. Even in software the first one would run faster.
The address-masking trick works perfectly as long as all memory accesses are aligned.
So let's say p points to address 0xF02 and the breakpoint is 32bits (4 bytes), then the breakpoint address gets aligned to ((0xF02 & -4) == 0xF00)
.
note: -4 is 0xFFFFFFFC (32bits) or 0xFFFFFFFFFFFFFFFC (64bits)
You then access the address (0xF02+2 == 0xF04)
The CPU then masks 0xF04 ((0xF04 & -4) == 0xF04)
before comparing it to the debug breakpoint address (0xF00).
They don't match so the CPU does not trigger the breakpoint.
Upvotes: 2
Reputation: 126787
The data breakpoints are set with the assistance of the CPU, using the debug registers on x86; about them, the Intel manual says (§17.2.5):
Breakpoint address registers (debug registers
DR0
throughDR3
) and theLENn
fields for each breakpoint define a range of sequential byte addresses for a data or I/O breakpoint. TheLENn
fields permit specification of a 1-, 2-, 4- , or 8-byte range, beginning at the linear address specified in the corresponding debug register (DRn). Two-byte ranges must be aligned on word boundaries; 4-byte ranges must be aligned on doubleword boundaries. I/O addresses are zero-extended (from 16 to 32 bits, for comparison with the breakpoint address in the selected debug register). These requirements are enforced by the processor; it uses LENn field bits to mask the lower address bits in the debug registers. Unaligned data or I/O breakpoint addresses do not yield valid results.
(emphasis added)
So, the limitation is in hardware.
Upvotes: 6