style: 修改排版

This commit is contained in:
iGmainC 2022-08-10 23:32:47 +08:00 committed by GitHub
parent f7b86df6bb
commit e56f76fac9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 10 additions and 10 deletions

View File

@ -4,13 +4,13 @@ Go 汇编程序始终是幽灵一样的存在。我们将通过分析简单的 G
## 3.1.1 实现和声明
Go 汇编语言并不是一个独立的语言,因为 Go 汇编程序无法独立使用。Go 汇编代码必须以 Go 包的方式组织,同时包中至少要有一个 Go 语言文件用于指明当前包名等基本包信息。如果 Go 汇编代码中定义的变量和函数要被其它 Go 语言代码引用,还需要通过 Go 语言代码将汇编中定义的符号声明出来。用于变量的定义和函数的定义 Go 汇编文件类似于 C 语言中的. c 文件,而用于导出汇编中定义符号的 Go 源文件类似于 C 语言的. h 文件。
Go 汇编语言并不是一个独立的语言,因为 Go 汇编程序无法独立使用。Go 汇编代码必须以 Go 包的方式组织,同时包中至少要有一个 Go 语言文件用于指明当前包名等基本包信息。如果 Go 汇编代码中定义的变量和函数要被其它 Go 语言代码引用,还需要通过 Go 语言代码将汇编中定义的符号声明出来。用于变量的定义和函数的定义 Go 汇编文件类似于 C 语言中的 `.c` 文件,而用于导出汇编中定义符号的 Go 源文件类似于 C 语言的 `.h` 文件。
## 3.1.2 定义整数变量
为了简单,我们先用 Go 语言定义并赋值一个整数变量,然后查看生成的汇编代码。
首先创建一个 pkg.go 文件,内容如下:
首先创建一个 `pkg.go` 文件,内容如下:
```go
package pkg
@ -59,7 +59,7 @@ GLOBL ·Id, $8
现在已经初步完成了用汇编定义一个整数变量的工作。
为了便于其它包使用该 Id 变量,我们还需要在 Go 代码中声明该变量,同时也给变量指定一个合适的类型。修改 pkg.go 的内容如下:
为了便于其它包使用该 Id 变量,我们还需要在 Go 代码中声明该变量,同时也给变量指定一个合适的类型。修改 `pkg.go` 的内容如下:
```go
package pkg
@ -69,7 +69,7 @@ var Id int
现状 Go 语言的代码不再是定义一个变量,语义变成了声明一个变量(声明一个变量时不能再进行初始化操作)。而 Id 变量的定义工作已经在汇编语言中完成了。
我们将完整的汇编代码放到 pkg_amd64.s 文件中:
我们将完整的汇编代码放到 `pkg_amd64.s` 文件中:
```
#include "textflag.h"
@ -86,7 +86,7 @@ DATA ·Id+6(SB)/1,$0x00
DATA ·Id+7(SB)/1,$0x00
```
文件名 pkg_amd64.s 的后缀名表示 AMD64 环境下的汇编代码文件。
文件名 `pkg_amd64.s` 的后缀名表示 AMD64 环境下的汇编代码文件。
虽然 pkg 包是用汇编实现,但是用法和之前的 Go 语言版本完全一样:
@ -107,7 +107,7 @@ func main() {
在前一个例子中,我们通过汇编定义了一个整数变量。现在我们提高一点难度,尝试通过汇编定义一个字符串变量。虽然从 Go 语言角度看,定义字符串和整数变量的写法基本相同,但是字符串底层却有着比单个整数更复杂的数据结构。
实验的流程和前面的例子一样,还是先用 Go 语言实现类似的功能,然后观察分析生成的汇编代码,最后用 Go 汇编语言仿写。首先创建 pkg.go 文件,用 Go 语言定义字符串:
实验的流程和前面的例子一样,还是先用 Go 语言实现类似的功能,然后观察分析生成的汇编代码,最后用 Go 汇编语言仿写。首先创建 `pkg.go` 文件,用 Go 语言定义字符串:
```go
package pkg
@ -128,7 +128,7 @@ go.string."gopher" SRODATA dupok size=6
输出中出现了一个新的符号 go.string."gopher",根据其长度和内容分析可以猜测是对应底层的 "gopher" 字符串数据。因为 Go 语言的字符串并不是值类型Go 字符串其实是一种只读的引用类型。如果多个代码中出现了相同的 "gopher" 只读字符串时,程序链接后可以引用的同一个符号 go.string."gopher"。因此,该符号有一个 SRODATA 标志表示这个数据在只读内存段dupok 表示出现多个相同标识符的数据时只保留一个就可以了。
而真正的 Go 字符串变量 Name 对应的大小却只有 16 个字节了。其实 Name 变量并没有直接对应 “gopher” 字符串,而是对应 16 字节大小的 reflect.StringHeader 结构体:
而真正的 Go 字符串变量 `Name` 对应的大小却只有 16 个字节了。其实 `Name` 变量并没有直接对应 “gopher” 字符串,而是对应 16 字节大小的 reflect.StringHeader 结构体:
```go
type reflect.StringHeader struct {
@ -137,9 +137,9 @@ type reflect.StringHeader struct {
}
```
从汇编角度看Name 变量其实对应的是 reflect.StringHeader 结构体类型。前 8 个字节对应底层真实字符串数据的指针,也就是符号 go.string."gopher" 对应的地址。后 8 个字节对应底层真实字符串数据的有效长度,这里是 6 个字节。
从汇编角度看,`Name` 变量其实对应的是 `reflect.StringHeader` 结构体类型。前 8 个字节对应底层真实字符串数据的指针,也就是符号 go.string."gopher" 对应的地址。后 8 个字节对应底层真实字符串数据的有效长度,这里是 6 个字节。
现在创建 pkg_amd64.s 文件,尝试通过汇编代码重新定义并初始化 Name 字符串:
现在创建 pkg_amd64.s 文件,尝试通过汇编代码重新定义并初始化 `Name` 字符串:
```
GLOBL ·NameData(SB),$8
@ -152,7 +152,7 @@ DATA ·Name+8(SB)/8,$6
因为在 Go 汇编语言中go.string."gopher" 不是一个合法的符号,因此我们无法通过手工创建(这是给编译器保留的部分特权,因为手工创建类似符号可能打破编译器输出代码的某些规则)。因此我们新创建了一个 ·NameData 符号表示底层的字符串数据。然后定义 ·Name 符号内存大小为 16 字节,其中前 8 个字节用 ·NameData 符号对应的地址初始化,后 8 个字节为常量 6 表示字符串长度。
当用汇编定义好字符串变量并导出之后,还需要在 Go 语言中声明该字符串变量。然后就可以用 Go 语言代码测试 Name 变量了:
当用汇编定义好字符串变量并导出之后,还需要在 Go 语言中声明该字符串变量。然后就可以用 Go 语言代码测试 `Name` 变量了:
```go
package main