汇编语言


汇编:

x86指令集   CISC: RISC
    向下兼容
    变长指令
       1-15字节,多为2-3字节长度
    多种寻址方式

MIPS 32位
    Load/Store 能访问内存
    指令格式只有3类
    R(register)  从寄存器堆中读取两个源操作数,计算结果写回寄存器堆
    I(immediate) 使用一个16位的立即数作为源操作数
    J(jump), 使用一个26位立即烦乱作为跳转的目标地址

地址: 32位, 0000 --> 0004 ,差32位,4个字节
      64位, 0000 --> 0008
     
Byte Ordering

Big Endian: Sun, PowerPC, Internet
      低位字节(Least singificant byte,LSB)占据高地址
Little Endian: x86
      低对低,高对高
     0x123456
     0x100 0x0101 0x102
        56   34     12    
        
带符号数
   最大 TMax : 0111...1  2^w -1
        TMin : 100...0     -2^w         001
     按位取反加1(符号位不动)     1*(-2^w)+          101  -4+1   011 -3  
     
何时采用无符号数
    模运算
    按位运算
  sizeof  返回值是unsigned
 
101    

2^6 2^5 2^2

x+y = -(x+y)
-x-1

浮点数:
   
    float: 23位有效,
    
    局限性: 只能精确表示x/2^k这类形式的数据
    
    数字形式:
        (-1)^s M 2^E
        符号: s
        尾数:  M
        阶码: E
        
    编码:
      s   exp    frac
          E       M
    单数度: exp:  8bits   fract: 23
    双           11bits         52
      
    规格化的浮点数 (Normalized)
      exp != 0000...0   111...1
      E = Exp -Bias     Exp(exp域所表示的无符号数值)
          Bias偏置量
            单精度: 127
            双      1023
    
    Float F = 15213.0
    15213 = 11101101101101 = 1.1101101101101 *  2^13
      M    = 1.1101101101101
      frac =   1101101101101000000000   23位
      
      E    =  13
      Bias =  127
      Exp  =  140    10001100
      
      Hex:      4   6    6    D    B    4    0    0
      Binary: 0100 0110 0110 1101 1011 0100 0000 0000
      140:     100 0110 0
      15213:            0110 1101 1011 01
      
     
    
    value     binary     Rounded   Action
    2 3/32    10.00011    10.00       down      .5 100
    2 3/16    10.00110    10.01       up
    2 7/8     10.11100    11.00
    2 5/8     10.10100    10.10

  向偶数舍入(Round-To-Even)
      
     
保护模式 (段模式)
     
32位微处理器
  8个通用寄存器
    %eax     : %ah %al
    %edx
    %ecx
    %ebx
    %esi
    %edi
    %esp
    %ebp  
  指令寄存器扩展为32位, EIP
  6个段寄存器 (CS DS SS ES FS GS)
     段寄存器长度均为16位,其中13位代表内存段的一个编号,称为“段选择器”
     
ESP:栈指针寄存器(extended stack pointer),
     其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。
EBP:基址指针寄存器(extended base pointer),
     其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。
    
---- CPU  ---------------
  P     Registers       |
  C     Condition Codes |
-------------------------

指令寄存器 (PC Process Counter)
    下一条指令的地址
    EIP(x86-32) RIP(x86-64)


gcc -O2 -S add.c -m32 -fno-omit-frame-pointer

汇编语言数据格式
  C声明          Intel数据类型   汇编代码后缀     大小
 char             字节             b            1
 short            字               w            2
 int              双字             l            4
 long int         双字             l            4
 long long int    --
 char*            双字             l            4
 float            单精度           s            4
 double           双精度           l            8
 long double      扩展精度         t            10/12
 
      Source      Dest
                  Reg       movl  $0x4,%eax     temp = 0x4;
      Imm         Mem       movl  $-147,(%eax)  *p = -147;
    
movl  Reg         Reg       movl %eax,%edx      temp2=temp1;
                  Mem       movl %eax,(%edx)    *p = temp;
                  
      Mem         Reg       movl (%eax),%edx    temp = *p;

   间接寻址      (R)        Mem[Reg[R]]
        movl (%ecx),%eax
        
   基址+偏移量  D(R)     Mem[Reg[R]+D]
        movl 8(%ebp),%edx
        
