diff --git a/ch3-asm/ch3-01-basic.md b/ch3-asm/ch3-01-basic.md index a9c7b69..fb447e6 100644 --- a/ch3-asm/ch3-01-basic.md +++ b/ch3-asm/ch3-01-basic.md @@ -1,6 +1,108 @@ # 3.1. 快速入门 -在第一章的“Hello, World 的革命”一节中,我们已经见过一个Go汇编程序。本节我们将通过由浅入深的一系列小例子来快速掌握Go汇编的简单用法。 +在第一章的“Hello, World 的革命”一节中,我们已经见过一个Go汇编程序。本节我们将通过分析简单的Go程序输出的汇编代码,然后照猫画虎用汇编实现一个简单的输出程序。 + +## 实现和声明 + +Go汇编语言并不是一个独立的语言,主要原因是因为Go汇编程序无法独立使用。Go汇编代码必须以Go包的方式被组织,同时包中至少要有一个Go语言文件。如果Go汇编代码中定义的变量和函数要被其它Go语言代码引用,还需要通过Go语言代码将汇编中定义的符号声明出来。用于变量的定义和函数的定义Go汇编文件类似于C语言中的.c文件。而用于导出汇编中定义符号的Go源文件类似于C语言的.h文件。 + +## 定义整数变量 + +为了简单,我们先用Go语言定义并赋值一个整数变量,然后查看生成的汇编代码。 + +创建pkg.go文件,内容如下: + +```go +package pkg + +var Id = 9527 +``` + +然后用以下命令查看的Go语言程序对应的伪汇编代码: + +``` +$ go tool compile -S pkg.go +"".Id SNOPTRDATA size=8 + 0x0000 37 25 00 00 00 00 00 00 '....... +``` + +输出的汇编比较简单,其中`"".Id`对应Id变量符号,变量的内存大小为8个字节。变量的初始化内容为`37 25 00 00 00 00 00 00`,对应十六进制格式的0x2537,对应十进制为9527。SNOPTRDATA是相关的标志,暂时忽略。 + +以上的内容只是目标文件对于的汇编,和Go汇编语言虽然相似当并不完全等价。Go语言官网自带了一个Go汇编语言的入门教程,地址在:https://golang.org/doc/asm。 + +Go汇编语言提供了DATA命令用于初始化变量,DATA命令的语法如下: + +``` +DATA symbol+offset(SB)/width, value +``` + +其中symbol为变量在汇编语言中对应的符号,offset是符号开始地址的偏移量,width是要初始化内存的宽度大小,value是要初始化的那天。其中当前包中Go语言定义的符号symbol,在汇编代码中对应`·symbol`,其中·为一个特殊的unicode符号。 + +采用以下命令可以给Id变量初始化为十六进制的0x2537,对应十进制的9527,常量需要以美元符号$开头表示: + +``` +DATA ·Id+0(SB)/1,$0x37 +DATA ·Id+1(SB)/1,$0x25 +``` + +变量定义好之后需要导出以共其它代码引用。Go汇编语言提供了GLOBL命令用于将符号导出: + +``` +GLOBL symbol(SB), width +``` + +其中symbol对应汇编中符号的名字,width为符号对应内存的大小。用以下命令将汇编中的·Id变量导出: + +``` +GLOBL ·Id, $8 +``` + +现在已经出版完成了用汇编定义一个整数变量的工作。 + +为了便于其它包使用该Id变量,我们还需要在Go代码中声明该变量,同时也给变量指定一个合适的类型。修改pkg.go的内容如下: + +```go +package pkg + +var Id int +``` + +表示声明一个一个int类型的Id变量。因为该变量已经在汇编中定义,因此Go语言部分只是声明变量,声明的变量不能含义初始化的操作。 + +完整的汇编代码在pkg_amd64.s中: + +``` +GLOBL ·Id(SB),$8 + +DATA ·Id+0(SB)/1,$0x37 +DATA ·Id+1(SB)/1,$0x25 +DATA ·Id+2(SB)/1,$0x00 +DATA ·Id+3(SB)/1,$0x00 +DATA ·Id+4(SB)/1,$0x00 +DATA ·Id+5(SB)/1,$0x00 +DATA ·Id+6(SB)/1,$0x00 +DATA ·Id+7(SB)/1,$0x00 +``` + +文件名pkg_amd64.s表示为AMD64环境下的汇编代码文件。 + +虽然pkg包改用汇编实现,但是用法和之前完全一样: + +```go +package main + +import pkg "pkg包的路径" + +func main() { + println(pkg.Id) +} +``` + +对于Go包的用户来说,用Go汇编语言或Go语言实现并无区别。 + +## 定义字符串变量 + +TODO ## Go语言版本 diff --git a/ch3-asm/pkg.go b/ch3-asm/pkg.go new file mode 100644 index 0000000..197a171 --- /dev/null +++ b/ch3-asm/pkg.go @@ -0,0 +1,3 @@ +package pkg + +var id int = 9527 diff --git a/ch3-asm/readme.md b/ch3-asm/readme.md index eda2cde..d33e4c5 100644 --- a/ch3-asm/readme.md +++ b/ch3-asm/readme.md @@ -6,5 +6,5 @@ Go语言中很多设计思想和工具都是传承自Plan9操作系统,Go汇 对于每一个严肃的Gopher,Go汇编语言都是一个不可忽视的技术。因为哪怕只懂一点点汇编,也便于更好地理解计算机,将更容易理解Go语言中动态栈/接口等高级特性的实现原理。而且掌握了Go汇编语言之后,你将不用担心再被其它所谓的任何高级编程语言用户鄙视。 -本章我们将简单地探讨Go汇编语言的基础用法。 +本章我们将以AMD64为主要开发环境,简单地探讨Go汇编语言的基础用法。 diff --git a/examples/ch3-01-quick-guide/id-01/pkg.go b/examples/ch3-01-quick-guide/id-01/pkg.go new file mode 100644 index 0000000..93bfda6 --- /dev/null +++ b/examples/ch3-01-quick-guide/id-01/pkg.go @@ -0,0 +1,3 @@ +package pkg + +var Id = 9527 diff --git a/examples/ch3-01-quick-guide/id-01/runme.go b/examples/ch3-01-quick-guide/id-01/runme.go new file mode 100644 index 0000000..e9b9965 --- /dev/null +++ b/examples/ch3-01-quick-guide/id-01/runme.go @@ -0,0 +1,9 @@ +// +build ignore + +package main + +import pkg "." + +func main() { + println(pkg.Id) +} diff --git a/examples/ch3-01-quick-guide/id-02/pkg.go b/examples/ch3-01-quick-guide/id-02/pkg.go new file mode 100644 index 0000000..8a207cb --- /dev/null +++ b/examples/ch3-01-quick-guide/id-02/pkg.go @@ -0,0 +1,3 @@ +package pkg + +var Id int diff --git a/examples/ch3-01-quick-guide/id-02/pkg_amd64.s b/examples/ch3-01-quick-guide/id-02/pkg_amd64.s new file mode 100644 index 0000000..09171c7 --- /dev/null +++ b/examples/ch3-01-quick-guide/id-02/pkg_amd64.s @@ -0,0 +1,11 @@ + +DATA ·Id+0(SB)/1,$0x37 +DATA ·Id+1(SB)/1,$0x25 +DATA ·Id+2(SB)/1,$0x00 +DATA ·Id+3(SB)/1,$0x00 +DATA ·Id+4(SB)/1,$0x00 +DATA ·Id+5(SB)/1,$0x00 +DATA ·Id+6(SB)/1,$0x00 +DATA ·Id+7(SB)/1,$0x00 + +GLOBL ·Id(SB),$8 diff --git a/examples/ch3-01-quick-guide/id-02/runme.go b/examples/ch3-01-quick-guide/id-02/runme.go new file mode 100644 index 0000000..c2fbf86 --- /dev/null +++ b/examples/ch3-01-quick-guide/id-02/runme.go @@ -0,0 +1,11 @@ +// +build ignore + +package main + +import ( + pkg "." +) + +func main() { + println(pkg.Id) +}