go plan 9汇编

本文将简单介绍一下go语言的汇编。
主要的内容如下:

  • plan 9 是什么?为什么学习plan9?
  • plan9 的常见指令
  • go程序如何转换为plan9 ?
    下面就开始简单介绍一下。

plan 9 是什么?为什么学习plan9?

plan9, Go一套自己的汇编。按照官方文档的说法,其设计初衷是解决跨平台的问题,但是没有做好。并且它不同于传统的汇编,也就是说要想学习go汇编,你需要重新学习一套语法。社区在争论这个问题,有人说是因为go的几个大佬,原来是用plan9的。这个问题咱们这儿不讨论。
那作为普通人,我们学汇编干啥呢?
装逼!这是第一生产力!我一直有一个不成熟的想法,希望自己能够像机器一样看穿代码的运行。有时想想,也挺可笑的。
除了装逼,真的一点用处都没有了吗?
当然不是!!!
说下我能想到的几个点:

  • 确定一段代码底层执行了什么函数
  • 查看基础的数据结构的运行机制
  • 查看内存的分配
  • 查看go在函数头和函数尾插入的相关的调度
    ...
    难道这几条还不够吗?

plan9 的常见指令

其实汇编跟java, go等语言 没啥区别,无非也是变量、方法等。只是我们做应用层开发不常用而已。如果懂了基础的语法,其实也就这样。下面列几个常用的指令,看不懂也没事,多看几遍就知道了,其实我也不是很熟,啥时候真用到,再回来看也行。

栈扩大、缩小

plan9 中栈操作并没有使用push,pop,而是采用sub 跟add SP。

SUBQ $0x18, SP // 对 SP 做减法,为函数分配函数栈帧
ADDQ $0x18, SP // 对 SP 做加法,清除函数栈帧

数据copy

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

计算指令

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

跳转

// 无条件跳转
JMP addr   // 跳转到地址,地址可为代码中的地址,不过实际上手写不会出现这种东西
JMP label  // 跳转到标签,可以跳转到同一函数内的标签位置
JMP 2(PC)  // 以当前指令为基础,向前/后跳转 x 行
JMP -2(PC) // 同上
// 有条件跳转
JNZ target // 如果 zero flag 被 set 过,则跳转

变量声明

在汇编里所谓的变量,一般是存储在 .rodata 或者 .data 段中的只读值。对应到应用层的话,就是已初始化过的全局的 const、var、static 变量/常量。

DATA    symbol+offset(SB)/width, value

使用 DATA 结合 GLOBL 来定义一个变量
GLOBL 必须跟在 DATA 指令之后:

DATA age+0x00(SB)/4, $18  // forever 18
GLOBL age(SB), RODATA, $4

DATA pi+0(SB)/8, $3.1415926
GLOBL pi(SB), RODATA, $8

DATA birthYear+0(SB)/4, $1988
GLOBL birthYear(SB), RODATA, $4

函数声明

先看一个定义:

// func add(a, b int) int
//   => 该声明定义在同一个 package 下的任意 .go 文件中
//   => 只有函数头,没有实现
TEXT pkgname·add(SB), NOSPLIT, $0-8
    MOVQ a+0(FP), AX
    MOVQ a+8(FP), BX
    ADDQ AX, BX
    MOVQ BX, ret+16(FP)
    RET

代码存储在TEXT段中。
pkgname 可以省略。
比如你的方法是 runtime·main,在编译之后的程序里的符号则是 runtime.main。

                             参数及返回值大小
                                  | 
 TEXT pkgname·add(SB),NOSPLIT,$32-32
       |        |               |
      包名     函数名         栈帧大小(局部变量+可能需要的额外调用函数的参数空间的总大小,但不包括调用其它函数时的 ret address 的大小)

寄存器

有4个核心的伪寄存器,这4个寄存器是编译器用来维护上下文、特殊标识等作用的:

FP(Frame pointer): arguments and locals
PC(Program counter): jumps and branches
SB(Static base pointer): global symbols
SP(Stack pointer): top of stack

还有很多其他的用法,就先不摘抄了,后面用到再去查吧。

go程序如何转换为plan9 ?

// 编译
go build -gcflags="-S"
go tool compile -S hello.go
go tool compile -N -S hello.go // 禁止优化
// 反编译
go tool objdump 

总结与后记

本文简单的讲述了plan9 是什么?什么用途? 也罗列了几个plan9 常见的指令,增加自信。最后简单的几行指令,教你如何看某一段代码的plan9 指令。
如果时间允许,会结合前人的文章,针对go中常见的类型、api操作、内存分配、channel 等进行汇编指令级的学习,给自己加油~

参考文献

1、汇编 is so easy
2、Go 系列文章3 :plan9 汇编入门
3、golang内核系列--深入理解plan9汇编&实践

你可能感兴趣的:(go plan 9汇编)