• 通过反汇编代码探究计算机运行过程


    在线学习了Mooc的《计算机内核分析》课程,为了探究计算机运行过程,现做博文记录实验过程。


    首先打开虚拟机中的linux环境,输入C语言代码:

    int g(int x)
    {
      return x + 3;
    }
    
    int f(int x)
    {
      return g(x);
    }
    
    int main(void)
    {
      return f(8) + 1;
    }


    保存为main.c文件


    使用反汇编命令

    gcc -S -o main.s main,c -m32

    -S : 编译为汇编语言程序

    -o : 指定目标程序名称

    -m32 : 指定编译为32位环境的汇编代码


    编译结果:

    	.file	"main.c"
    	.text
    	.globl	g
    	.type	g, @function
    g:
    .LFB0:
    	.cfi_startproc
    	pushl	%ebp
    	.cfi_def_cfa_offset 8
    	.cfi_offset 5, -8
    	movl	%esp, %ebp
    	.cfi_def_cfa_register 5
    	movl	8(%ebp), %eax
    	addl	$3, %eax
    	popl	%ebp
    	.cfi_def_cfa 4, 4
    	.cfi_restore 5
    	ret
    	.cfi_endproc
    .LFE0:
    	.size	g, .-g
    	.globl	f
    	.type	f, @function
    f:
    .LFB1:
    	.cfi_startproc
    	pushl	%ebp
    	.cfi_def_cfa_offset 8
    	.cfi_offset 5, -8
    	movl	%esp, %ebp
    	.cfi_def_cfa_register 5
    	subl	$4, %esp
    	movl	8(%ebp), %eax
    	movl	%eax, (%esp)
    	call	g
    	leave
    	.cfi_restore 5
    	.cfi_def_cfa 4, 4
    	ret
    	.cfi_endproc
    .LFE1:
    	.size	f, .-f
    	.globl	main
    	.type	main, @function
    main:
    .LFB2:
    	.cfi_startproc
    	pushl	%ebp
    	.cfi_def_cfa_offset 8
    	.cfi_offset 5, -8
    	movl	%esp, %ebp
    	.cfi_def_cfa_register 5
    	subl	$4, %esp
    	movl	$8, (%esp)
    	call	f
    	addl	$1, %eax
    	leave
    	.cfi_restore 5
    	.cfi_def_cfa 4, 4
    	ret
    	.cfi_endproc
    .LFE2:
    	.size	main, .-main
    	.ident	"GCC: (GNU) 4.6.2 20111027 (Red Hat 4.6.2-1)"
    	.section	.note.GNU-stack,"",@progbits

    除去代码中的标志符号后:

    g:
    	pushl	%ebp
    	movl	%esp, %ebp
    	movl	8(%ebp), %eax
    	addl	$3, %eax
    	popl	%ebp
    	ret
    f:
    	pushl	%ebp
    	movl	%esp, %ebp
    	subl	$4, %esp
    	movl	8(%ebp), %eax
    	movl	%eax, (%esp)
    	call	g
    	leave
    	ret
    main:
    	pushl	%ebp
    	movl	%esp, %ebp
    	subl	$4, %esp
    	movl	$8, (%esp)
    	call	f
    	addl	$1, %eax
    	leave
    	ret

    下面对此段汇编代码进行分析,首先要熟悉一些基本的汇编知识:

    为了计算方便,加快计算机运行速度,计算机中配置了很多寄存器,这些寄存器在计算机工作中起到各种不同的重要作用:

    eax默认作为函数返回值保存寄存器

    ebp为栈低指针寄存器

    esp为栈顶指针寄存器(运行中的每个程序都有一段堆栈空间,用来存放程序运行时数据)

    eip为当前指令位置寄存器

    push为压栈指令

    pop为出栈指令

    mov为转移指令,通过该指令有七种寻址方式:
    mov eax,0x1234 //立即寻址
    mov eax,ebx //寄存器寻址
    mov eax,[ebx] //寄存器间接寻址
    mov eax,[0x1234] //直接寻址
    mov eax,[ebx+0x1234] //寄存器相对寻址
    mov eax,[esi+edi] //基址变址寻址
    mov eax,[esi+edi+0x1234] //基址变址相对寻址

    add为加法指令

    此外还有一些宏指令,它们对应一行或多行汇编代码:

    ret : pop %eip(实则返回指令)

    leave : mov %ebp %esp

    pop %ebp

    enter :  push %ebp

    mov %esp %ebp

    call 0x12345 : push %eip

    mov $0x12345 , %eip

    在指令后加上b、l、w、q分别代表8位、16位、32位、64位操作


    下面直接通过分析代码来熟悉这些指令:

    首先程序从main函数开始执行,

    pushl	%ebp

    将基址寄存器的值进栈,用于系统控制main函数返回,此时堆栈情况如图:


    movl	%esp, %ebp

    此行代码将esp寄存器的内容赋值给ebp,即ebp和esp都指向地址1的位置

    subl	$4, %esp

    将esp的指针指向下面一个位置

    movl	$8, (%esp)

    将立即数8存放在当前esp所指位置,堆栈的情况如图:


    call	f

    调用f函数,即执行下面的指令

    push %eip //将当前的eip入栈,即将下一条指令[addl $1, %eax]的地址入栈

    mov f, %eip //函数f的入口地址赋值到eip中,即程序跳转到f函数入口处

    pushl	%ebp

    f函数的第一条指令,同main函数,如图:


    movl	%esp, %ebp

    初始化f函数的堆栈,如图:


    subl	$4, %esp<span style="white-space:pre">		</span>//栈顶下移

    movl	8(%ebp), %eax<span style="white-space:pre">		</span>//栈低加8对应地址的数据存入eax中
    movl	%eax, (%esp)<span style="white-space:pre">			</span>//eax中数据存入esp所指空间
    如图:


    call	g

    调用g函数

    pushl	%ebp
    movl	%esp, %ebp
    movl	8(%ebp), %eax
    addl	$3, %eax

    如图:


    popl	%ebp

    出栈,如图:


    ret<span style="white-space:pre">		</span>//pop %eip

    eip指向f函数中leave指令的地址,开始继续执行


    leave

    mov %ebp %esp

    pop %ebp


    ret

    返回main函数


    addl	$1, %eax<span style="white-space:pre">		</span>//eax = 12
    leave<span style="white-space:pre">			</span>//
    ret

    如图:



    程序执行完毕,main函数返回交给系统处理。


    计算机执行就是顺序执行的过程,借助着寄存器和堆栈实现程序的跳转执行。

  • 相关阅读:
    jquery清空下拉框,保留第一个
    [转]js和jquery获取窗体高度
    点击回车 按钮不执行点击事件
    时间戳 Date.parse()和dateObject.getTime()的区别
    uni-app 时间格式问题 new Date(str) IOS系统跟Android系统不兼容
    uni-app 使用 iconfont 图标 自定义图标
    uni-app 使用Vuex+ (强制)登录
    uni-app 保持登录状态 (Vuex)
    package.json和npm install、cnpm install 的問題
    如何将baseUrl项目地址提取放到放到static
  • 原文地址:https://www.cnblogs.com/slz-coder150315/p/4376368.html
Copyright © 2020-2023  润新知