Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does data breakpoint not work on unaligned address

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 ?

like image 999
amanjiang Avatar asked Jul 22 '14 06:07

amanjiang


People also ask

Why is my breakpoint not working?

If a source file has changed and the source no longer matches the code you're debugging, the debugger won't set breakpoints in the code by default. Normally, this problem happens when a source file is changed, but the source code wasn't rebuilt. To fix this issue, rebuild the project.

Where should a breakpoint be set?

You set breakpoints wherever you want to pause debugger execution. For example, you may want to see the state of code variables or look at the call stack at a certain breakpoint. If you are trying to resolve a warning or issue while using breakpoints, see Troubleshoot breakpoints in the Visual Studio debugger.

How do you fix the breakpoint will not currently be hit C++?

solution -> Properties -> Startup Project -> startup projects -> Start action , Debug/General settings -> uncheck Just My Code , quit VC++, reload, restart PC.


2 Answers

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 through DR3) and the LENn fields for each breakpoint define a range of sequential byte addresses for a data or I/O breakpoint. The LENn 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.

like image 157
Matteo Italia Avatar answered Oct 21 '22 00:10

Matteo Italia


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:

  • 16bits (2 bytes) breakpoints get its lowest address bit cleared (addr & -2).
  • 32bits (4 bytes) breakpoints get its 2 lowest address bits cleared (addr & -4).
  • 64bits (8 bytes) breakpoints get its 3 lowest address bits cleared (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.

like image 34
Stephane Hockenhull Avatar answered Oct 21 '22 00:10

Stephane Hockenhull