golang的build命令编译

59   /   0   /   0   /   0   /   发布于 1年前
`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 条回复
  • 需要登录 后方可回复, 如果你还没有账号请点击这里注册
梦初醒 茅塞开
  • 不经他人苦,莫劝他人善。
  • 能量足,心态稳,温和坚定可以忍。
  • 辛苦决定不了收入,真正决定收入的只有一个,就是不可替代性。
  • 要么忙于生存,要么赶紧去死!
  • 内心强大到混蛋,比什么都好!
  • 规范流程比制定制度更重要!
  • 立志需要高远,但不能急功近利;
    行动需要迅速,却不可贪图速成。
  • 不要强求人品,要设计高效的机制。
  • 你弱的时候,身边都是鸡零狗碎;
    你强的时候,身边都是风和日丽。
  • 机制比人品更可靠,契约比感情更可靠。
  • 合作不意味着没有冲突,却是控制冲突的最好方法。
  • 误解是人生常态,理解本是稀缺的例外。
  • 成功和不成功之间,只差一次坚持!
  • 祁连卧北雪,大漠壮雄关。
  • 利益顺序,过程公开,机会均等,付出回报。