• 从一段代码的汇编看计算机的工作原理


    朱宇轲 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

    /*-------------------------------------以下内容是课堂笔记,咿呀咿呀呦!------------------------------------------*/

    本课主要对计算机的运行原理和汇编语言进行了简单的介绍。

    冯若依曼体系结构即存储程序计算机,也就是将程序写在内存中,由CPU通过总线从内存中读取一条条程序,根据程序的内容执行具体的步骤。

    如图所示

    CPU在读取指令时,通过寄存器IP来指向下一条指令(如果是32位系统,则为EIP)

    CPU的寄存器分为通用寄存器、段寄存器、状态寄存器

    四种寻址方式:

    movl %eax,%edx     edx=eax                          寄存器寻址

    movl $0x123,%edx  edx=0x123                       立即寻址

    movl 0x123,%edx    edx=*(int32_t*)0x123        直接寻址

    movl (%ebx),%edx   edx=*(int32_t*)ebx           间接寻址

    movl 4(%ebx),%edx  edx=*(int32_t*)(ebx+4)    变址寻址

    了解pushl、popl、call 0x12345、ret命令

    注意:IP寄存器一般不能随便修改,只能通过call、ret等命令更改!

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

        

    /*-------------------------以下内容是实验分析,咿呀咿呀呦!------------------------------------------*/

         首先写下这么一段C程序:

     1 //linux.c
     2 int g(x)
     3 {
     4     return x+3;
     5 }
     6 int f(x)
     7 {
     8     return g(x);
     9 }
    10 int main()
    11 {
    12     return f(10)+1;
    13 }

      在Linux的环境中输入如下指令:

    gcc –S –o linux.s linux.c -m32

      然后打开linux.s,就可以看到我们汇编后的代码(直接上截图了)

     

       将里面以“.”开头的行去掉(这是为链接用的),得到汇编后的代码:

     1 g:
     2     pushl   %ebp
     3     movl    %esp, %ebp
     4     movl    8(%ebp), %eax
     5     addl    $3, %eax
     6     popl    %ebp
     7     ret
     8 f:
     9     pushl   %ebp
    10     movl    %esp, %ebp
    11     subl    $4, %esp
    12     movl    8(%ebp), %eax
    13     movl    %eax, (%esp)
    14     call    g
    15     leave
    16     ret
    17 main:
    18     pushl   %ebp
    19     movl    %esp, %ebp
    20     subl    $4, %esp
    21     movl    $10, (%esp)
    22     call    f
    23     addl    $1, %eax
    24     leave
    25     ret

       接下来我们来分析一下改程序具体的流程。

      程序一开始,CPU的IP寄存器指向汇编代码的第18行,假设堆栈在内存中的地址分别为0,1,2,3……堆栈基指针寄存器(EBP)和堆栈顶指针寄存器(ESP)均指向堆栈段0处。

      第18~21行首先为main函数开辟新的内存区域,之后将传的参数10入栈,此时堆栈段如下所示:

      

      然后程序调用call 函数,将IP入栈,IP指向代码第9行f处。

      在f函数的代码处,首先为f函数开辟新的内存区域,接着将传入的参数10赋值给EAX,并将EAX入栈,此时堆栈段内存如下图:

      程序在此调用call进入g函数。在g函数中,同样先是开辟内存空间,然后将参数传给EAX,并将EAX的值加上3。

      之后将EBP出栈,并调用ret命令。此时IP重新指向f函数call之后的命令,堆栈内存的情况如下:

      

      之后就是不断的调用leave与ret命令,跳出当前的内存区域,回到上一级函数的内存区域中,并将EAX的值加3,直到跳出main函数,至此程序结束。

      从上面的分析中,我觉得可以归纳出以下几点:

      1.计算机的运行流程确是遵循冯诺依曼框架,CPU将内存中的代码和数据读取到自己的寄存器中,再根据一条条命令调用寄存器进行进一步的操作。

      2.在进入每一个程序之前,CPU都会将上一级的EIP和EBP压栈,相当于为新的函数重新开辟了一段新的内存空间,直到退出函数的时候才将它们出栈。与此同时,将函数的返回值保存在EAX中。

      3.CPU的各个寄存器都有不同的分工,如EIP指向要执行的代码,EAX存储返回值等。它们贯穿于整个程序执行流程,自己写程序时一般不要轻易改动。

      

  • 相关阅读:
    git
    redis
    Hexo-butterfly-magicv3.0.1(持续更新中....)
    转发好文章1
    0x07 Nagios Notifications
    0x06 nagios监控状态
    0x05 Nagios Host Check
    0x03 Nagios Plugins介绍
    0x02 Nagios CGI的认证和授权
    0x01 Nagios配置文件
  • 原文地址:https://www.cnblogs.com/wickedpriest/p/4315189.html
Copyright © 2020-2023  润新知