• 汇编语言学习笔记及总结


    汇编语言简述

    鱼C 汇编语言

    鱼C win32汇编语言

    汇编入门

    内联汇编

    G++中的内联汇编分为基本形式的内联汇编与扩展形式的内联汇编;毫无疑问,扩展形式的内联汇编更加复杂,也更加强大

    __asm__与asm

    两者是一样的,只不过ANSI C标准将asm作为关键字用于其他用途;所以为了与ANSIC兼容,还是使用__asm__;

    __volatile__于volatile

    告诉编译器,此处禁止优化,与__asm__一同使用,表示不优化内联汇编段

    基本形式的内联汇编

    可以使用宏定义,来更加方便得使用内联汇编,如

    #define _mBeginASM  __asm__ __volatile__ (
    #define _mEndASM 	);
    
    • 1
    • 2

    基本形式的内联汇编语法如:

    _mBeginASM
    
    "汇编代码"
    
    _mEndASM
    
    • 1
    • 2
    • 3
    • 4
    • 5

    G++会将"汇编代码"部分逐字插入到为程序生成的汇编代码中,所以应该在每一条指令后面手动添加’\n\t’;

    扩展形式的内联汇编

    语法形式:

    _mBeginASM
    
    "汇编代码模板"    
    
    :输出参数列表
    :输入参数列表
    :改动的寄存器列表
    
    _mEndASM
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    总的作用机理就是:

    1. g++ 首先根据’输入参数列表’部分将输入参数复制到指定的寄存器(使用mov,fld…等指令);
    2. 替换一下汇编代码模板中的占位符,然后将该部分逐字插入到为程序生成的汇编代码中
    3. 根据’输出参数列表’部分将指定寄存器中的值mov到C++变量中

    感觉就像一个函数调用,'汇编代码模板’就是函数体,'输入参数列表’部分指定了函数的参数,'输出参数列表’部分指定了函数的返回值

    计算机系统结构

    cpu内部:               
    1. PC Program Counter
       指令指针寄存器
       指向下一条指令的地址
       EIP(X86-32)或者
       RIP(X86-64)
    2. 寄存器与寄存器堆
      Registers
       在处理器CPU内部以名字来访问的快速存储单元
    3. 条件状态码
       Condition Codes
        用于存储最近执行指令的结果状态信息
        用于条件指令的判断执行
    内存单元Memory:
      以字节编码的连续存储空间
        存储程序代码、数据、运行栈stack 以及操作系统数据
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    汇编语言数据格式

    c 语言          数据类型   汇编代码后缀  大小(字节为单位)
    char               字节         b  byte       1
    short        字           w  word       2
    int         双字         l              4
    long int      双字         l              4
    long long int   无           无             4
    char*        双字     l              4
    float       单精度       s               4
    double       双精度       l               8
    long double    扩展精度      t              10/12
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    第一条汇编指令实例

    c 语言代码:
     int t = x+y // 两个整数(32位)
    汇编代码:
     addl 8(%ebp) %eax//l表示双字 8是位偏移量
      操作数:
      x: 寄存器 Register  eax
      y: 内存    Memory   M[ebp+8]   ebp是栈基址寄存器
      t: 寄存器  Register eax
      结果t保存在寄存器eax中
    类似于表达:
     x += y
    或者:
      int eax;
      int* ebp;
      eax += ebp[2];//这里按字节
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    数据传送指令

    movel 源地 目的地
    将一个双字从源地移动到目的地
    允许的操作数类型有:
       立即数Imm:常整数
          如: $0x400, $-533
          可以用1,24个字节来表示
       寄存器 Reg:
          8个通用寄存器之一
          %eax
          %ebx
          %ecx
          %edx
          %esi
          %edi
          %esp  栈顶
          %ebp  栈底
       存储器Mem:四个连续的字节
                         汇编       类似C语言
    立即数--->寄存器  movl $0x41, %eax     temp = 0x41;
    立即数--->内存     movl $-43, (%eax)    *p   = -43;
    寄存器--->寄存器   movl %eax, %edx      temp2 = temp;
    寄存器--->内存     movl %eax, (%edx)    *p    = temp;
    内存 --->寄存器   movl (%eax), %edx    temp  = *p;
    不允许内存到内存  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    简单得寻址模式

    1. 间接寻址 (R)     Mem[Reg[R]]
    寄存器R指定得内存地址
    movl (%ecx), %eax
    2. 基址+便宜量寻址 D(R)  Mem[Reg[R] + D]
    寄存器R指定内存的起始地址
    常数D给出偏移地址
    movl 8(%ebp), %ecx
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    寻址模式使用示例

    交换两个数
    C语言
    void swap(int* xp, int* yp){
    int t0 = *xp;
    int t1 = *yp;
    *xp = t1;
    *yp = t0;
    }
    
    汇编语言分析
    寄存器 变量:
    %ecx   yp
    %edx   xp
    %eax   t1
    %ebx   t0
    对应汇编:
    movl 12(%ebp),%ecx  # ecx = yp  是地址  放到ecx寄存器中
    movl 8(%ebp), %edx  # edx = xp 是地址  放到edx寄存器中
    movl (%ecx), %eax   # eax = *yp t1 值   取寄存器中地址指向的内存地址中的内容放入 寄存器eax中
    movl (%edx), %ebx   # ebx = *xp t0 值  取寄存器中地址指向的内存地址中的内容放入 寄存器ebx中
    movl %eax, (%edx)   # *xp = eax          交换内容放入原来内存指向得地址中
    movl %ebx, (%ecx)   # *yp = ebx
    
    ebp 是函数栈 基地址
    ebp+8 的位置 存储 指针xp 指向内存的一个地址
    ebp+12 的位置 存储 指针yp 指向内存的一个地址
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    变址寻址

    常见形式:
       D(Rb,Ri,S) Mem[Reg[Rb] + S*Reg[Ri] + D]
     D:  常量(地址偏移量)
     Rb: 基址寄存器:8个通用寄存器之一
     Ri: 索引寄存器: %esp不作为索引寄存器
             一般%ebp也不做这个用途
     S: 比例因子, 1,2,4,8
     其他变形:
       D(Rb,Ri) Mem[Reg[Rb] + Reg[Ri] + D]
       (Rb,Ri) Mem[ Reg[Rb] + Reg[Ri] ]
       (Rb,Ri) Mem[Reg[Rb] + S*Reg[Ri]]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    地址计算指令 leal lea +l

    leal src, dest
      src 是地址计算表达式子
      计算出来得地址赋给 dest
    使用实例:
      地址计算,无需访问内存 auto *p = &x[i];
      进行x+k*y这一类型得整数计算,k = 1,2,4,8
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    整数计算指令:

    addl src,dest   #  dest = dest + src #加法
    subl src,dest   #  dest = dest - src #减法
    imull src,dest  #  dest = dest * src #乘法
    sall src,dest  #  dest = dest << src #左移位 等价于shll
    sarl src,dest  #  dest = dest >> src #算术右移位 补 被移动数的最高位 
    shrl src,dest  #  dest = dest >> src #逻辑右移位 左边单纯补 0
    xorl src,dest  #  dest = dest ^ src  #按位异或
    andl src,dest  #  dest = dest & src  #按位与
    orl  src,dest  #  dest = dest | src  #按位或
    
    incl dest  #  dest = dest + 1  #++ 自增1
    decl dest  #  dest = dest - 1  #-- 自减1
    negl dest  #  dest = - dest # 取非
    notl dest  #  dest = ~ dest #  取反
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    将leal指令用于计算

    c语言:
       int temp(int x, int y){
          int t1 = x+y;
          int t2 = z+t1;
          int t3 = x+4;
          int t4 = y*48;
          int t5 = t3+t4;
          int ret = t2*t5
          return ret;
       }
    汇编代码:
       movl 8(%ebp), %eax   # eax = x
       movl 12(%ebp), %edx  # edx = y
       leal (%eax, %edx), %ecx # t1  = ecx = x+y 
       addl 16(%ebp), %ecx     # t2 = ecx = z+t1
       leal (%edx,%edx,2), %edx # edx = 3*y
       sall $4, %edx            # t4 = edx = 2^4 * 3 * y =16*3*y = 48*y
       leal 4(%eax,%edx), %eax  # t5 = eax =4+x+t4
       imul %ecx, %eax          # ret = eax = t2*t5
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    逻辑运算示例

    C语言:
       int logical_(int x, int y){
          int t1 = x^y;
          int t2 = t1>>17;
          int mask = (1<<13) - 7;
          int ret = t2 & mask;
          return ret;
       }
    汇编语言:
    movl 8(%ebp), %eax  # eax = x
    xorl 12(%ebp), %eax # t1 = eax = x^y  
    sarl $17, %eax      # t2 = eax = t1 >> 12
    andl $8185, %eax    # ret = eax = t2 & 8185  2^13-7 = 8185 这里编译器会算出来
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    x86-32 与 x86-64主要类型数据宽度得区别

                 x86-32  x86-64
      long int     4       8
      char*        4       8   内存地址编号长度
    
    • 1
    • 2
    • 3

    使用 条件码(标志寄存器) 来进行控制

      addl src,dest    t =a+b
      CF Carry Flag 进位标志  可用于无符号整数运算的溢出 
      SF Sign Flag  符号位标志 结果<0 的话,被设置为1
      ZF Zero Flag 0结果标志  结果为0的话,被设置为1
      OF Overflow Flag 溢出标志 if a>0 & b>0 & t<0 被设置为1
    
    • 1
    • 2
    • 3
    • 4
    • 5

    比较指令 compare 被比较数 比较数 compare b,a a-b(但是不会改变a的值)

      compare b,a  a-b
      if a == b , ZF =1
      if a < b  , SF=1
      if (a>0 && b<0 && (a-b)<0)||(a<0 && b>0 && (a-b)>0) OF=1
    
    • 1
    • 2
    • 3
    • 4

    测试指令 test 做与操作

      testl s2,s1
      testq s2,s1
      计算 s1 & s2并设置相应的条件码,不改变目的操作数
      testl b,a   计算a&b
      if a&b ==0,  ZF =1
      if a&b < 0,  SF =1
      CF=0,OF=0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    setX指令 获取条件码状态

      sete  相等 结果为0 ZF =1时
      setne 不相等         ZF =0
    
      sets  结果<0  SF =1
      setns 结果>=0    SF=0
      有符号数:
      setg  大于      
      setge 大于等于
      setl  小于
      setle 小于等于
      无符号数
      setb  小与 below
      seta  大于  ablove
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    读取条件码实例

    c语言:
       int gt(int x, int y){
       return x>y;
       }
    汇编语言:
    movl 12(%ebp), %eax    # eax = y
    cmpl %eax, 8(%ebp)     # compare x:y  x-y
    setg %al               # al寄存器是eax的低八位得名字 al = x>y
    movzbl #al, %eax       # 8位扩展到16位 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    跳转指令 Jx

      jmp   无条件跳转
      je    相等
      jne   不相等
      js    结果<0
      jns   结果>=0
    
      有符号 
      jg      大于
      jge   大于等于
      jl    小于
      jle    小与等与
    
      无符号 
      ja   大于
      jb   小于
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    跳转指令 Jx 实例

    c语言:
     int abs_(int x, int y){
           int rest;
           if(x>y){
            rest = x - y;
           }
           else{
            rest = y-x;
           }
        return rest;
      }
    汇编语言:
       movl 8(%ebp), %edx  #  edx = x
       movl 12(%ebp), %eax #  eax = y
       cmpl %eax, %edx     # x - y
       jle  .L7            # x <=y 跳转到.L7
       subl %eax, %edx     # x>y  ,  计算rest = x-y
       movl %edx, %eax     # 结果放在 寄存器eax指向的地址
       .L8:
         leave
         ret
       .L7:
          subl %edx, %eax  # x <=y 跳转到.L7 计算 y-x 
          jmp L8
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    循环的汇编语言表示

    for循环:
       for(Init初始条件; 判断测试条件Test; 更新量Update)
           循环体Body;
    
    转换到 While-do结构:
       Init初始条件;
       while(判断测试条件Test){
          循环体Body;
          更新量Update;
       }
    
    再转换成go-to结构:
       Init初始条件;
       goto middle;  # 无条件跳转 jmp
    loop:
       循环体Body;
       更新量Update;
    middle:
       if(判断测试条件Test)
          goto loop;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    switch case的汇编语言格式

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    c语言版本:
    
    long switch_eg(long x, long y, long z){
       long w = 1;
       switch(x){
       case 1:
          w = y*z;
          break;
       case 2:
          w = y/z;
          break;
       case 3:
          w +=z;
          break;
       case 5:   
       case 6:
          w -= z;
          break; 
       defult:    // 这里 case 0 和 case 4 和其他情况
          w = 2;
         }
       return w;
    }
    
    汇编语言版本:
    
    pushl %ebp      # 保存 寄存器老的 ebp的值
    movl %esp, %ebp # 函数堆栈 栈顶指针 %esp 存放在%ebp
    pushl %ebx      # 保存 寄存器ebx的值
    
    movl 8(%ebp), %ebx  # 函数堆栈距离栈顶 偏移8位处存放x的值
    movl 12(%ebp), %eax # y
    movl 16(%ebp), %ecx # z
    cmpl $6, %ebx       # x - 6 
    jbe .L11            # X <= 6 才进入正常的 case
    
    .L8:                # default 部分
       movl $2, %eax    # w = 2
       jmp .L12         # break
    
    .L11:
       jmp *.L7(,%ebx,4) %访问.L7段表 对应的 case block
      
       .section     .rodata
    .L7:
       .long .L8   # case 0 情况 进入 default
       .long .L3   # case 1 情况 
       .long .L4   # case 2 情况 
       .long .L9   # case 3 情况 
       .long .L8   # case 4 情况 进入 default  
       .long .L6   # case 5 情况 case 6
       .long .L6   # case 6 情况 进入 default
       
    .L12:         # break
       popl %ebx  # 后进先出
       popl %ebp  # 先进后出
       ret        # 函数段返回
    
    .L3:                # case 1
       imull %ecx, %eax # w = w*z
       jmp .L12         # break
       
    .L4:                # case 2
       movl %eax, %edx  
       sarl $31, %edx
       idivl %ecx      # w =y/z
       addl %ecx, %eax # case 3 w += z  
       jmp .L12        # break
       
    .L9:               # case 3
       movl %1, %eax   # w = 1
       addl %ecx, %eax # w +=z
       jmp .L12        # break
    
    .L6:               # case 6  case 5
       movl $1, %eax   # w = 1
       subl %ecx, %eax # w -= Z
       jmp .L12        # break
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    x86-32的程序栈

      栈---水桶结构的一块内存区域---->只有一个出口(栈底%ebp (桶口)高地址(计算机地址逆向生长))
      先进后出后进先出 FILO
    
      寄存器 %esp 存储栈顶地址(下部)
      寄存器 %ebp 存储栈底地址(上部)
    
      注意 %esp  %ebp  始终指向当前正在运行的活动的函数过程栈
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    存储的内容

      局部变量
      返回地址
      临时空间
    
    • 1
    • 2
    • 3

    栈帧的分配与释放

      1. 进入过程 先分配栈帧空间
        set-up code
      2. 过程返回时 释放栈帧空间
        finish code
    
    • 1
    • 2
    • 3
    • 4

    pushl 压栈 把大象装进水桶的操作

      水面栈顶%esp会上什(步进单位为4个内存地址块)
    
    • 1
    pushl src  # 从src取得操作数
          # %esp = %esp-4 栈顶上升 水面上什
    
    • 1
    • 2

    popl 出栈操作 把大象从水桶中取出来

      水面栈顶%esp会下降
    
    • 1
    popl Dest # 读取栈顶数据(%esp) 放入 目标位置Dest
              # %esp = %esp+4 栈顶下降 水面下降
    
    • 1
    • 2

    过程调用 call label 过程返回指令 ret

    过程调用指令:
    call label #将返回地址(call指令的下一条指令地址(%eip)压入栈),跳转至label继续执行
    过程返回指令
    ret  #跳转至栈顶的返回地址
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    函数调用时 寄存器的使用惯例

      8个寄存器:
         两个特殊寄存器:
            调用者和被调用者可能都需要保存
            始终指向当前活动的堆栈上两端
            %ebp 栈底指针
            %esp 栈顶指针
         三个由调用者保存:
            %eax  常用于保存过程的返回值的
            %ecx
            %edx
         三个由被调用者保存:
            %ebx   
            %esi
            %edi
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    递归调用实例

      c语言版本:
      int rfact(int x){
         int rval;
         // 递归出口
         if(x<=1) return 1;
         // 递归调用
         rval = rfact(x-1);
         return rval * x;
      }
      汇编语言版本:
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    调用者栈情况          前 %ebp       <-  %ebp 栈底
                         前 %ebx
                         变量x 父过程调用的参数
                         返回地址 adr   <- %esp 栈顶
    #  进入过程 先分配栈帧空间
    被调用者(函数rfact)   老的 %ebp      第一步需要保存%ebp    pushl %ebp 指向 前%ebp   <- %esp 栈顶 栈顶自动调整
                                        第二步 movl %esp, %ebp                        <- %ebp 栈第指针也指向了栈顶 (栈基址)
                         老的 %ebx      第三步 pushl %ebx                             <- %esp 栈顶 栈顶自动调整 
                         (用来存储函数内的变量值,之前的值需要先保存)
                         
    主体代码:
    movl 8(%ebp), %ebx   # ebx = x
    cmpl $1, %ebx        # compare x : 1  , 计算 x - 1
    jle .L78             # x <= 1  直接跳转到递归出口 .L78
    leal -1(%ebx), %eax  # eax = x-1  被调用者这里需要用到 %eax 作为过程的返回值的
    pushl %eax           # push x-1  入栈保存 这里用作为调用者需要保存 %eax
    call rfact           # rval = rfact(x-1), 函数返回值保存在 %eax中
    imull %ebx, %eax     # rval * x
    jmp .L79             # 跳转到 done
    .L 78:
       movl $1, %eax     # return eax = 1
       
     # 返回代码  过程返回时 释放栈帧空间
    .L79:
       movl -4(%ebp), %ebx # 复原老的 %ebx 的值
       movl %ebp, %esp     # 栈顶指针 %esp  也指向%ebp指向的位置
       popl %ebp           # 恢复老的 %ebp 
       ret
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    x86-64的通用寄存器

      x86-32 有 8个32位的寄存器
            8个寄存器:
               两个特殊寄存器:
                  调用者和被调用者可能都需要保存
                  始终指向当前活动的堆栈上两端
                  %ebp 栈底指针
                  %esp 栈顶指针
               三个由调用者保存:
                  %eax  常用于保存过程的返回值的  累加器 计算操作数和存放结果数据
                  %ecx  计数寄存器
                  %edx  数据寄存器
               三个由被调用者保存:
                  %ebx  基础寄存器  指向DS数据段的数据指针
                  %esi  源索引
                  %edi  目的索引
      x86-64 有 16个64位的寄存器:
               与 x86-32兼容的8个64位的寄存器
                     %rax  低16位也叫 %eax     eax 低16位称为ax,ax的高8位称为ah,低8位称为al
                     %rbx            %ebx     ebx 低16位称为bx,bx的高8位称为bh,低8位称为bl
                     %rcx            %ecx     ecx 低16位称为cx,cx的高8位称为ch,低8位称为cl
                     %rdx            %edx     edx 低16位称为dx,dx的高8位称为dh,低8位称为dl
                     %rsi            %esi     esi 低16位称为si
                     %rdi            %edi     edi 低16位称为di
                     %rsp            %esp     esp 低16位称为sp
                     %rbp            %ebp     ebp 低16位称为bp
               新增加的8个64位的寄存器:
                     %r8   低16位也叫 %r8d
                     %r9             %r9d
                     %r10            %r10d
                     %r11            %r11d
                     %r12            %r12d
                     %r13            %r13d
                     %r14            %r14d
                     %r15            %r15d
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    x86-32 6个16位的段寄存器,定义内存中的段

            CS   代码段   存储指令和执行的地方
            DS, ES, FS, GS  数据段
            SS  堆栈段  当前程序存储堆栈的地方
    
    • 1
    • 2
    • 3

    数组的表示 访问 操作

    访问:
    c语言:
    typedef int array_i_5[5];// 定义一个长度为5的整数数组类型 array_i_5
    array_i_5 cmu = {1, 2, 5, 3, 4};// 定义一个数组cmu  函数五个整形变量
    cum[index];//可以获取相应索引位置处的 元素
    
    汇编语言:
    # %edx = cmu   数组首地址
    # %eax = index 索引
    movl (%edx, %eax, 4), %eax # cmu[index]  4为一个整形类型所占据的直接数量
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    数组循环遍历修改
    c语言:
    void temp(array_i_5 z){
    int i;
    for (i=0; i<5; i++)
       z[i]++;// 数组各元素自增1
    }
    汇编语言:
    movl $0, %eax  # 循环变量 %eax = i
    .L4:
       addl $1, (%edx, %eax, 4) # z[i]++
       addl $1, %eax            # i++
       cmpl $5, %eax            # compare i:5
       jne .L4                  # i < 5 循环
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    多维数组的表示

    int arr_i_i[row_index][col_index];
    一个int类型占据4个字节地址
    则 arr_i_i[y][x] 的实际地址为 arr_i_i + y*row_index*col_index + 4*x
    例如一个 arrii[4][5]的二维数组,每一行占有 4*5=20个字节
    那么 arrii[i][j] 的地址为  arrii + i*20 + 4*j
                              arrii + 4*(i+4*i) + 4*j 
    相关汇编代码:
    先计算列地址
    leal 0(,%ecx,4), %edx        #  4*j
    leal (%eax, %eax, 4), %eax   # 5 *i
    movl arrii(%edx, %eax, 4), %eax # arrii + 4*j + 4*(i+4*i) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    hello world程序

          //helloworld.c
          #include 
          #include 
          int main(){
             printf("Hello World\n");
             exit(0);
             return 0;//这里就不会编译了
          }
          // gcc 编译
          gcc -S -O2 helloworld.c
             -S   生成汇编代码(与系统类型相同)  helloworld.s
             -m32 64位系统生成32位汇编
             -On  -O2 2级编译优化
         // asm      
         movl $LC0 (%esp)  #设置过程调用参数 字符串首地址 放在栈顶对应的地址
         call _puts
         movl $0, (%esp)   #设置过程调用参数
         call _exit
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    标记符号

    .text  #代码段
    .p2align 4,,15 #对齐方式 按16 = 2^4字节对齐,填充,, 0, 最大填充15个字节
    .section .rdata "dr"  #只读数据段
    LC0:# 字符串的起始地址
          .ascii "Hello World\12\0"
    
    • 1
    • 2
    • 3
    • 4
    • 5

    linux 汇编命令

    编译:

      //gcc -S -O2 helloworld.c
      as -o my-object-file.o helloworld.s
      # -gstabs 产生带调试信息的object文件
      # 64位环境下添加命令行 --32 生成 32位目标文件
      链接成执行程序
      ld -o my-exe-file my-object-file.o
      # 64位环境下添加命令行 -m elf_i386 生成 32位目标文件
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    helloworld.s
    .data     #数据段
    msg:      # 字符 首地址
       .ascii "Hello World\n"
       len = .-msg #  字符长度 . 表示当前地址
    .text     # 代码段
    .globl _start  # 汇编程序入口地址,如同C语言的main函数
    _start:
       movl $len, %edx # 字符串长度 字节数量
       movl $msg, %ecx # 字符串存储位置的起始地址
       movl $1, %ebx   # 系统输出(write 系统调用1)
       movl $4, %eax   #
       int $0x80       # 中断 来指向系统调用
                       # eax存放的是系统调用的功能号
                       # ebx 为参数 为1时 是 write 显示器输出
                       # edx 字符串长度 字节数量
                       # ecx 字符串存储位置的起始地址
       
       movl $0, %ebx  # 系统调用(程序退出 0)
       movl $4, eax
       int $0x80      # 中断 来指向系统调用
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  • 相关阅读:
    心法利器[70] | 短文本理解的难点和解决方案
    SpringBoot 执行定时任务
    day14 书城项目第六阶段
    一个Python爬虫案例,带你掌握xpath数据解析方法!
    【C++】经典二叉树面试题
    十个开发人员面临的最常见的JavaScript问题总结
    【精简改造版】大型多人在线游戏BrowserQuest服务器Golang框架解析(2)——服务端架构
    paddle实现,多维时序数据增强 ,mixup(利用beta分布制作连续随机数)
    pytorch中Tensor(张量)
    简易版剪辑视频程序(python-VideoFileClip)
  • 原文地址:https://blog.csdn.net/RuanJian_GC/article/details/132866983