走向计算机系统基础 汇编指令

1.mov指令
走向计算机系统基础 汇编指令_第1张图片
1.1 mov指令分三类,

  1. 源数据和目的数据宽度相同
    走向计算机系统基础 汇编指令_第2张图片
    2) 零扩展
    走向计算机系统基础 汇编指令_第3张图片
    3)符号扩展
    走向计算机系统基础 汇编指令_第4张图片
    总结一下:
    走向计算机系统基础 汇编指令_第5张图片
    可以看出mov不会把位数多的数据传送到位数少的数据。
    1.2 举几个实例:
    1)在这里插入图片描述
    2)在这里插入图片描述

在这里插入图片描述
因为这里的ax寄存器就是两个地址单元,而mov指令就是等宽传送的。
4)
走向计算机系统基础 汇编指令_第6张图片
1.3C语言可以调用asm命令嵌入汇编指令
举个例子:

#include                                                                                                                                
int main()
{
    short x = 0x8543;
    short y = 1;
    short z = 2;
    int p = 0x12345678;
    int q = 3;
    asm(
        "mov    -0x8(%rbp),%eax;"
        "mov    %ax,-0xe(%rbp);"
    );  
    printf("x=%x,y=%d,z=%d\n",x, y, z); 
    printf("p=%x,q=%d\n", p, q); 
    return 0;
}

运行后结果为:

x=5678,y=1,z=2
p=12345678,q=3

反汇编为:

#include 
int main()
{
  400526:   55                      push   %rbp
  400527:   48 89 e5                mov    %rsp,%rbp
  40052a:   48 83 ec 10             sub    $0x10,%rsp
    short x = 0x8543;
  40052e:   66 c7 45 f2 43 85       movw   $0x8543,-0xe(%rbp)
    short y = 1;
  400534:   66 c7 45 f4 01 00       movw   $0x1,-0xc(%rbp)
    short z = 2;
  40053a:   66 c7 45 f6 02 00       movw   $0x2,-0xa(%rbp)
    int p = 0x12345678;
  400540:   c7 45 f8 78 56 34 12    movl   $0x12345678,-0x8(%rbp)
    int q = 3;
  400547:   c7 45 fc 03 00 00 00    movl   $0x3,-0x4(%rbp)
    asm(
        "mov    -0x8(%rbp),%eax;"
        "mov    %ax,-0xe(%rbp);"
    );  
  40054e:   8b 45 f8                mov    -0x8(%rbp),%eax
  400551:   66 89 45 f2             mov    %ax,-0xe(%rbp)
    printf("x=%x,y=%d,z=%d\n",x, y, z); 
  400555:   0f bf 4d f6             movswl -0xa(%rbp),%ecx
  400559:   0f bf 55 f4             movswl -0xc(%rbp),%edx
  40055d:   0f bf 45 f2             movswl -0xe(%rbp),%eax
  400561:   89 c6                   mov    %eax,%esi
  400563:   bf 14 06 40 00          mov    $0x400614,%edi
  400568:   b8 00 00 00 00          mov    $0x0,%eax
  40056d:   e8 8e fe ff ff          callq  400400 
    printf("p=%x,q=%d\n", p, q); 
  400572:   8b 55 fc                mov    -0x4(%rbp),%edx
  400575:   8b 45 f8                mov    -0x8(%rbp),%eax
  400578:   89 c6                   mov    %eax,%esi
  40057a:   bf 24 06 40 00          mov    $0x400624,%edi
  40057f:   b8 00 00 00 00          mov    $0x0,%eax
  400584:   e8 77 fe ff ff          callq  400400 
    return 0;
  400589:   b8 00 00 00 00          mov    $0x0,%eax
}

这两个汇编就是把p(0x12345678)复制给x,可以看出数据截断x为0x5678。

2mov和lea的区别
lea是把地址存进去
走向计算机系统基础 汇编指令_第7张图片
lea相当于mov的一个特例。
走向计算机系统基础 汇编指令_第8张图片
lea针对地址,mov指令针对数据。
走向计算机系统基础 汇编指令_第9张图片
3. add,sub,cmp
直接看例子:

#include 
int main()
{
    int a = 10;
    int b = 30;
    int x = a + b;
    int y = a - b;
    if (x < y) {
        printf("hello world");    
    }
    return 0;
}

反汇编之后看对应的加法减法和比较。

