如何从 switch 语句获取跳转表的案例编号

Meg*_*rcy 0 c x86 assembly switch-statement jump-table

我正在阅读这本教科书,Randal E. Bryant、David R. O\xe2\x80\x99Hallaron - 计算机系统。A Programmer\xe2\x80\x99s Perspective [3rd ed.] (2016, Pearson)
\n我一直在弄清楚作者如何计算出开关表的案例编号,如下所示~

\n

我不确定的是他们如何获得案例编号,例如Case Ais Case 5: 以及如何Case 2/7isCase CCase D,对于本示例中的其余案例,依此类推

\n

任何帮助表示感谢,谢谢!!

\n

开关指令

\n

汇编代码

\n

给出答案

\n

Dan*_*ein 11

首先,你的书有一个错误- 它说“ ain %rsi, bin %rdi” - 但这不是标准的 x64 调用约定,并且与程序集的其余部分不一致。

这本书的意思是

  • %rdi-> a%rsi-> b%rdx->c%rcx->dest

继续,让我们了解发生了什么:


默认代码块

前两个操作码是:

cmpq $7, %rdi
ja .L2
Run Code Online (Sandbox Code Playgroud)

ja是如果上面则跳转,即如果a > 7则转到.L2- 位于程序集的末尾。我们可以推断出这是default代码块(它立即继续到函数末尾) - 在下面.L2我们有:

movq %rsi, %rdi
movq %rdi, %(rcx) ; this corresponds to *dest = val in C
Run Code Online (Sandbox Code Playgroud)

所以我们可以得出结论,在这种情况下%(rcx)gets%rsi的值 - 换句话说,在默认代码块中,val = b.


切换代码块

如果我们ja上面的第一个没有执行,那么我们jmp *.L4(,%rdi,8). 由于a不高于 7,我们有八种可能性 - 我们可以在.L4表中看到:

  • 如果a == 0然后我们跳到.L3
  • 如果a == 1a == 3、 或a == 6,我们跳转到.L2我们的默认代码块,如上所述)
  • 如果a == 2或者a == 7我们跳到.L5
  • 如果a == 4我们跳到.L6
  • 如果a == 5我们跳到.L7

.L3,或情况 0

该块运行时,仅具有设置为-which isleaq 112(%rdx), %rdi的效果。然后我们跳到函数的末尾——我们可以在代码块中得出结论。%rdi%rdx + 112c + 112val = c + 112case 0


.L7,或情况 5

该块运行leaq (%rdx, %rsi), %rdi,它设置%rdi%rdx + %rsi(即c + b) - 然后调用salq $2, %rdi,它只是将该值左移 2 位 - 总值为(c + b) << 2。然后我们跳到函数的末尾——我们可以val = (c + b) << 2case 5代码块中得出结论。


.L6,或情况 4

这里我们立即跳转到函数的末尾,仅调用movq %rdi, (%rcx)操作码 - 这实际上相当于设置*dest = a。我们可以得出结论,在这种情况下,val = a.


.L7,或情况 5

该块运行xorq $15, %rsi- 相当于b ^= 15. 然后它运行movq %rsi, %rdx- 设置c为该值。然后我们继续直接进入.L3上面描述的-哪组val = c + 112。我们可以得出结论,这.L7就是我们的失败开关案例。


一般来说,反转开关情况可以非常简单 - 它主要涉及理解跳转表如何对应于比较寄存器中的不同值(请注意这里几个可能的值如何a映射到表中的同一跳转) - 并理解下降 -不同开关盒之间的穿通。

  • 这一问题中的调用约定寄存器混合列在勘误表中,https://csapp.cs.cmu.edu/3e/errata.html。所以希望这不是 CS:APP 3e 全球版的标志,这是一场[灾难](/sf/ask/4059929891/) (2认同)
  • 我们还请读者注意,“ja”是无符号比较,因此负长值将“高于”7 并采用默认值。 (2认同)