# 4.5.1 类型检查逻辑 - 包加载器

go 编译器一次编译的最大单元是一个包，输出的结果叫着对象文件（Object File），go 定义了自己的对象文件格式，并以`.a`或者`.o`为后缀存在于文件系统中。在`$GOROOT/pkg/$OS_$ARCH/`目录下就包含了所有标准库的对象文件，例如 linux 系统该目录为：`$GOROOT/pkg/linux_amd64`.

加载器在编译阶段解决包依赖问题，其任务就是通过包名找到对应的对象文件，然后将其解析成为上文提到的[Package](https://gocompiler.shizhz.me/golang-bian-yi-qi-lei-xing-jian-cha/4.4.2-shu-ju-jie-gou-package)对象。加载器可以有各种实现，其接口定义在文件`$GCROOT/compile/internal/types2/api.go`中：

```go
type Importer interface {
	Import(path string) (*Package, error)
}

type ImportMode int

type ImporterFrom interface {
	Importer
	ImportFrom(path, dir string, mode ImportMode) (*Package, error)
}
```

两个接口的区别是方法`ImporterFrom`通过指定目标目录的方式来支持[vendor 机制](https://go.googlesource.com/proposal/+/master/design/25719-go15vendor.md), 而`Importer`无法支持，保留`Importer`接口是为了兼容性考虑，ImportMode 当前没有用途。加载器的实现在`$GCROOT/compile/internal/noder/import.go`中，抛开具体细节，其代码框架为：

```go
type gcimports struct {
	packages map[string]*types2.Package
}

func (m *gcimports) Import(path string) (*types2.Package, error) {
	return m.ImportFrom(path, "" /* no vendoring */, 0)
}

func (m *gcimports) ImportFrom(path, srcDir string, mode types2.ImportMode) (*types2.Package, error) { /* 忽略方法体 */
}
```

可见`Import`只是简单地调用`ImportFrom`方法，而后者负责整个加载解析逻辑。加载器配置在[Config](https://gocompiler.shizhz.me/4.4.5-lei-xing-jian-cha-qi#1-config)中，并在类型检查的入口函数`check2()`中进行初始化，该函数在文件`$GCROOT/compile/internal/noder/irgen.go`中。

{% hint style="info" %}
go 编译过程的最后一件事情就是将编译结果导出成对象文件，所以包加载器只是该过程的一个逆操作，当前加载器的实现也是 Go 编译器团队在重写类型检查器的过程中临时的一个实现，后续可能会修改。

对于对象文件的详细格式、导出逻辑与加载逻辑我们没有必要深挖细刨，只需要掌握如下要点即可：&#x20;

1. 对象文件是二进制格式文件，是编译器的输出，链接器的输入&#x20;
2. 对象文件分为不同的块（Section）来组织数据，例如数据块，代码块，调试信息块等&#x20;
3. 对象文件内包含重定位（Relocation）信息，链接器在将多个对象链接在一起时，主要工作就是为需要重定位的数据或者指令规划内存地址&#x20;
4. go 是静态链接语言，在生成可执行文件时，链接器会将所有依赖的包链接在一起，形成一个独立的可执行文件&#x20;
5. 可以通过工具`go tool objdump`来查看对象文件或者可执行文件的内容
   {% endhint %}