#include 
int main()
{
  400526:   55                      push   %rbp
  400527:   48 89 e5                mov    %rsp,%rbp
  40052a:   48 83 ec 10             sub    $0x10,%rsp
    int a = 10; 
  40052e:   c7 45 f0 0a 00 00 00    movl   $0xa,-0x10(%rbp)
    int b = 30; 
  400535:   c7 45 f4 1e 00 00 00    movl   $0x1e,-0xc(%rbp)
    int x = a + b;
  40053c:   8b 55 f0                mov    -0x10(%rbp),%edx
  40053f:   8b 45 f4                mov    -0xc(%rbp),%eax
  400542:   01 d0                   add    %edx,%eax
  400544:   89 45 f8                mov    %eax,-0x8(%rbp)
    int y = a - b;
  400547:   8b 45 f0                mov    -0x10(%rbp),%eax
  40054a:   2b 45 f4                sub    -0xc(%rbp),%eax
  40054d:   89 45 fc                mov    %eax,-0x4(%rbp)
    if (x < y) {
  400550:   8b 45 f8                mov    -0x8(%rbp),%eax
  400553:   3b 45 fc                cmp    -0x4(%rbp),%eax
  400556:   7d 0f                   jge    400567 
        printf("hello world");        
  400558:   bf f4 05 40 00          mov    $0x4005f4,%edi
  40055d:   b8 00 00 00 00          mov    $0x0,%eax
  400562:   e8 99 fe ff ff          callq  400400 
    }   
    return 0;
  400567:   b8 00 00 00 00          mov    $0x0,%eax
}

4.乘法运算
乘法运算,可能使用加法,移位以及乘法指令去实现,同时乘法有可能存在溢出问题。
看个例子

#include 
int main()
{
    int x = 3;
    int y = 4;
    int z1, z2, z3, z4;
    unsigned ux = 3,uy = 4,uz;
    z1 = x * y;
    uz = ux* uy;
    z2 = x*3;
    z3=x*1024;
    z4=x*x+4*x+8;
    printf("z1=%d z2=%d z3=%d z4=%d",z1,z2,z3,z4);
    return 0;
}

反汇编结果为:

int main()
{
  400526:   55                      push   %rbp
  400527:   48 89 e5                mov    %rsp,%rbp
  40052a:   48 83 ec 30             sub    $0x30,%rsp
    int x = 3;
  40052e:   c7 45 dc 03 00 00 00    movl   $0x3,-0x24(%rbp)
    int y = 4;
  400535:   c7 45 e0 04 00 00 00    movl   $0x4,-0x20(%rbp)
    int z1, z2, z3, z4;
    unsigned ux = 3,uy = 4,uz;
  40053c:   c7 45 e4 03 00 00 00    movl   $0x3,-0x1c(%rbp)
  400543:   c7 45 e8 04 00 00 00    movl   $0x4,-0x18(%rbp)
    z1 = x * y;
  40054a:   8b 45 dc                mov    -0x24(%rbp),%eax
  40054d:   0f af 45 e0             imul   -0x20(%rbp),%eax
  400551:   89 45 ec                mov    %eax,-0x14(%rbp)
    uz = ux* uy;
  400554:   8b 45 e4                mov    -0x1c(%rbp),%eax
  400557:   0f af 45 e8             imul   -0x18(%rbp),%eax
  40055b:   89 45 f0                mov    %eax,-0x10(%rbp)
    z2 = x*3;
  40055e:   8b 55 dc                mov    -0x24(%rbp),%edx
  400561:   89 d0                   mov    %edx,%eax
  400563:   01 c0                   add    %eax,%eax
  400565:   01 d0                   add    %edx,%eax
  400567:   89 45 f4                mov    %eax,-0xc(%rbp)
    z3=x*1024;
  40056a:   8b 45 dc                mov    -0x24(%rbp),%eax
  40056d:   c1 e0 0a                shl    $0xa,%eax
  400570:   89 45 f8                mov    %eax,-0x8(%rbp)
    z4=x*x+4*x+8;
  400573:   8b 45 dc                mov    -0x24(%rbp),%eax
  400576:   83 c0 04                add    $0x4,%eax                                                                                                               
  400579:   0f af 45 dc             imul   -0x24(%rbp),%eax
  40057d:   83 c0 08                add    $0x8,%eax
  400580:   89 45 fc                mov    %eax,-0x4(%rbp)
    printf("z1=%d z2=%d z3=%d z4=%d",z1,z2,z3,z4);
  400583:   8b 75 fc                mov    -0x4(%rbp),%esi
  400586:   8b 4d f8                mov    -0x8(%rbp),%ecx
  400589:   8b 55 f4                mov    -0xc(%rbp),%edx
  40058c:   8b 45 ec                mov    -0x14(%rbp),%eax
  40058f:   41 89 f0                mov    %esi,%r8d
  400592:   89 c6                   mov    %eax,%esi
  400594:   bf 34 06 40 00          mov    $0x400634,%edi
  400599:   b8 00 00 00 00          mov    $0x0,%eax
  40059e:   e8 5d fe ff ff          callq  400400 
    return 0;
  4005a3:   b8 00 00 00 00          mov    $0x0,%eax
}

