分享
  1. 首页
  2. 文章

Go Assembly Programming

polaris · · 1961 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

源自 Plan9 汇编实现。 保存在 .s 文件中,编译器自动编译、链接。

本文内容基于 amd64 架构。

指令

指令参数长度。

MOVB: 1-byte
MOVW: 2 
MOVL: 4 
MOVQ: 8 

数据移动方向:从左往右。

 ADD R1, R2 // R2 += R1
 SUB R3, R4 // R4 -= R3
 SUB R3, R4, R5 // R5 = R4 - R3
 MUL 7,ドル R6 // R6 *= 7

内存访问。

 MOV (R1), R2 // R2 = *R1
 MOV 8(R1), R2 // R2 = *(8 + R2)
 MOV 16(R1)(R2*2), R3 // R3 = *(16 + R1 + R2*2)
 MOV runtime·x(SB), R2 // R2 = *runtime·x

跳转指令。

 JMP label // 跳转到标签。
 JMP 2(PC) // 跳转到 PC + n 行。
 JMP -2(PC)

数字常量以 $ 开头,十进制(10ドル)和 十六进制(0ドルx10)。 标签仅在函数內有效。

伪寄存器

伪寄存器(pseudo-register)由语言定义并使用,最终会被编译为硬件寄存器引用。

考虑到平台差异,编译后的机器代码,可能须保存 PC、BP、SP 等物理寄存器值。 在编写汇编代码时,很难事先计算好实际所需偏移量。为此,汇编语言用伪寄存器表示某个相对位置就很有必要。

  • SB: Static Base Pointer(全局符号)

表示一个全局符号地址,通常应用于全局函数或数据。
例如 CALL add(SB) 表示对应符号名字为 add 的内存地址。
在名字后添加尖括号(add<>(SB)),表示该符号名仅在当前文件內可见。
还可用偏移量表示基于某个符号名字的地址,例如 add+8(SB)。

  • FP: Frame Pointer(参数地址)

指向由调用方提供的参数列表起始地址,通过偏移量指向不同参数或返回值。
通常在偏移量前包含参数名。例如 MOVQ size+16(FP), AX
  • SP: Stack Pointer (栈局部变量内存地址)

伪 SP 寄存器表示栈帧內,用于本地局部变量操作的起始地址。
鉴于栈从底开始的操作方式,SP 实际是栈底位置(等同调整后的 BP 地址)。
使用该方式访问局部变量,须添加变量名,如 x-8(SP)。如果省略变量名,则表示硬件寄存器。

  • PC: Program Counter(指令地址)

可用来按指令行数条转。
比如 JMP 2(PC) 表示以当前位置为 0 基准,往下跳到第 2 行。

考虑到栈帧内存实际上分成局部变量(底)和调用参数(顶)两部分使用,所以用伪 SP 寄存器负值便宜访问局部变量是很自然的做法。 如此,物理寄存器 SP 用来操作调用参数入栈;而伪寄存器 SP 用来访问局部变量。 毕竟 BP 寄存器是可选的。

注意 x+0(FP)gobuf_pc(AX) 宏函数的区别。

 CALLEE
 lo SP +-----------+ ..........................
 | | .
 +-----------+ .
 | | frame size(包括 caller BP)
 BP (pseudo SP) +-----------+ .
 | caller BP | .
 +-----------+ ..........................
 | caller PC |
 FP +-----------+------------+ SP ..........
 | arg0 | call arg0 | .
 +-----------+------------+ .
 | argn | call argn | argument size
 +-----------+------------+ .
 | return | call ret | .
 hi +-----------+------------+ .............
 | local var0 |
 +------------+
 | local varn |
 +------------+ BP (pseudo SP)
 CALLER

函数

函数定义。

 参数及返回值大小
 | 
 TEXT runtime·cgocallback(SB),NOSPLIT,32ドル-32
 | | |
 包名 函数名 栈帧大小(不包括参数及返回值)

当前包,可省略包名,直接以中心点开始。

由调用者(caller)负责分配目标函数(callee)参数和返回值内存。 调用者须自行保存相关寄存器状态。

示例

使用汇编代码编写一个简单的加法。

add.s

 #include "textflag.h"
 
 // add(x, y int) int
 TEXT ·add(SB), NOSPLIT, 8ドル-24
 MOVQ 0,ドル z-0x8(SP)
 MOVQ x+0x0(FP), AX
 MOVQ y+0x8(FP), BX
 ADDQ AX, BX 
 MOVQ BX, z-0x8(SP)
 MOVQ BX, ret+0x10(FP)
 RET

main.go

 package main
 
 func add(x, y int) (z int) // 声明汇编函数原型
 
 func main() {
 z := add(0x100, 0x200)
 println(z)
 }

可以看到编译器插入栈帧调整,环境保存等指令。

 $ go build -gcflags "-l"
 
 $ go tool objdump -s "main\.add" test
 
 TEXT main.add(SB) add.s
 add.s:5 0x104bfe0 SUBQ 0ドルx10, SP // 因为要保存 BP,所以栈帧大小调整到 0x10。
 add.s:5 0x104bfe4 MOVQ BP, 0x8(SP) 
 add.s:5 0x104bfe9 LEAQ 0x8(SP), BP 
 
 add.s:6 0x104bfee MOVQ 0ドルx0, 0(SP) 
 add.s:7 0x104bff6 MOVQ 0x18(SP), AX 
 add.s:8 0x104bffb MOVQ 0x20(SP), BX 
 add.s:9 0x104c000 ADDQ AX, BX 
 add.s:10 0x104c003 MOVQ BX, 0(SP) 
 add.s:11 0x104c007 MOVQ BX, 0x28(SP) 
 add.s:12 0x104c00c MOVQ 0x8(SP), BP 
 
 add.s:12 0x104c011 ADDQ 0ドルx10, SP // 清除栈帧。
 add.s:12 0x104c015 RET 

使用 -gcflags -S 输出反汇编时,会有 FUNCDATA 和 PCDATA 信息。 它们是编译器引入,包含垃圾回收器要使用的信息。


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
1961 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