【问题标题】:RISC pipeline effect on setting breakpoints?RISC管道对设置断点的影响?
【发布时间】:2021-03-03 05:21:06
【问题描述】:

RISC Pipelining 中的说明坚持 5 个步骤。
我有一个关于流水线是否会影响设置断点的问题。

示例:
假设下面的二进制文件正在运行并且$pcline 1

line1: lwz r11 8(r31)     <= PC @ here
line2: lwz r0, 0(r31)
line3: cmpwi cr7, r10, 0
line4: lwz r9, 4(r31)
line5: stw r11, 0xA0(r1)

管道状态(我的猜测):
据我所知,PPC 指令有 5 种状态:获取、解码、执行、内存访问、写回
在这一刻,我猜流水线会像下面这样。对吗?

line1: lwz r11 8(r31)     <= execution (because PC is at here)
line2: lwz r0, 0(r31)     <= decode
line3: cmpwi cr7, r10, 0  <= fetch
line4: lwz r9, 4(r31)
line5: stw r11, 0xA0(r1)

问题

  • 我写的状态是否正确?
  • 此时,是不是不允许调试器在运行时更改line 3中的指令?
    (比如在line 3处设置断点?)

【问题讨论】:

  • 这都是非常多的实现定义的,对于初学者来说,真正的硬件管道看起来是教科书式的,不一定是现实的。如果定义了一个实现,那么调试器如何工作没有涵盖所有实现的通用答案。

标签: assembly pipeline breakpoints cpu-architecture powerpc


【解决方案1】:

在真正的微处理器上,这取决于您是否...

  • ...使用 JTAG 调试器
    (这意味着当断点出现时某些外部硬件正在停止 CPU 到达)

    在这种情况下,如果 CPU 在断点处停止时更改行“2”或“3”会发生什么,这取决于 CPU。可能有 CPU 从断点继续后会再次读取内存,而其他 CPU 则不这样做。

    我不知道 PowerPC 微控制器(如 MPC57xx)的行为。

    但是,我猜在大多数微控制器中,硬件的设计方式是(有意)在断点的情况下管道无法“正常”工作:到达断点后,“第 1 行”到“第 3 行” " 从内存中重新读取。

  • ...或者如果您正在执行片上调试
    (这意味着调试软件与被调试软件运行在同一个 CPU 上)

    在这种情况下,输入断点时会输入一些异常。

    对于 PowerPC,异常返回使用 rfdi 或(对于较旧的控制器)rfci 指令。

    这意味着调试器使用rfdi指令继续被调试的程序。

    rfdirfirfci 是跳转/分支指令。在跳转/分支之后,CPU 无论如何都必须重新读取管道。

    这意味着CPU肯定会再次从内存中读取“第1行”,因此您甚至可以在断点处修改“第1行”。

【讨论】:

  • 在跳转/分支之后,CPU 无论如何都必须重新读取管道。 - 对于 PowerPC 的体系结构是否有保证,就像非官方的 x86 一样? (现代 x86 甚至更强大,Observing stale instruction fetching on x86 with self-modifying code AFAIK 是不可能的,但是在某些 CPU 中,某种 jmp 会刷新指令预取。)但是,对于没有 I-cache 甚至连贯的 PowerPC,为什么 CPU 必须重新取指(或保持已经这样做的错觉)在正确预测的分支之后?
  • 或者这种效果在没有缓存的 PowerPC 中真的可以观察到吗?
  • @PeterCordes 在异常处理过程中,异常处理本身的指令在管道中;不是发生异常的代码的说明。这适用于任何 CPU。 OP的问题归结为异常处理程序是否可以修改发生异常的代码的问题。
  • 对,这就是他们的要求,但您似乎对所有跳转/分支做出了更广泛的声明,其中似乎包括正常的用户空间跳转。你真的只是指特权转移分支,比如从异常中返回吗? (与管道本身分开,如果您没有手动完成,是否从异常同步 D-cache 返回到 I-cache?)
  • @PeterCordes 这不是我想说的。我想说的是:当您执行“返回”指令时(例如 x86 上的ret,MIPS 上的jr ra 或 PowerPC 上的blr),管道不会“恢复”到“呼叫”指令;相反,必须在“返回”指令之前或之后的某个时间点从内存中重新读取流水线内容。然而,只有在“return”指令以某种方式“恢复”了管道时,OP 的担忧才是合理的。
【解决方案2】:

如果您想查看指令流经 POWER 处理器管道时的许多状态,您可以进行指令跟踪,使用 Power Performance Simulator 模拟流经各种管道的指令(通常有很多) ,并使用与 Power Performance Simulator 关联的查看器之一逐个周期地查看状态变化。我写了一篇文章 (https://developer.ibm.com/technologies/linux/tutorials/l-porting-tuning-tools/) 快速总结了如何做到这一点。

话虽如此,像 gdb 这样的用户级调试器认为指令大致是原子的。一条指令执行之前的状态,就好像前面的所有指令都已完成,而当前指令没有改变任何状态。

【讨论】:

    【解决方案3】:

    流水线阶段不是架构性的(例如,高性能 PowerPC CPU 可以拥有更长的流水线,而无需更改任何软件可见的内容)。这不是单步执行的一部分,除非您使用软件 模拟器 来查看指令通过模拟 CPU。

    如果您在设置另一个断点或单步时在断点处停止,则正在调试的代码中的任何指令都不会在管道中运行。 CPU 将处于休眠状态或运行调试器的代码。

    此外,PowerPC 没有一致的指令缓存,因此运行 stw 指令并在其自身之后修改了几条指令的自修改代码不会必然导致获取新指令,即使是超过 5 条指令之后。为此,您需要 一堆 指令,如 dcbfmsync 将 D-cache 刷新到更高级别,然后 icbi 使该行 I- 无效缓存,然后msyncisync 以确保在指令获取之前发生。例如,https://www.nxp.com/docs/en/application-note/AN3441.pdf 记录了您应该在第 2.2 节指令缓存一致性中执行的操作,至少对于 PowerPC 的特定实现。 (我认为飞思卡尔的 PowerQUICC™ III 是 PowerPC;这个文档是 google 找到的。)

    奇怪的是 GNU C __builtin___clear_cache(start, end) 对 GCC 没有任何作用。 https://godbolt.org/z/nbre7Y 可能是因为推荐的过程涉及将页面标记为不可执行,如果没有系统调用,您无法从用户空间执行此操作?


    调试器本身在进程未运行时修改内存会更容易;操作系统已经必须确保加载到内存中或使用ptrace 修改的页面可以安全地执行代码。因此它可以将大部分刷新工作留给操作系统。

    当内核在被调试的进程中返回用户空间时,如果第一条或后面的任何一条指令是调试陷阱/软件断点指令,就会陷入陷阱。

    【讨论】:

      猜你喜欢
      • 2016-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-02
      • 1970-01-01
      • 2020-03-09
      相关资源
      最近更新 更多