可以看出:
走向计算机系统基础 汇编指令_第10张图片
注意:imul用于带符号乘,mul用于无符号乘,不过上面的无符号乘法用的也是imul,因为他只取低32位。

5.控制转移指令
rip 程序指令寄存器,指向下一条待执行指令,如果要跳转的话,就改rip.
转移有两种方法,一种是相对跳转,另一种是绝对跳转。
走向计算机系统基础 汇编指令_第11张图片
走向计算机系统基础 汇编指令_第12张图片

举个例子:

#include 

int sum(int a[], int n)
{
    int sum = 0;
    for (int i = 0;i < n;i++ ) {
        sum += a[i];
    }
    return sum;
}

int main()
{
    int a[4] = {1,2,3,4};
    int n =3;
    int x;
    x =sum (a, n);
    printf("sum =%d", x);
    return 0;
}

反汇编后:

int sum(int a[], int n)
{
  400596:   55                      push   %rbp
  400597:   48 89 e5                mov    %rsp,%rbp
  40059a:   48 89 7d e8             mov    %rdi,-0x18(%rbp)
  40059e:   89 75 e4                mov    %esi,-0x1c(%rbp)
    int sum = 0;
  4005a1:   c7 45 f8 00 00 00 00    movl   $0x0,-0x8(%rbp)
    for (int i = 0;i < n;i++ ) {
  4005a8:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
  4005af:   8b 45 fc                mov    -0x4(%rbp),%eax
  4005b2:   3b 45 e4                cmp    -0x1c(%rbp),%eax
  4005b5:   7d 1f                   jge    4005d6 <_Z3sumPii+0x40>
        sum += a[i];
  4005b7:   8b 45 fc                mov    -0x4(%rbp),%eax
  4005ba:   48 98                   cltq   
  4005bc:   48 8d 14 85 00 00 00    lea    0x0(,%rax,4),%rdx
  4005c3:   00 
  4005c4:   48 8b 45 e8             mov    -0x18(%rbp),%rax
  4005c8:   48 01 d0                add    %rdx,%rax
  4005cb:   8b 00                   mov    (%rax),%eax
  4005cd:   01 45 f8                add    %eax,-0x8(%rbp)
#include 

int sum(int a[], int n)
{
    int sum = 0;
    for (int i = 0;i < n;i++ ) {
  4005d0:   83 45 fc 01             addl   $0x1,-0x4(%rbp)
  4005d4:   eb d9                   jmp    4005af <_Z3sumPii+0x19>
        sum += a[i];
    }
    return sum;
  4005d6:   8b 45 f8                mov    -0x8(%rbp),%eax
}
  4005d9:   5d                      pop    %rbp
  4005da:   c3                      retq   

00000000004005db 
: int main() { 4005db: 55 push %rbp 4005dc: 48 89 e5 mov %rsp,%rbp 4005df: 48 83 ec 30 sub $0x30,%rsp 4005e3: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax 4005ea: 00 00 4005ec: 48 89 45 f8 mov %rax,-0x8(%rbp) 4005f0: 31 c0 xor %eax,%eax int a[4] = {1,2,3,4}; 4005f2: c7 45 e0 01 00 00 00 movl $0x1,-0x20(%rbp) 4005f9: c7 45 e4 02 00 00 00 movl $0x2,-0x1c(%rbp) 400600: c7 45 e8 03 00 00 00 movl $0x3,-0x18(%rbp) 400607: c7 45 ec 04 00 00 00 movl $0x4,-0x14(%rbp) int n =3; 40060e: c7 45 d8 03 00 00 00 movl $0x3,-0x28(%rbp) int x; x =sum (a, n); 400615: 8b 55 d8 mov -0x28(%rbp),%edx 400618: 48 8d 45 e0 lea -0x20(%rbp),%rax 40061c: 89 d6 mov %edx,%esi 40061e: 48 89 c7 mov %rax,%rdi 400621: e8 70 ff ff ff callq 400596 <_Z3sumPii> 400626: 89 45 dc mov %eax,-0x24(%rbp) printf("sum =%d", x); 400629: 8b 45 dc mov -0x24(%rbp),%eax 40062c: 89 c6 mov %eax,%esi 40062e: bf e4 06 40 00 mov $0x4006e4,%edi 400633: b8 00 00 00 00 mov $0x0,%eax 400638: e8 33 fe ff ff callq 400470 return 0; 40063d: b8 00 00 00 00 mov $0x0,%eax }

上面的jge就是如果大于等于则跳转,jmp是无条件跳转。
6.过程调用
函数调用过程,看看栈是怎么变化的。
1)保存调用者的ebp
走向计算机系统基础 汇编指令_第13张图片
2)建立自己的栈空间
走向计算机系统基础 汇编指令_第14张图片
3)为自己的非静态局部变量分配空间
走向计算机系统基础 汇编指令_第15张图片
4)函数处理完之后回收栈空间
走向计算机系统基础 汇编指令_第16张图片
5) 返回调用者
走向计算机系统基础 汇编指令_第17张图片

