• 2019-2020-1 20199325《Linux内核原理与分析》第二周作业


    冯诺依曼计算机硬件框图:

    下面是一个简单的程序example.c。

    intadd_a_and_b(int a,int b){returna+b;}intmain(){returnadd_a_and_b(2,3);}

    gcc 将这个程序转成汇编语言。

    $ gcc-S example.c

    上面的命令执行以后,会生成一个文本文件example.s,里面就是汇编语言,包含了几十行指令。

    example.s经过简化以后,大概是下面的样子。

    _add_a_and_b:push%ebx mov%eax,[%esp+8]mov%ebx,[%esp+12]add%eax,%ebx pop%ebx ret _main:push3push2call _add_a_and_b add%esp,8ret

    可以看到,原程序的两个函数add_a_and_b和main,对应两个标签_add_a_and_b和_main。每个标签里面是该函数所转成的 CPU 运行流程。

    每一行就是 CPU 执行的一次操作。它又分成两部分,就以其中一行为例。

    push%ebx

    这一行里面,push是 CPU 指令,%ebx是该指令要用到的运算子。一个 CPU 指令可以有零个到多个运算子。

    push 指令

    根据约定,程序从_main标签开始执行,这时会在 Stack 上为main建立一个帧,并将 Stack 所指向的地址,写入 ESP 寄存器。后面如果有数据要写入main这个帧,就会写在 ESP 寄存器所保存的地址。

    然后,开始执行第一行代码。

    push3

    push指令用于将运算子放入 Stack,这里就是将3写入main这个帧。

    虽然看上去很简单,push指令其实有一个前置操作。它会先取出 ESP 寄存器里面的地址,将其减去4个字节,然后将新地址写入 ESP 寄存器。使用减法是因为 Stack 从高位向低位发展,4个字节则是因为3的类型是int,占用4个字节。得到新地址以后, 3 就会写入这个地址开始的四个字节。

    push2

    第二行也是一样,push指令将2写入main这个帧,位置紧贴着前面写入的3。这时,ESP 寄存器会再减去 4个字节(累计减去8)。

    call 指令

    第三行的call指令用来调用函数。

    call _add_a_and_b

    上面的代码表示调用add_a_and_b函数。这时,程序就会去找_add_a_and_b标签,并为该函数建立一个新的帧。

    下面就开始执行_add_a_and_b的代码。

    push%ebx

    这一行表示将 EBX 寄存器里面的值,写入_add_a_and_b这个帧。这是因为后面要用到这个寄存器,就先把里面的值取出来,用完后再写回去。

    这时,push指令会再将 ESP 寄存器里面的地址减去4个字节(累计减去12)。

    mov 指令

    mov指令用于将一个值写入某个寄存器。

    mov%eax,[%esp+8]

    这一行代码表示,先将 ESP 寄存器里面的地址加上8个字节,得到一个新的地址,然后按照这个地址在 Stack 取出数据。根据前面的步骤,可以推算出这里取出的是2,再将2写入 EAX 寄存器。

    mov%ebx,[%esp+12]

    上面的代码将 ESP 寄存器的值加12个字节,再按照这个地址在 Stack 取出数据,这次取出的是3,将其写入 EBX 寄存器。

    add 指令

    add指令用于将两个运算子相加,并将结果写入第一个运算子。

    add%eax,%ebx

    上面的代码将 EAX 寄存器的值(即2)加上 EBX 寄存器的值(即3),得到结果5,再将这个结果写入第一个运算子 EAX 寄存器。

    pop 指令

    pop指令用于取出 Stack 最近一个写入的值(即最低位地址的值),并将这个值写入运算子指定的位置。

    pop%ebx

    上面的代码表示,取出 Stack 最近写入的值(即 EBX 寄存器的原始值),再将这个值写回 EBX 寄存器(因为加法已经做完了,EBX 寄存器用不到了)。

    注意,pop指令还会将 ESP 寄存器里面的地址加4,即回收4个字节。

    ret 指令

    ret指令用于终止当前函数的执行,将运行权交还给上层函数。也就是,当前函数的帧将被回收。

    ret

    可以看到,该指令没有运算子。

    随着add_a_and_b函数终止执行,系统就回到刚才main函数中断的地方,继续往下执行。

    add%esp,8

    上面的代码表示,将 ESP 寄存器里面的地址,手动加上8个字节,再写回 ESP 寄存器。这是因为 ESP 寄存器的是 Stack 的写入开始地址,前面的pop操作已经回收了4个字节,这里再回收8个字节,等于全部回收。

    ret

    最后,main函数运行结束,ret指令退出程序执行。

    进行C反编译实验部分
    在GVIM当中使用$ gcc –S –o main.s main.c -m32命令进行反编译!

    在汇编语言当中,能够看到计算机是一个存储数据的计算机,是一个数组,不断的进行压栈进栈,代码首先进入栈中,通过指针的指示,能够将EIP所指的栈空间内指令调出,并运行指令。

  • 相关阅读:
    [OpenCV] Ptr类模板
    [OpenCV]Mat类详解
    [C++] Vector用法
    [OpeCV] highgui头文件
    c++中的.hpp文件
    【2017】KK English
    CMake Tutorial & Example
    [g2o]C++图优化库
    Teradata基础教程中的数据库试验环境脚本
    Oracle中对象权限与系统权限revoke
  • 原文地址:https://www.cnblogs.com/buhery/p/11536613.html
Copyright © 2020-2023  润新知