CSAPP讨论课-switch跳转表

题目

选题三

对于以下c程序:

#include <stdio.h>

  int main()
  {
    int i=0;
    int j=0;
    switch(i)
    {
      case 1:
        j+=1;
        break;
      case 2:
        j+=2;
        break;
      case 3:
        j+=3;
        break;
      case 4:
        j+=4;
        break;
      case 5:
        j+=5;
      case 6:
        j+=70;
        break;
      default:
        j+=5;
        break;
    }
    return 0;
  }

(1)将其编译成汇编代码,找到跳转表,并分析汇编代码是如何通过跳转表来完成switch功能的;

(2)将分支条件调整为case 6,case 2,case 5,case 3,case 4,case 1(即交换一下分支条件顺序),观察跳转表的变化情况。

(3)将分支条件调整为case 5, case 3, case 2, case1,或是调整为case 138,case 106, case 2, case 9, case 68后,汇编后的代码中不包括跳转表,而是采用cmpl,je, jmp等指令来实现switch功能,请再找出几组能生成跳转表与不生成跳转表的分支条件组合,并分析编译器在哪些情况下更有可能采用跳转表的方式来实现switch功能。同时考察不同的编译器是否有不同的行为。

第一问:跳转表

编译:gcc switch.c -S -o switch.s -O0
汇编如下,地址跳转部分有注释:

main:
.LFB0:
	.cfi_startproc
	endbr32
	pushl	%ebp
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	movl	%esp, %ebp
	.cfi_def_cfa_register 5
	subl	$16, %esp
	call	__x86.get_pc_thunk.ax
	addl	$_GLOBAL_OFFSET_TABLE_, %eax

	movl	$0, -4(%ebp)	;i=0
	movl	$0, -8(%ebp)	;j=0
	cmpl	$6, -4(%ebp)	;if i>6, jmp default
	ja	.L2
	movl	-4(%ebp), %edx	;edx=i=0
	sall	$2, %edx		;edx=4*i=0
	movl	.L4@GOTOFF(%edx,%eax), %edx ;edx=table[4*i]
	addl	%edx, %eax
	notrack jmp	*%eax		;jmp table[edx]
	.section	.rodata
	.align 4
	.align 4
.L4:
	.long	.L2@GOTOFF		;default
	.long	.L9@GOTOFF		;case 1
	.long	.L8@GOTOFF		;case 2
	.long	.L7@GOTOFF		;case 3
	.long	.L6@GOTOFF		;case 4
	.long	.L5@GOTOFF		;case 5
	.long	.L3@GOTOFF		;case 6
	.text
.L9:
	addl	$1, -8(%ebp)
	jmp	.L10
.L8:
	addl	$2, -8(%ebp)
	jmp	.L10
.L7:
	addl	$3, -8(%ebp)
	jmp	.L10
.L6:
	addl	$4, -8(%ebp)
	jmp	.L10
.L5:
	addl	$5, -8(%ebp)
.L3:
	addl	$6, -8(%ebp)
	jmp	.L10
.L2:
	addl	$70, -8(%ebp)
	nop
.L10:
	movl	$0, %eax
	leave
	.cfi_restore 5
	.cfi_def_cfa 4, 4
	ret
	.cfi_endproc

第二问:交换分支顺序

将分支条件调整为case 6,case 2,case 5,case 3,case 4,case 1(即交换一下分支条件顺序),汇编结果不变
在这里插入图片描述

第三问:是否生成跳转表

不同编译器对switch-case的优化

总结

The compiler may choose cmp/je when the numbers are far apart; if the numbers are close together, the compiler will typically use a jumptable.

1、密集分布的case分支,采用跳转表
2、稀疏分布的case分支,采用直接跳转;若总数量较多,或者间隔较大,用二叉树搜索式跳转

参考

C++逆向学习三步走 :1.6、switch-case识别技巧提高

笔记:
switch-case语句(Release版本)
<1>switch里面的值如果是常量,则编译器直接裁掉无用分支,保留所要执行的分支。
<2>case如果case后面语句有规律的变化则编译器会对switch中的值进行减法,并依次与case后面值进行比较。
<3>case中的语句块内容如果一样,则编译器并不会将其合并。
<4>case后面的值如果没有规律且跨度不大,跨度值有一个上限,在此范围内则编译器会使用jump table处理分支跳转。
<5>如果case后面的值差距在255之间则使用间接表,即稀疏矩阵进行查找。
<6>如果case后面的值差距大于255,则用平衡二叉树,时间复杂度为2^k - 1(k为二叉树深度)。

https://stackoverflow.com/questions/17061967/c-switch-and-jump-tables

一个方便查看不同编译器汇编代码的网站:https://godbolt.org/

  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值