其中最重要的栈空间就是从rbp到rsp中间的区域,比如下图就是一个栈帧。
走向计算机系统基础 汇编指令_第18张图片
走向计算机系统基础 汇编指令_第19张图片
具体的细节使用的时候搜索调用约定吧,比如子函数哪几个参数放在什么寄存器,调用者保存还是被调用者保存寄存器的值,以及返回值用什么寄存器都可以在调用约定中找到。举个例子:

int sum(int a1,int a2,int a3,int a4,int a5,int a6,int a7)
{
    int sum = 0;
    sum += a1;
    sum += a2;
    sum += a3;
    sum += a4;
    sum += a5;
    sum += a6;
    sum += a7;
    return sum;
}

反汇编之后:

0000000000400526 <_Z3sumiiiiiii>:
#include 

int sum(int a1,int a2,int a3,int a4,int a5,int a6,int a7) 
{
  400526:   55                      push   %rbp
  400527:   48 89 e5                mov    %rsp,%rbp
  40052a:   89 7d ec                mov    %edi,-0x14(%rbp)
  40052d:   89 75 e8                mov    %esi,-0x18(%rbp)
  400530:   89 55 e4                mov    %edx,-0x1c(%rbp)
  400533:   89 4d e0                mov    %ecx,-0x20(%rbp)
  400536:   44 89 45 dc             mov    %r8d,-0x24(%rbp)
  40053a:   44 89 4d d8             mov    %r9d,-0x28(%rbp)
    int sum = 0;
  40053e:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
    sum += a1; 
  400545:   8b 45 ec                mov    -0x14(%rbp),%eax
  400548:   01 45 fc                add    %eax,-0x4(%rbp)
    sum += a2; 
  40054b:   8b 45 e8                mov    -0x18(%rbp),%eax
  40054e:   01 45 fc                add    %eax,-0x4(%rbp)
    sum += a3; 
  400551:   8b 45 e4                mov    -0x1c(%rbp),%eax
  400554:   01 45 fc                add    %eax,-0x4(%rbp)
    sum += a4; 
  400557:   8b 45 e0                mov    -0x20(%rbp),%eax
  40055a:   01 45 fc                add    %eax,-0x4(%rbp)
    sum += a5; 
  40055d:   8b 45 dc                mov    -0x24(%rbp),%eax
  400560:   01 45 fc                add    %eax,-0x4(%rbp)
    sum += a6; 
  400563:   8b 45 d8                mov    -0x28(%rbp),%eax
  400566:   01 45 fc                add    %eax,-0x4(%rbp)
    sum += a7; 
  400569:   8b 45 10                mov    0x10(%rbp),%eax
  40056c:   01 45 fc                add    %eax,-0x4(%rbp)
    return sum;
  40056f:   8b 45 fc                mov    -0x4(%rbp),%eax
}
  400572:   5d                      pop    %rbp
  400573:   c3                      retq 

你可能感兴趣的:(计算机系统第四篇,linux,数据结构)