x86-64下汇编指令switch case部分疑惑
Doubts about the switch case part of the assembly instructions under x86-64
源代码
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 , %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 , %rdi // val += 10
.L6:
addq , %rdi // val += 11
.L2:
movq %rdi, (%rdx) // *dest = val
ret // return
.L7:
addq , %rdi // val += 11
jmp .L2 // break
.L8:
movl [=11=], %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接收到跳转首地址table数组
movslq (%rcx, %rsi, 4), %rax
%rax接收具体的数组元素值,即case对应的操作地址
但是我不知道这里的addq %rcx, %rax
是什么意思...
谁能帮帮我...
谢谢!!!
跳转table实际上并不是构造为包含跳转地址,而是包含偏移量。参见 (.L3-.L4
)。
从table获取偏移量时,需要加上table的基地址,位于leaq .L4(%rip), %rcx
.
该方案使目标文件重定位table;并使用符号扩展加载允许使用 32 位整数解码偏移量以保存 space.
请注意,跳转中的地址 table 存储为相对于标签地址 L4
的偏移量,而不是绝对地址(例如 .L3-.L4
而不仅仅是 .L3
).这允许使用更短的 32 位值而不是 64 位值,并且它使代码位置无关(在任何时候都必须在指令中硬编码绝对地址,否则需要在加载模块时进行修复)。
要进行最后的跳转,代码需要构造相关标签的正确绝对地址(例如.L3
)。这分三个步骤完成:
- 根据
.L4
相对于RIP的偏移量得到.L4
的绝对地址,并将结果存入RCX:leaq .L4(%rip), %rcx
。 someLabel(%rip)
是一种特殊的寻址方式,记录在this docs page的底部。本质上,它将编译为在当前代码行和指令中给定标签之间具有位置无关偏移量的代码,指令的结果将是基于给定相对地址的绝对地址。
- 从跳转table加载相关偏移量并将其存储到RAX中:
movslq (%rcx,%rsi,4), %rax
.
- 然后,将跳转table(现在在RAX中)的偏移量添加到
.L4
(在RCX中)的绝对地址,所以我们得到例如.L4+(.L3-.L4)
= .L3
,并将结果存入RAX:addq %rcx, %rax
.
然后我们可以跳转到计算出的地址:jmp *%rax
.
源代码
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 , %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 , %rdi // val += 10
.L6:
addq , %rdi // val += 11
.L2:
movq %rdi, (%rdx) // *dest = val
ret // return
.L7:
addq , %rdi // val += 11
jmp .L2 // break
.L8:
movl [=11=], %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接收到跳转首地址table数组
movslq (%rcx, %rsi, 4), %rax
%rax接收具体的数组元素值,即case对应的操作地址
但是我不知道这里的addq %rcx, %rax
是什么意思...
谁能帮帮我... 谢谢!!!
跳转table实际上并不是构造为包含跳转地址,而是包含偏移量。参见 (.L3-.L4
)。
从table获取偏移量时,需要加上table的基地址,位于leaq .L4(%rip), %rcx
.
该方案使目标文件重定位table;并使用符号扩展加载允许使用 32 位整数解码偏移量以保存 space.
请注意,跳转中的地址 table 存储为相对于标签地址 L4
的偏移量,而不是绝对地址(例如 .L3-.L4
而不仅仅是 .L3
).这允许使用更短的 32 位值而不是 64 位值,并且它使代码位置无关(在任何时候都必须在指令中硬编码绝对地址,否则需要在加载模块时进行修复)。
要进行最后的跳转,代码需要构造相关标签的正确绝对地址(例如.L3
)。这分三个步骤完成:
- 根据
.L4
相对于RIP的偏移量得到.L4
的绝对地址,并将结果存入RCX:leaq .L4(%rip), %rcx
。someLabel(%rip)
是一种特殊的寻址方式,记录在this docs page的底部。本质上,它将编译为在当前代码行和指令中给定标签之间具有位置无关偏移量的代码,指令的结果将是基于给定相对地址的绝对地址。 - 从跳转table加载相关偏移量并将其存储到RAX中:
movslq (%rcx,%rsi,4), %rax
. - 然后,将跳转table(现在在RAX中)的偏移量添加到
.L4
(在RCX中)的绝对地址,所以我们得到例如.L4+(.L3-.L4)
=.L3
,并将结果存入RAX:addq %rcx, %rax
.
然后我们可以跳转到计算出的地址:jmp *%rax
.