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

how to get case number for jump table from a switch statement

我正在学习这本教科书,Randal E. Bryant、David R. O'Hallaron - 计算机系统。程序员的视角 [第 3 版](2016 年,Pearson)
我一直在想作者是如何计算出开关的案例编号 table,如下所示~

我不确定的是他们如何获得案例编号,例如 Case ACase 5: 以及 Case 2/7Case CCase D,依此类推本例中的其他情况

感谢任何帮助谢谢!!

首先,您的书有一个错误 - 它说“a in %rsib in %rdi" - 但这是 not the standard x64 calling convention 并且与程序集的其余部分不一致。

这本书的意思

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

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


默认代码块

前两个操作码是:

cmpq , %rdi
ja .L2

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

movq %rsi, %rdi
movq %rdi, %(rcx) ; this corresponds to *dest = val in C

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


切换代码块

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

  • 如果a == 0那么我们跳到.L3
  • 如果a == 1a == 3a == 6,我们跳转到.L2我们的默认代码块,描述以上)
  • 如果a == 2a == 7我们跳转到.L5
  • 如果a == 4我们跳到.L6
  • 如果a == 5我们跳到.L7

.L3,或案例 0

此块运行 leaq 112(%rdx), %rdi,它只是具有将 %rdi 设置为 %rdx + 112 的效果 - 即 c + 112。然后我们跳转到函数的末尾 - 我们可以得出结论 case 0 代码块中的 val = c + 112


.L7,或案例 5

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


.L6,或案例 4

这里我们直接跳到了函数的末尾,只是调用了 movq %rdi, (%rcx) 操作码——这实际上等同于设置 *dest = a。我们可以得出结论,在这种情况下,val = a.


.L7,或案例 5

此块运行 xorq , %rsi - 相当于 b ^= 15。然后它运行 movq %rsi, %rdx - 将 c 设置为此值。然后我们直接进入上面描述的 .L3 - 它设置 val = c + 112。我们可以得出结论,.L7 是我们的 fall-through switch case。


一般来说,反转 switch cases 可以非常简单 - 它主要涉及理解跳转 table 如何对应于比较寄存器中的不同值(注意这里 a 的几个可能值是如何映射的到 table) 中的相同跳转 - 并了解不同切换情况之间的失败。