变址寻址:
    D(Rb,Ri,S)   Mem[Reg[Rb]+S*reg[Ri]+D]
    D:常量(地址偏移量)
    Rb:基址寄存器:8个通用寄存器之一
    Ri: 索引寄存器: %esp不作为索引寄存器
        一般%ebp也不用做这个用途
    S: 比例因子1,2,4,8
    
    0x8(%edx)       0xf000 + 0x8     0xf008
    (%edx,%ecx)     0xf000 + 0x100   0xf100
    (%edx,%ecx,4)   0xf000+4*0x100   0xf400
    0x80(,%edx,2)   2*0xf000+0x80    0x1e080
    
  寻址模式实例:
    movb
    movw
    movl
    
    movs           D <---符号扩展(S)
    movsbw
    movsbl
    movswl
    
    MOVZ   S,D     D <---零扩展(S)
    movzbw
    movzbl
    movzwl
    
    pushl
    popl  
    
  地址计算指令:
    leal Src,Dest   
        leal (%edx,%eax),%ecx    # ecx = x+y
        leal (%edx,%edx,2),%edx  # edx = 3*y
     
  双操作数指令:
    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    逻辑右移
    xorl  Src,Dest  Dest = Dest ^ Src
    andl  Src,Dest  Dest = Dest & Src
    orl   Src,Dest  Dest = Dest | Src
    
  单操作数指令
    incl Dest    Dest = Dest + 1
    decl Dest    Dest = Dest -1
    negl Dest    Dest = - Dest
    notl Dest    Dest = ~ Dest

     
指针在32位是4bytes, 64位8bytes
     
        
-masm=intel   Intel/Microsoft Format格式的汇编

64位:
    16个通用寄存器
    %rax %eax        %r8  %r8d
    %rdx %edx        %r9  %r9d
    %rcx %ecx        %r10 %r10d
    %rbx %ebx        %r11 %r11d
    %rsi %esi        %r12 %r12d
    %rdi %edi        %r13 %r13d
    %rsp %esp        %r14 %r14d
    %rbp %ebp        %r15 %r15d
 参数小于7个时
   rdi,rsi,rdx,rcx,r8,r9, 多于7个的放于栈中  
 
