【问题标题】:Doubts about the switch case part of the assembly instructions under x86-64 [duplicate]x86-64下汇编指令switch case部分的疑惑[重复]
【发布时间】:2022-01-09 14:41:25
【问题描述】:

源代码

void switch_eg(long x, long n, long* dest) {
    long val = x;
    switch(n) {
        case 0: 
            val *= 13;
            break;
        case 2:
            val += 10;
        case 3:
            val += 11;
            break;
        case 4:
        case 6:
            val += 11;
            break;
        default: 
            val = 0;
    }
    *dest = val;
} 

对应的汇编代码

.LFB0:
    // x in %rdi, n in %rsi, *dest in %rdx
    cmpq    $6, %rsi
    ja  .L8 // if n > 6, switch->default
    -------------------------
    leaq    .L4(%rip), %rcx
    movslq  (%rcx,%rsi,4), %rax
    addq    %rcx, %rax
    jmp *%rax
    -------------------------
    
    
.L4:
    .long   .L3-.L4 // case 0
    .long   .L8-.L4 // case 1
    .long   .L5-.L4 // case 2
    .long   .L6-.L4 // case 3
    .long   .L7-.L4 // case 4
    .long   .L8-.L4 // case 5
    .long   .L7-.L4 // case 6
.L3:
    leaq    (%rdi,%rdi,2), %rax // %rax = 3 * val
    leaq    (%rdi,%rax,4), %rdi // val = 4 * 3 * val + val = 13val
    jmp .L2 // break
.L5:
    addq    $10, %rdi // val += 10
.L6:
    addq    $11, %rdi // val += 11
.L2:
    movq    %rdi, (%rdx) // *dest = val
    ret // return 
.L7:
    addq    $11, %rdi // val += 11
    jmp .L2 // break
.L8:
    movl    $0, %edi // val = 0
    jmp .L2 // break

谁能解释一下下面汇编代码的含义?

    leaq    .L4(%rip), %rcx
    movslq  (%rcx,%rsi,4), %rax
    addq    %rcx, %rax
    jmp *%rax

在我看来,

leaq .L4(%rip), %rcx %rcx 接收跳转表数组的首地址

movslq (%rcx, %rsi, 4), %rax %rax接收具体的数组元素值,即case对应的操作的地址

但我不知道这里的addq %rcx, %rax 是什么意思...

谁能帮帮我... 谢谢!!!

【问题讨论】:

    标签: assembly switch-statement x86-64


    【解决方案1】:

    跳转表实际上不是用来包含跳转地址的,而是包含偏移量的。请参阅 (.L3-.L4)。

    从表中取出偏移量时,需要加上表的基地址,位于leaq .L4(%rip), %rcx

    此方案使目标文件可重定位;并且使用符号扩展加载允许使用 32 位整数来解码偏移量以节省空间。

    【讨论】:

      【解决方案2】:

      请注意,跳转表中的地址存储为相对于标签L4 地址的偏移量,而不是绝对地址(例如.L3-.L4 而不仅仅是.L3)。这允许使用更短的 32 位值而不是 64 位,并且它使代码与位置无关(在任何时候都必须在指令中硬编码绝对地址,否则将需要在加载模块时进行修复)。

      要进行最后的跳转,代码需要构造相关标签的正确绝对地址(例如.L3)。这分三个步骤完成:

      1. 根据相对于 RIP 的偏移量获取.L4 的绝对地址,并将结果存储到 RCX:leaq .L4(%rip), %rcxsomeLabel(%rip) 是一种特殊的寻址模式,记录在 this docs page 的底部。本质上,它将编译为在当前代码行和指令中给定标签之间具有与位置无关的偏移量的代码,并且指令的结果将是基于给定相对地址的绝对地址。
      2. 从跳转表中加载相关偏移量并存储到RAX中:movslq (%rcx,%rsi,4), %rax
      3. 然后,将跳转表(现在在 RAX 中)的偏移量添加到 .L4(在 RCX 中)的绝对地址,所以我们得到例如.L4+(.L3-.L4) = .L3,并将结果存储在 RAX 中:addq %rcx, %rax

      然后我们可以跳转到计算出来的地址:jmp *%rax

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-11
        • 2015-06-29
        • 1970-01-01
        • 1970-01-01
        • 2011-08-18
        • 1970-01-01
        相关资源
        最近更新 更多