1
0
mirror of https://github.com/chai2010/advanced-go-programming-book.git synced 2025-05-24 12:32:21 +00:00

fix type error

This commit is contained in:
pplonepiece 2018-07-05 12:00:03 +08:00
parent af9a7337a6
commit 1dbb2e1cb5

View File

@ -172,7 +172,7 @@ func main() {
pkgpath.NameData: missing Go //type information for global symbol: size 8
```
提示汇编中定义的NameData符号没有类型信息。其实Go汇编语言中定义的数据并没有所谓的类型每个符号只不过是对应一个内存而且。出现这种错误的原因是Go语言的垃圾回收器在扫描NameData变量的时候无法知晓该变量内部是否包含指针。因此真正错误的原因并不是NameData没有类型是NameData变量没有标注是否会含有指针信息。
提示汇编中定义的NameData符号没有类型信息。其实Go汇编语言中定义的数据并没有所谓的类型每个符号只不过是对应一块内存而已。出现这种错误的原因是Go语言的垃圾回收器在扫描NameData变量的时候无法知晓该变量内部是否包含指针。因此真正错误的原因并不是NameData没有类型是NameData变量没有标注是否会含有指针信息。
通过给NameData变量增加一个标志表示其中不会包含指针数据可以修复该错误
@ -193,9 +193,9 @@ var NameData [8]byte
var Name string
```
我们将NameData声明为长度为8的字节数组。因为编译器可以通过类型分析出该变量不会包含指针因此汇编代码中可以NOPTR标志信息
我们将NameData声明为长度为8的字节数组。编译器可以通过类型分析出该变量不会包含指针因此汇编代码中可以省略NOPTR标志。
在这个实现中Name字符串底层其实引用的是NameData内存对应的“gopher”字符串数据。因此如果NameData发生变化的化Name字符串的数据也会跟着变化
在这个实现中Name字符串底层其实引用的是NameData内存对应的“gopher”字符串数据。因此如果NameData发生变化Name字符串的数据也会跟着变化。
```go
func main() {
@ -218,7 +218,7 @@ DATA ·Name+8(SB)/8,$6
DATA ·Name+16(SB)/8,$"gopher"
```
在新的结构中Name符号对应的内存从16字节变为24字节多出的8个字节用户存放底层的“gopher”字符串。·Name符号前16个字节依然对应reflect.StringHeader结构体Data部分对应`$·Name+16(SB)`表示数据的地址为Name符号往后偏移16个字节的位置Len部分依然对应6个字节的长度。
在新的结构中Name符号对应的内存从16字节变为24字节多出的8个字节存放底层的“gopher”字符串。·Name符号前16个字节依然对应reflect.StringHeader结构体Data部分对应`$·Name+16(SB)`表示数据的地址为Name符号往后偏移16个字节的位置Len部分依然对应6个字节的长度。
## 定义main函数
@ -245,7 +245,7 @@ TEXT ·main(SB), $16-0
CALL runtime·printnl(SB)
RET
```
`TEXT ·main(SB), $16-0`用于定义`main`函数,其中`$16-0`表示`main`函数的帧大小是16个字节对应string头的大小用于给`runtime·printstring`函数传递参数),`0`表示`main`函数没有参数和返回值。`main`函数内部通过调用运行时内部的`runtime·printstring(SB)`函数来打印字符串。然后调用runtime·printnl打印换行符号。
`TEXT ·main(SB), $16-0`用于定义`main`函数,其中`$16-0`表示`main`函数的帧大小是16个字节对应string头部结构体的大小,用于给`runtime·printstring`函数传递参数),`0`表示`main`函数没有参数和返回值。`main`函数内部通过调用运行时内部的`runtime·printstring(SB)`函数来打印字符串。然后调用`runtime·printnl`打印换行符号。
Go语言函数在函数调用时完全通过栈传递调用参数和返回值。先通过MOVQ指令将helloworld对应的字符串头部结构体的16个字节复制到栈指针SP对应的16字节的空间然后通过CALL指令调用对应函数。最后使用RET指令表示当前函数返回。
@ -254,7 +254,7 @@ Go语言函数在函数调用时完全通过栈传递调用参数和返回值
Go语言函数或方法符号在编译为目标文件后目标文件中的每个符号均包含对应包的绝对导入路径。因此目标文件的符号可能非常复杂比如“path/to/pkg.(*SomeType).SomeMethod”或“go.string."abc"”。目标文件的符号名中不仅仅包含普通的字母还可能包含诸多特殊字符。而Go语言的汇编器是从plan9移植过来的二把刀并不能处理这些特殊的字符导致了用Go汇编语言手工实现Go诸多特性时遇到种种限制。
Go汇编语言同样遵循Go语言少即是多的哲学它只保留了最基本的特性定义变量和全局函数。同时为了简化Go汇编器的词法扫描程序的实现特别引入了Unicode中的中点`·`和大写的除法`/`对应的Unicode码点为`U+00B7``U+2215`。汇编器编译后,中点`·`会被替换为ASCII中的点“.”,大写除法会被替换为ASCII码中的除法“/”,比如`math/rand·Int`会被替换为`math/rand.Int`。这样可以将点和浮点数中的小数点、大写的除法和表达式中的除法符号分开,可以简化汇编程序法分析部分的实现。
Go汇编语言同样遵循Go语言少即是多的哲学它只保留了最基本的特性定义变量和全局函数。同时为了简化Go汇编器的词法扫描程序的实现特别引入了Unicode中的中点`·`和大写的除法`/`对应的Unicode码点为`U+00B7``U+2215`。汇编器编译后,中点`·`会被替换为ASCII中的点“.”,大写除法会被替换为ASCII码中的除法“/”,比如`math/rand·Int`会被替换为`math/rand.Int`。这样可以将点和浮点数中的小数点、大写的除法和表达式中的除法符号分开,可以简化汇编程序法分析部分的实现。
即使暂时抛开Go汇编语言设计取舍的问题中点`·`和除法`/`两个字符的如何输入就是一个挑战。这两个字符在 https://golang.org/doc/asm 文档中均有描述,因此直接从该页面复制是最简单可靠的方式。