【问题标题】:Why does data breakpoint not work on unaligned address为什么数据断点在未对齐的地址上不起作用
【发布时间】:2014-07-22 06:49:01
【问题描述】:

在 Visual Studio 中调试 C++ 项目时, 一些数据断点从未命中。

所以我写了一些代码进行测试:

#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
}

如您所见,在函数测试中,*p 的值将首先归零, 那么它将被隐式更改,我有一个小端CPU,所以它 必须是 65536。

如果在 p(4 字节)上设置数据断点来检测变化, 你会得到两种不同的结果:命中与否。这取决于 p 指向的地址。

在我上面的测试代码中,测试#1 会命中,而测试#2 不会, #1 和 #2 之间的区别是返回的地址 alloc(0) 和 alloc(2)。

MSDN上的这篇文章How to: Set a Data Breakpoint不讲这个。

数据断点在未对齐的地址上不起作用吗?

【问题讨论】:

    标签: c++ debugging visual-c++ breakpoints


    【解决方案1】:

    数据断点是在 CPU 的帮助下设置的,使用 x86 上的调试寄存器;关于它们,英特尔手册说(§17.2.5):

    断点地址寄存器(调试寄存器DR0 到DR3)和每个断点的LENn 字段定义了一个 数据或 I/O 断点的连续字节地址范围。 LENn 字段允许指定 1-、2-、4- ,或 8 字节范围,从相应调试寄存器 (DRn) 中指定的线性地址开始。 双字节 范围必须在字边界上对齐; 4 字节范围必须在双字边界上对齐。 I/O 地址是零扩展的(从 16 位到 32 位,用于与所选调试中的断点地址进行比较 登记)。这些要求由处理器强制执行;它使用 LENn 字段位来屏蔽低地址位 在调试寄存器中。 未对齐的数据或 I/O 断点地址不会产生有效结果。

    (强调)

    所以,限制在于硬件。

    【讨论】:

      【解决方案2】:

      详细解释为什么会这样:

      数据断点使用 CPU 的调试寄存器。在 x86 上,这些调试寄存器通过屏蔽地址的低位来与其数据大小对齐:

      • 16 位(2 字节)断点清除其最低地址位(addr &amp; -2)。
      • 32 位(4 字节)断点清除其 2 个最低地址位 (addr &amp; -4)。
      • 64 位(8 字节)断点清除其 3 个最低地址位(addr &amp; -8)。

      当 x86 CPU 访问内存时,它会以相同的方式通过屏蔽地址与调试寄存器地址进行比较,如果两者相等,它会触发断点。


      这是在调试断点比较器中简化电子电路的一个技巧:只需要比较两个对齐的地址。

      它以电子方式转换为伪代码:

      if(!((address ^ debug_address) & debug_mask))
          breakpoint();
      

      代替:

      if((address >= debug_address) & (address < debug_address_plus_length))
          breakpoint();
      

      在硅片中实现要复杂得多,而且会降低 CPU 的速度。即使在软件中,第一个也会运行得更快。

      只要所有内存访问都对齐,地址屏蔽技巧就可以完美运行。


      假设 p 指向地址 0xF02 并且断点是 32 位(4 字节),那么断点地址与 ((0xF02 &amp; -4) == 0xF00) 对齐。

      注意:-4 是 0xFFFFFFFC(32 位)或 0xFFFFFFFFFFFFFFFC(64 位)

      然后你访问地址(0xF02+2 == 0xF04)

      CPU 然后屏蔽 0xF04 ((0xF04 &amp; -4) == 0xF04),然后将其与调试断点地址 (0xF00) 进行比较。

      它们不匹配,因此 CPU 不会触发断点。

      【讨论】:

        猜你喜欢
        • 2022-11-10
        • 2010-09-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多