条件码:
    CF Carry Flag,进位
        用于检测无符号整数运算的溢出
    ZF Zero : set if t == 0        set 1
    SF Sign : set if t < 0
    OF Overflow : set if 补码运算溢出(即带符号整数运算)
          (a>0 && b>0 && t<0)
          || (a<0 && b<0 && t>0)
    
  比较指令
  cmpl Src2,Src1
     ZF set if a==b
     SF set if(a-b)<0
     OF set if (a>0&&b<0 && (a-b)<0) ||
     
  测试指令  and
    ZF set when a&b == 0
    SF set when a&b < 0
 
  SetX指令
    读取当前的条件码(或者某些条件码的组合),并存入目的字节寄存器
 
  SetX      Condition       Description
 
  sete      ZF              Equal/Zero
  setne     ~ZF             Not Equal/Not Zero
  sets         SF                Negative
  setns     ~SF
  setg        ~(SF^OF)&~ZF    Greter(Signed)
  setge     ~(SF^OF)         Greater of Equal(Signed)
  setl        (SF^OF)            Less(Signed)
  setle        (SF^OF)|ZF        Less or Equal(Signed)
  seta        ~CF&~ZF            Above(unsigned)
  setb        CF                Below(unsigned)
 
 32位:   (movzbl 指令对目的寄存器进行"0"扩展)
    int gt(int x,int y)
    {
        return x>y;
    }
    movl 12(%ebp),%eax    # eax = y
    cmpl %eax,8(%ebp)      # compare x:y
    setg %al              # al = x>y
    movzbl %al,%eax       # zero rest of %eax
 
 64位
  int gt(long x,long y)       long lgt(long x, long y)
  {                               {
        return x>y;               return x>y;
  }                           {
 
  assem:
    xorl  %eax,%eax    # eax = 0
    cmpg  %rsi,%rdi    # Compare x:y
    setg  %al          # al = x>y
    
  跳转指令:
    jX            Condition        Description
    
    jmp            1                Unconditional
    je            ZF                Equal/Zero
    jne
    js
    jns
    jg
    jge
    jl
    jle
    ja
    jb
    
  64:
    cmovC  stc,dest
     如果条件C成立,将数据从src传送至dest
     
    -march=i686
    
int fact_goto(int x)
{
    int result = 1;
loop:
    result *= x;
    x = x-1;
    if(x>1)
        goto loop;
    return result;    
}

fact_goto:
    pushl %ebp
    movl  $1,%eax
    movl  8(%ebp),%edx
    
L11:
    imul %edx,%eax
    decl %edx
    cmpl $1,%edx
    jg L11
    
jump-to-middle

条件跳转指令会引起性能损失,Branck Prediction技术被引入来进行优化

switch :
    表结构:
        每个表项(及跳转地址占4个字节)
        基地址是.L62
    jmp .L61
    jmp *.L62(,%edx,4)  *表明这是一个间接跳转,即目标地址存于内存地址中,数即是地址

 .section .rodata
    .allign 4
 .L62:
    .long  .L61   # x=0
    .long  .L56   # x=1
    .long  .L57   # x=2
    .long  .L58   # x=3
    .long  .L61   # x=4
    .long  .L59   # x=5
    .long  .L60   # x=6
    
   12356, 0,4没有就跳到default
 
 64:
    .L62
        .quad  .L55   # x=0
    
 64位寄存器使用惯例:
    %rax (Return Value)     %r8  Argument #5
    %rdx Callee Saved       %r9  Argument #6
    %rcx Argument #4        %r10 Callee Saved
    %rbx Argument #3        %r11 Callee Saved
    %rsi Argument #2        %r12 Callee Saved
    %rdi Argument #1        %r13 Callee Saved
    %rsp %esp                %r14 Callee Saved
    %rbp Callee Saved       %r15 Callee Saved
    
 
  压栈操作:
    pushl Src
    %esp = %esp -4
  出栈操作:
    popl Dest
    %esp = %esp + 4
    
  过程调用指令:
    call label  将返回地址压入栈,跳转至label
  过程返回指令:
    ret    跳转至栈顶的返回地址
    

  64位:
    一次性分配整个栈帧
      将%rsp减去某个值(栈帧的大小)   128宏区
      对于栈帧内容的访问都是基于%rsp完成的
      可以延迟分配
    释放简单
    

 数组访问:    
  x86-32:
    # %edx = z       数组起始地址
    # %eax = dig    下标
    movl (%edx,%eax,4),%eax # z[dig]
        
    二维数组:
        pgh[index][dig]
            pgh + 20*index + 4*dig
    # %ecx = dig
    # %eax = index
    leal 0(,%ecx,4),%edx       # 4*dig
    leal (%eax,%eax,4),%eax    # 5*index
    movl pgh(%edx,%eax,4),%eax # *(pgh+5*index+4*dig)
    
Multi-Level Array:
  int get_univ_digit(int index,int dig){
    return univ[index][dig];
  }        
  leal 0(,%ecx,4),%edx      # 4*index
  movl univ(%edx),%edx      # Mem[univ+4*index]
  movl (%edx,%eax,4),%eax   # Mem


 "."开头的行都是汇编指示(Directives) .file .def .text
    其中.file和.def均用于调试(可忽略)
    
  ":"

gcc -O2 -mpreferred-stack-boundary=2

编译参数:
as -o my-object-file.o hellowrold.s
  -gstabs  //产生带调试信息的Object文件
  --32
 
ld -o my-exe-file my-object-file.o
   -m elf_i368
   
sudo apt install g++-multilib

.text
.globl _start

_start:
    popl %ecx    # argc
    
-- helloworld.s
.data
msg:
    .ascii "Hello hhh  world\n"
    len = .-msg            # "."表示当前地址,减msg表示长度

.text                      # 代码段
.globl _start              # 汇编程序的入口

_start:
    movl $len,%edx
    movl $msg,%ecx
    movl $1,%ebx          # 系统输出(write系统调用)
    movl $4,%eax          # write系统调用编号是4
    int $0x80             # 中断指令

    movl $0,%ebx          # 程序退出 exit()中的参数0
    movl $1,%eax          # 1号系统调用,就是程序退出
    int $0x80

传给系统调用的功能号,存放到
    ebx,ecx,edx,esi,edi中,再多就放入栈中
    返回值在 eax 中获得

部分系统调用列表:

exit-terminate current process:
    
    In          eax                 1
                ebx                    return code
    Out            
    
fork-create child process
    
    In            eax                 2
    Out            eax                    0 in the clone
                                    process id of clone
                                    
read-read from file or device:
    
    In            eax                    3
                ebx                    file descriptor
                ecx                    address of the buffer to read into
                edx                    maximum number of bytes to read
    Out         eax                    number of bytes actually read

close
    In             eax                    6
                ebx                    file descriptor
    Out            eax                    zero for success | EBADF
    
x86-32 linux系统调用的参考资料
    http://syscalls.kernelgrok.com/
    
处理命令行参数:

.text
.globl _start

_start:
    popl %ecx             # argc
vnext:
    popl %ecx            #argv
    test %ecx,%ecx      
    jz      exit          #如果为空就退出
    movl %ecx,%ebx     # 参数列表的首地址
    xorl %edx,%edx
strlen:
    movb (%ebx),%al
    inc  %edx          # 参数个数 记数
    inc  %ebx
    test %al,%al       #如果为0则表示结束了  // and ,常用于判断是否为空
    jnz strlen
    
    movl $10,-1(%ebx)  # 10是换行符
    movl $4,%eax       # 系统调用
    movl $1,%ebx       # 文件描述符
    int $0x80
    jmp vnext
    
exit:
    movl $1,%eax
    xorl %ebx,%ebx      # 自己和自己异或,清零
    int $0x80     
    
汇编调用lib_c库函数示例
.section .data
output:
         .asciz  "The process vendor id is '%s'\n"   # z zero,补零
.section .bss       ;可读可写且没有初始化的数据区
         .lcomm  buffer,12
.section .text
.globl   _start
_start:
        movl  $0,%eax
        cpuid          ; eax传入0,返回值在ebx,edx,ecx
        movl  $buffer,%edi
        movl  %ebx,(%edi)
        movl  %edx,4(%edi)
        movl  %ecx,8(%edi)
        push  $buffer    ; $表示取的是地址的值
        push  $output
        call  printf
        addl  $8,%esp  ;调用者恢复
        push  $0       ;c函数exit的参数
        call  exit
    
as -o cpuid.o cpuid.s --32
ld -m elf_i386 -lc -dynamic-linker  /lib/ld-linux.so.2 -o cpuid cpuid.o
        
数据段 .rodata
output:
        .ascii  "hello world."           .byte
pi:                                         .double
        .float 3.14                         .float
ages:                                    .long    32位整数,和int相同
        .int 20,10,30,40                 .octa   16字节整数
                                         .qual   8字节
                                         .short  16位
                                         .singlee 单精度浮点数
                                        
bss段
   无需声明特定的数据类型,只需声明为所需目的的原始内存部分即可
   .comm 声明为未初始化全局内存区域
   .lcomm  本地 , 外部模块不能访问他们
   
   
幂计算:
    pushl $3
    pushl $2
    call power
    addl $8,%esp    # move the stack pointer back
    pushl %eax      # save the first anwser
    
    pushl $2
    pushl $5
    call power
    addl $8,$esp
    popl %ebx
    
    addl %eax,%ebx
    movl $1,%eax
    int $0x80     
    
    
.equ
    .equ factor,3   # 类似define

syscall 号码:    
/usr/include/asm/unistd_64.h
32位的汇编程序请查看/usr/include/asm/unistd_32.h                                        
                         
    


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM