golang的build命令编译
`go build`,是我们非常常用的命令,它可以启动编译,把我们的包和相关的依赖编译成一个可执行的文件。
`usage: go build [-o output] [-i] [build flags] [packages]`
`go build`的使用比较简洁,所有的参数都可以忽略,直到只有 `go build`,这个时候意味着使用当前目录进行编译,下面的几条命令是等价的:
```
go build
go build .
go build hello.go
```
以上这三种写法,都是使用当前目录编译的意思。因为我们忽略了 `packages`,所以自然就使用当前目录进行编译了。从这里我们也可以推测出,`go build`本质上需要的是一个路径,让编译器可以找到哪些需要编译的go文件。`packages`其实是一个相对路径,是相对于我们定义的 `GOROOT`和 `GOPATH`这两个环境变量的,所以有了 `packages`这个参数后,`go build`就可以知道哪些需要编译的go文件了。
`go build flysnow.org/tools`
这种方式是指定包的方式,这样会明确地编译我们这个包。当然我们也可以使用通配符
`go build flysnow.org/tools/...`
**3个点表示匹配所有字符串,这样 `go build`就会编译tools目录下的所有包。**
**清理依赖缓存和强制依赖,执行如下命令:**
```go
go clean -modcache
go mod tidy
```
讲到 `go build`编译,不能不提跨平台编译,Go提供了编译链工具,可以让我们在任何一个开发平台上,编译出其他平台的可执行文件。
默认情况下,都是根据我们当前的机器生成的可执行文件,比如你的是Linux 64位,就会生成Linux 64位下的可执行文件,比如我的Mac;可以使用go env查看编译环境,以下截取重要的部分。
```
go env
GOARCH="amd64"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOROOT="/usr/local/go"
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
```
**注意里面两个重要的环境变量GOOS和GOARCH,其中GOOS指的是目标操作系统,它的可用值为:**
1. darwin
2. freebsd
3. linux
4. windows
5. android
6. dragonfly
7. netbsd
8. openbsd
9. plan9
10. solaris
**一共支持10种操作系统。GOARCH指的是目标处理器的架构,目前支持的有:**
1. arm
2. arm64
3. 386
4. amd64
5. ppc64
6. ppc64le
7. mips64
8. mips64le
9. s390x
一共支持9种处理器的架构
如果我们要生成不同平台架构的可执行程序,只要改变这两个环境变量就可以了,比如要生成Linux 64位的程序,命令如下:
`GOOS=linux GOARCH=amd64 go build flysnow.org/hello`
前面两个赋值,是更改环境变量,这样的好处是只针对本次运行有效,不会更改我们默认的配置。
**-ldflags参数的使用和gdb调试**
设置编译参数-ldflags "-w -s" -trimpath
其中-w为去掉调试信息(无法使用gdb调试),-s为去掉符号表
-trimpath用于在编译时移除路径信息,主要用于生成更简洁的二进制文件。该命令通过去除编译时生成的路径信息,减少二进制文件的大小并提升安全性。
`go build -ldflags "-w -s" -trimpath ./hello.go`
`gdb ./main`
这样就可以进行gdb调试。
查询所有build用法使用这个命令
`go help build`
**CGO\_ENABLED 环境变量**
系统默认为1,可以使用 `go env`命令查看。
当CGO\_ENABLED=1,进行编译时会将文件中引用libc的库(比如常用的net包),以动态链接的方式生成目标文件。
当CGO\_ENABLED=0,进行编译时则会把在目标文件中未定义的符号(外部函数)一起链接到可执行文件中。
`# CGO_ENABLED=1 go build hello.go`
# 下面详细说一说静态链接和动态链接
## 什么是静态链接和动态链接?
**静态链接**是将程序所需的所有库直接复制到最终可执行文件中的做法。
Go 语言**非常喜欢并希望**在可能的情况下这样做,因为这样生成的二进制文件更加便携,不需要在运行的主机系统上存在库。因此,你的二进制文件可以在任何系统上运行,无论是哪个发行版或版本,而且不依赖任何系统库。
另一方面,**动态链接**是在运行时按名称将外部或共享库加载到可执行文件中。
动态链接也有其自身的优势。例如,程序可以重用主机系统上可用的常用 **libc** 库,而不需要重新实现它们,你还可以在不重新链接程序的情况下受益于主机的更新。在许多情况下,它还可以减小可执行文件的大小。
## 什么是二进制文件?
使用 **file** 命令首先检查文件类型
```
# file main | tr, '\n'
// ELF(表示可执行和可链接格式)可执行文件表示
// ELF 是 Linux 上的默认格式,Mach-O 是 macOS 的默认格式,PE/PE32+ 是 Windows 的默认格式
// statically Linked 表示静态链接
// dynamically Linked 表示动态链接
```
Linux 程序 **ldd**,可以告诉我们二进制文件的动态依赖关系
```
# ldd main
```
## 动态链接的程序
如前所述,Go 有一个机制叫 **cgo**,可以从 Go 调用 C 代码,甚至 Go 的标准库在多个地方使用了它。例如,在 **net** 包中,它使用标准的 C 库来处理 DNS。
默认情况下,导入此类包或在代码中使用 cgo 会生成一个动态链接的二进制文件,链接到那些 **libc** 库。
## 我们可以让它静态链接吗?
当你希望二进制文件静态链接时,可能有多种原因,但主要原因是为了简化部署和分发。然而,并不总是有必要这样做。通过链接 **libc**,你可以从主机更新中受益,并且在使用 **net** 包的情况下,可以利用 **libc** 中包含的复杂的 DNS 查找函数。
有趣的是,Go 的 net 包也有一个纯 Go 版本,这使得在编译时禁用 cgo 成为可能。你可以通过指定构建标签或使用 **CGO\_ENABLED=0** 完全禁用 cgo 来实现。
## 内部链接器 vs 外部链接器
链接器是一个程序,它读取 main 包的 Go 存档或对象,以及它的依赖项,并将它们组合成一个可执行的二进制文件。
默认情况下,Go 的工具链使用其内部链接器(go tool link),但是你可以在编译时指定使用哪个链接器,这样可以让我们在获得静态二进制文件的同时,享受完整的 libc 功能。
在 Linux 上,默认的外部链接器是 gcc 的 **ld**。我们可以告诉它生成一个静态二进制文件。
```
# go build -ldflags "-linkmode 'external' -exldflags '-static'" main.go
```
它能工作,但我们会收到一个警告。在我们的例子中,**glibc** 使用 **libnss** 来支持多种地址解析服务提供者,而你无法静态链接 libnss。
其他使用 cgo 的包可能会产生类似的警告,你需要查看文档来判断它们是否严重。
## 交叉编译
如介绍中所述,交叉编译是 Go 的一个非常好的特性,它允许你为几乎任何平台/架构编译程序。然而,如果你的程序使用了 **cgo**,这可能会非常棘手,因为交叉编译 C 代码通常很困难。
你可以通过为目标操作系统和/或架构安装工具链来解决这个问题。
如果可能,最好在交叉编译时不要使用 **cgo**。你将得到静态链接的稳定二进制文件。
## 加分项:减小二进制文件大小
你可能注意到,上面 **file** 命令的输出包含:“not stripped”。这意味着我们的二进制文件中包含调试信息。但我们通常不需要它,删除它可以减小二进制文件的大小。
```
# go build -ldflags="-w -s" main.go
```
这将删除调试信息和符号表,减小二进制文件的大小。
## 当心:LD_PRELOAD 技巧
Linux 系统程序 ld-linux.so(动态链接器/加载器)使用 **LD\_PRELOAD** 来加载指定的共享库。特别是,在加载任何其他库之前,动态加载器将首先加载 **LD\_PRELOAD** 中的共享库。
**LD\_PRELOAD** 技巧是在动态链接的二进制文件中使用的一种强大技术,用于覆盖或拦截对共享库的函数调用。
通过将 **LD\_PRELOAD** 环境变量设置为指向自定义的共享对象文件,用户可以将自己的代码注入程序的执行中,从而有效地替换或增强现有的库函数。
这种方法允许各种应用,例如调试、测试,甚至在不修改原始源代码的情况下改变程序行为。
```
LD_PRELOAD=/path/to/my/malloc.so /bin/ls
```
这也表明**静态链接的二进制文件**更安全,因为它们不存在这个问题,因为它们不依赖任何外部库。此外,还有一个“**安全执行模式**”——这是由 Linux 系统上的动态链接器实现的安全特性,用于在运行需要提升权限的程序时限制某些行为。
- 共 0 条回复
- 需要登录 后方可回复, 如果你还没有账号请点击这里注册。
wiseAI
✨ 梦初醒 茅塞开
- 不经他人苦,莫劝他人善。
- 能量足,心态稳,温和坚定可以忍。
- 辛苦决定不了收入,真正决定收入的只有一个,就是不可替代性。
- 要么忙于生存,要么赶紧去死!
- 内心强大到混蛋,比什么都好!
- 规范流程比制定制度更重要!
-
立志需要高远,但不能急功近利;
行动需要迅速,却不可贪图速成。 - 不要强求人品,要设计高效的机制。
-
你弱的时候,身边都是鸡零狗碎;
你强的时候,身边都是风和日丽。 - 机制比人品更可靠,契约比感情更可靠。
- 合作不意味着没有冲突,却是控制冲突的最好方法。
- 误解是人生常态,理解本是稀缺的例外。
- 成功和不成功之间,只差一次坚持!
- 祁连卧北雪,大漠壮雄关。
- 利益顺序,过程公开,机会均等,付出回报。
