一个简单程序的汇编执行过程分析

  • 题目自拟,内容围绕计算机是如何工作的进行;
  • 博客中需要使用实验截图
  • 博客内容中需要仔细分析汇编代码的工作过程中堆栈的变化
  • 总结部分需要阐明自己对“计算机是如何工作的”理解。

实验环境:ubuntu64位

源代码:

int g(int x){
    return x + 4;
}
int f(int x){
    return g(x);
}
int main(){
    return f(10) + 5;
}

编译命令:

gcc -S -o anlysis_assemble.s anlysis_assemble.c -m32 //

生成的汇编代码为:

.file    "anlysis_assemble.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    $4, %eax
    popl    %ebp
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    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    $10, (%esp)
    call    f
    addl    $5, %eax
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE2:
    .size    main, .-main
    .ident    "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
    .section    .note.GNU-stack,"",@progbits

删除伪代码,最简化后的代码为:

g:
    pushl   %ebp
    movl    %esp, %ebp
    movl    8(%ebp), %eax
    addl    $4, %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    $10, (%esp)
    call    f
    addl    $5, %eax
    leave
    ret

 首先对push指令进行分析:

pushl %eax 等价于以下两句:

subl $4, %esp            # esp = esp - 4, 栈向下增长, 栈顶指针向下增长

movl %eax, (%esp) # 将寄存器eax内容取出到esp指向的地址的内存,即栈顶位置

分析pop指令:

popl %eax 等价于以下两句:

movl (%esp), %eax  # 将esp指向的栈顶处内存值赋值给eax 

addl $4, %esp          # esp栈顶向上缩小

可以用这两个指令替代push 或 pop

分析call指令

call 0x12345 等价与以下两句:

pushl %eip

movl $0x12345, %eip

分析 ret 指令

ret 等价与 popl %eip

这里注意的是eip不能在写汇编代码时直接修改,只能间接修改

分析几段汇编指令片段

例子一

pushl $8  # 假设当前栈为空,栈顶和栈底都指向相同地址,esp = esp - 4, %esp = 8

movl %esp, %ebp #则ebp也指向esp的位置

subl $4,%esp #esp向下移动一个位置

movl $8, (%esp) # 将8赋值到栈顶位置

例子二

pushl $8

movl %esp, $ebp

pushl %esp  # 将当前位置放到栈中

pushl $8

addl $4, %esp

popl %esp

分析leave和enter指令,它们分别由两条指令组成

enter: #进入函数调用堆栈

  pushl %ebp

  movl %esp, %ebp

leave: #撤销函数调用堆栈

  movl %ebp, %esp

  popl %ebp

函数调用堆栈是由逻辑上多个堆栈叠加起来的。

ebp , esp是相对的栈底和栈顶,每个函数都有自己的栈顶和栈底

函数的返回值默认使用eax寄存器返回给上一级函数

原文地址:https://www.cnblogs.com/jinee/p/4536945.html