美文网首页
汇编入门

汇编入门

作者: 某某香肠 | 来源:发表于2018-12-24 00:04 被阅读0次

    CPU在运行的时候实际上是读取指令并一条一条的执行,而这些指令是二进制的,也就是机器码,但是由于二进制的语言对人类的可读性不好,因此便出现了汇编语言,一般而言,汇编语言可以看作是机器码的文本格式,它们间可以相互转换,还原成机器码后便可以被CPU执行。其特点有:

    • 可直接访问、控制各种硬件设备。比如存储器、CPU等,能最大限度地发挥硬件的功能
    • 能够不受编译器的限制,对生成的二进制代码进行完全的控制
    • 目标代码简短,占用内存少,执行速度快
    • 汇编指令是机器指令的助记符,同机器指令一一对应。每种CPU都有自己的机器指令集/汇编指令集,所以* 汇编语言不具备可移植性
    • 汇编语言知识点过多,开发者需要对CPU等硬件结构有所了解,不宜于编写、调试、维护
    • 不区分大小写,比如mov和MOV是一样的

    对于一个使用高级语言的程序员来说,汇编语言不需要自己编写,但是至少需要看得懂,知道其中的原理,才能更好的排查问题。

    总的来说,汇编代码是被CPU一条一条地取出执行的,CPU会通过这些指令来对寄存器和内存等进行操作。因此学好汇编,需要对寄存器和内存有一定的了解

    寄存器

    由于CPU的运算速度通常都比内存读写速度要快,而CPU要运算的时候都要从内存中读取数据。为了节省时间,一般CPU都自带缓存,除此之外,还自带了寄存器,用来存储频繁使用的数据,此外CPU还会使用寄存器与内存交换数据。

    不同CPU里面的寄存器名字和数量都不一致,寄存器的用法基本都是想通的,但是大致上可以分成以下种:

    通用寄存器

    主要是AX,BX,CX,DX等等

    指令寄存器

    也就是IP,用于记录现在程序执行到哪

    指针寄存器

    主要是栈指针寄存器SP和栈基指针寄存器BP,每当有数据入栈/出栈的时候,都会导致SP改变

    段寄存器

    标志段的开始,主要有CS,DS,SS,ES等等

    当然还有许多别的种类的寄存器没有提及,比如索引寄存器(SI,DI),而且,上述的通用寄存器部分又有各自的用途,比如AX用于累加和中断,CX用于计数、循环等等。实际上,如果仅是看代码的话,大部分寄存器在通常情况下都可以看作通用寄存器。因为它们存放的数值被汇编代码的指令控制的。我们只需要知道的特殊的寄存器(比如IP,SP)即可。

    内存分段

    在程序运行的时候,系统会给程序分配一定的内存空间,这些内存空间会分成好几段:

    代码段

    存放汇编指令,其段寄存器为CS,此外还有指令寄存器IP,CPU就是从这个段读取指令

    数据段

    存放全局变量,可以根据这些变量有没有被初始化进一步划分,其段寄存器为DS

    堆栈段

    用来存放程序运行期间产生的变量,又分成堆和栈,其中栈用于存放函数中的局部变量,而堆用来存放动态分配的变量,堆栈段的寄存器为SS,另外有个SP的寄存器永远指向栈顶。堆和栈的具体区别如下图。


    image
    扩展段

    保存程序其它相关的信息,其段寄存器为ES

    常用汇编指令

    每一个处理器汇编指令都不太一样,但是其基本功能都是相通的,这里仅以8086处理器的指令集为例:

    mov

    传送指令,其用法为mov a,b,指的是将b的值赋值给a

    add

    加法,其用法为add a,b,将b的值加上a的值赋值给a,即a = a + b

    sub

    减法,其用法为sub a, b,与上面类似,相当于a = a - b

    cmp

    比较,其用法为cmp a, b,比较a和b的大小,其比较的结果存储在标志寄存器中。

    jmp

    无条件转移指令,通过修改IP和CS寄存器,使程序跳到目标地址运行

    jcc

    条件转移指令,jcc包含一系列的指令,通过判断标志寄存器的状态决定是否跳转

    call

    调用函数,程序会调到函数入口执行

    ret

    函数返回

    高级语言程序结构对应的汇编语言

    下面看一下程序的顺序结构、选择结构、循环结构下的汇编语言是怎么样的,本文所使用的语言为C++,处理器为x86_64,其指令集跟上面的不太一样,而且,其用法跟8086是相反的。

    顺序结构

    在main函数里面写上如下代码:

    int a = 5;
    int b = 1;
    a++;
    b += a;
    

    其对应的汇编语言是这样的

    ;a = 5
    0x100000f94 <+20>: movl   $0x5, -0x14(%rbp) ;将5赋值给偏移量为-0x14的内存区域,也就是说-0x14代表a
    ;b = 1
    0x100000f9b <+27>: movl   $0x1, -0x18(%rbp) ;将1赋值给偏移量为-0x18的内存区域,也就是说-0x18代表b
    ;a++
    0x100000fa2 <+34>: movl   -0x14(%rbp), %edi ;将a赋值给edi寄存器
    0x100000fa5 <+37>: addl   $0x1, %edi ; edi寄存器加1
    0x100000fa8 <+40>: movl   %edi, -0x14(%rbp) ;将edi寄存器赋值给a,这个时候完成了a++的操作
    ;b += a
    0x100000fab <+43>: movl   -0x14(%rbp), %edi ;将a赋值给edi寄存器
    0x100000fae <+46>: addl   -0x18(%rbp), %edi ;edi加上b的值
    0x100000fb1 <+49>: movl   %edi, -0x18(%rbp) ;将edi寄存器赋值给a,这个时候完成了b += a的操作
    
    

    选择结构

    同样,在main函数中写一个简单的选择结构

    int a = 5;
    if (a>3) {
        a++;
    }else {
        a--;
    }
    

    对应的汇编代码如下:

    0x100000f82 <+18>: movl   $0x5, -0x14(%rbp)  ;a = 5
    0x100000f89 <+25>: cmpl   $0x3, -0x14(%rbp)  ;a 和 3比较
    0x100000f8d <+29>: jle    0x100000fa1        ;如果小于等于,就跳到0x100000fa1执行
    ;下面是a++的实现
    0x100000f93 <+35>: movl   -0x14(%rbp), %eax
    0x100000f96 <+38>: addl   $0x1, %eax
    0x100000f99 <+41>: movl   %eax, -0x14(%rbp)
    0x100000f9c <+44>: jmp    0x100000faa        ; 实现后程序跳转到0x100000faa执行
    ;下面是a--
    0x100000fa1 <+49>: movl   -0x14(%rbp), %eax  
    0x100000fa4 <+52>: addl   $-0x1, %eax
    0x100000fa7 <+55>: movl   %eax, -0x14(%rbp)
    0x100000faa .....
    

    显然,汇编语言是使用cmp、jmp和jcc指令实现选择结构的。

    循环机构

    在main函数中写一个简单的循环:

    int i = 0;
    while(1){
        i++;
        if(i>5){
            break;
        }
    }
    

    这里使用了while做循环,实际上跟使用for循环的汇编代码是差不多的。

    0x100000f82 <+18>: movl   $0x0, -0x14(%rbp) ;i = 0
    0x100000f89 <+25>: movl   -0x14(%rbp), %eax ;这里开始循环
    0x100000f8c <+28>: addl   $0x1, %eax  
    0x100000f8f <+31>: movl   %eax, -0x14(%rbp)
    0x100000f92 <+34>: cmpl   $0x5, -0x14(%rbp)
    0x100000f96 <+38>: jle    0x100000fa1               
    0x100000f9c <+44>: jmp    0x100000fa6  ;如果i>5,则跳出循环              
    0x100000fa1 <+49>: jmp    0x100000f89  ;跳回0x100000f89,继续循环
    0x100000fa6 ....
    

    可以看出,循环结构也是通过cmp、jmp和jcc指令实现的。

    参考文献

    iOS之底层汇编(一)

    程序内存空间(代码段、数据段、堆栈段)

    8086汇编指令全集

    相关文章

      网友评论

          本文标题:汇编入门

          本文链接:https://www.haomeiwen.com/subject/lkaokqtx.html