[go]汇编ASM简介


Go汇编代码主要用于优化和与底层系统交互,并不会像其它的经典汇编代码那样独立运行。

汇编(ASM)

Go ASM是一种被Go编译器使用的特殊形式的汇编语言(伪汇编),它基于Plan9输入风格;它是架构独立的,没有所谓的32或64位寄存器。

go汇编寄存器与x86_64对应关系:

AMD64  rax rbx rcx rdx rdi rsi rbp rsp r8 r9 r10 r11 r12 r13 r14 rip
Plan9  AX  BX  CX  DX  DI  SI  BP  SP  R8 R9 R10 R11 R12 R13 R14 PC

寄存器

Go ASM中有四个预定义的符号作为伪寄存器(虚拟寄存器,伪寄存器使用时一般需要一个标识符和偏移量为前缀):

  • FP: 帧指针,参数和局部变量;
  • PC: 程序计数器,跳转和分支;
  • SB: 静态基址指针,用来定义和读取文本区(代码区)、数据区的;
  • SP: 栈指针,栈的顶端;

伪寄存器SB可以看作是内存的起始地址。foo(SB)就是foo在内存中的地址,有两种修饰符<>和+N(整数)

  • foo<>(SB):表示一个私有元素,只有在同一个源文件中才可以访问;
  • +N:表示相对地址加上一个偏移量后得到的地址,如foo+8(SB)就指向foo之后8个字节处的地址;

帧指针FP

Go汇编使用的是caller-save模式,被调用函数的入参参数、返回值都由调用者维护、准备。因此,当需要调用一个函数时,需要先将这些工作准备好,才调用下一个函数,另外这些都需要进行内存对齐,对齐的大小是 sizeof(uintptr)。
在这里插入图片描述

伪寄存器FP是一个虚拟帧指针,被用来引用过程参数(由编译器负责维护):

  • 在64位机器上,0(FP)是第一个参数,8(FP)是第二个参数;
  • 为清晰和可读性的考虑,编译器会强制它们的命名使用:MOVL foo+0(FP),CX把第一个参数放入到物理上的CX寄存器;MOVL bar+8(FP),DX把第二个参数放入到DX寄存器中。

常见指令

栈调整(SP寄存器):

SUBQ    $24, SP  // 为函数分配函数栈帧
...
ADDQ    $24, SP  // 清除函数栈帧

加减操作:

ADDQ  AX, BX   // BX += AX
SUBQ  AX, BX   // BX -= AX

数据搬运:搬运的长度是由MOV的后缀决定

MOVB $1, DI      // 1 byte
MOVW $0x10, BX   // 2 bytes
MOVD $1, DX      // 4 bytes
MOVQ $-10, AX    // 8 bytes

// 加括号代表是指针的引用
MOVQ (AX), BX   // => BX = *AX 将AX指向的内存区域8byte赋值给BX
MOVQ 16(AX), BX // => BX = *(AX + 16)

//不加括号是值的引用
MOVQ AX, BX     // => BX = AX 将AX中存储的内容赋值给BX

跳转:

// 无条件跳转
JMP addr   // 跳转到地址,地址可为代码中的地址
JMP label  // 跳转到标签,可以跳转到同一函数内的标签位置
JMP 2(PC)  // 以当前指令为基础,向前/后跳转 x 行

// 有条件跳转
JLS addr

地址运算:

// 2代表 scale,scale只能是0、2、4、8
LEAQ (AX)(AX*2), CX // => CX = AX + (AX * 2) = AX * 3

函数示例

func neg(x uint64) int64为例,会生成类似如下的汇编代码:

TEXT ·neg(SB), NOSPLIT, $0-16
    MOVQ     x+0(FP), AX
    NEGQ     AX
    MOVQ     AX, ret+8(FP)
    RET

以中间点·作为名的分隔符;没有前缀的·foo等价于main·foo

  • TEXT: 标识位于text段;
  • ·neg: 函数名(包括包名);
  • (SB): 基址指针,代表函数地址;
  • NOSPLIT: 禁止stack拆分(即函数内部不需要判断栈空间是否足够,不会增长函数栈);编译器必须确保运行函数是安全的,即有足够的栈空间;
  • $0-16:其中0表示函数栈帧大小为0(即,没有局部变量);16表示参数及返回值的大小;

生成汇编

生成Go汇编:

  • go tool compile -S -N -l main.go直接输出汇编;
  • go build -gcflags="-N -l -S" main.go直接输出汇编;
    注意:加上对应的flag,避免某些逻辑被编译器优化掉,而看不到对应完整的汇编代码:
    • -l:禁止内联
    • -N:禁止优化
    • -S:输出汇编代码
8088 汇编跳转 一、状态寄存器 PSW(Program Flag)程序状态字寄存器,是一个16位寄存器,由条件码标志(flag)和控制标志构成,如下所示: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0         OF DF IF TF SF ZF   AF   PF   CF 条件码: ①OF(Overflow Flag)溢出标志。溢出时为1,否则置0。 ②SF(Sign Flag)符号标志。结果为负时置1,否则置0. ③ZF(Zero Flag)零标志,运算结果为0时ZF位置1,否则置0. ④CF(Carry Flag)进位标志,进位时置1,否则置0. ⑤AF(Auxiliary carry Flag)辅助进位标志,记录运算时第3位(半个字节)产生的进位置。有进位时1,否则置0. ⑥PF(Parity Flag)奇偶标志。结果操作数中1的个数为偶数时置1,否则置0. 控制标志位: ⑦DF(Direction Flag)方向标志,在串处理指令中控制信息的方向。 ⑧IF(Interrupt Flag)中断标志。 ⑨TF(Trap Flag)陷井标志。 二、 直接标志转移(8位寻址) 指令格式 机器码 测试条件 如...则转移     指令格式 机器码 测试条件 如...则转移 JC 72 C=1 有进位 JNS 79 S=0 正号 JNC 73 C=0 无进位 JO 70 O=1 有溢出 JZ/JE 74 Z=1 零/等于 JNO 71 O=0 无溢出 JNZ/JNE 75 Z=0 不为零/不等于 JP/JPE 7A P=1 奇偶位为偶 JS 78 S=1 负号 JNP/IPO 7B P=0 奇偶位为奇 三、间接标志转移(8位寻址) 指令格式 机器码 测试格式 如...则转移 JA/JNBE(比较无符号数) 77 C或Z=0 >  高于/不低于或等于 JAE/JNB(比较无符号数) 73 C=0 >=  高于或等于/不低于 JB/JNAE(比较无符号数) 72 C=1 <  低于/不高于或等于 JBE/JNA(比较无符号数) 76 C或Z=1  大于/不小于或等于 JGE/JNL(比较带符号数) 7D S异或O=0 >=  大于或等于/不小于 JL/JNGE(比较带符号数) 7C S异或O=1 <  小于/不大于或等于 JLE/JNG(比较带符号数) 7E (S异或O)或Z=1 <=  小于或等于/不大于
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值