Go Wiki:GcToolchainTricks

此页面记录了一些鲜为人知(可能高级)的 gc 工具链(和 Go 工具)技巧。

不使用 cgo 的 C 代码

使用 syso 文件嵌入任意自包含的 C 代码

基本上,您使用 GNU as(1) 格式编写汇编语言,但确保所有接口函数都使用 Go 的 ABI(所有内容都在堆栈上,等等,请阅读 Go 1.2 汇编器简介 了解更多详情)。

最重要的一步是将该文件编译到 file.syso(gcc -c -O3 -o file.syso file.S),并将生成 syso 放到包源目录中。然后,假设你的汇编函数名为 Func,你需要一个存根 cmd/asm 汇编文件来调用它

TEXT ·Func(SB),$0-8 // please set the correct parameter size (8) here
    JMP Func(SB)

然后你只需在包中声明 Func 并使用它,go build 将能够获取 syso 并将其链接到包中。

注释

将数据打包到 Go 二进制文件中

有很多方法可以将数据打包到 Go 二进制文件中,例如

第三种替代方法的关键技巧是,gc 工具链的链接器能够将不同架构的 COFF 对象文件链接到二进制文件中而不会出现问题,因此您不必为所有受支持的架构提供 syso 文件。只要 syso 文件不包含指令,您就可以只使用一个来嵌入数据。

生成 COFF .syso 文件的汇编模板

/* data.S, as -o data.syso */
.section .rdata,"dr" /* put in COFF section .rdata */
.globl _bindataA /* no longer need to prepend package name here */
.globl _ebindataA
_bindataA:
.incbin "dataA"
_ebindataA:

.globl _bindataB /* no longer need to prepend package name here */
.globl _ebindataB
_bindataB:
.incbin "dataB"
_ebindataB:

还有另外两个文件,第一个是为 Go 汇编切片的 Plan 9 C 源文件

/* slice.c */
#include "runtime.h"
extern byte _bindataA[], _bindataB[], _ebindataA, _ebindataB;

void ·getDataSlices(Slice a, Slice b) {
  a.array = _bindataA;
  a.len = a.cap = &_ebindataA - _bindataA;
  b.array = _bindataB;
  b.len = b.cap = &_ebindataB - _bindataB;
  FLUSH(&a);
  FLUSH(&b);
}

最后,使用嵌入幻灯片的 Go 文件

/* data.go */
package bindata

func getDataSlices() ([]byte, []byte) // defined in slice.c

var A, B = getDataSlices()

注意:您将需要一个能够生成 COFF syso 文件的 as(1),您可以在 Unix 上轻松构建一个

wget http://ftp.gnu.org/gnu/binutils/binutils-2.22.tar.bz2   # any newer version also works
tar xf binutils-2.22.tar.bz2
cd binutils-2.22
mkdir build; cd build
../configure --target=i386-foo-pe --enable-ld=no --enable-gold=no
make
# use gas/as-new to assemble your data.S
# all the other file could be discarded.

此问题的缺点是它似乎与 cgo 不兼容,因此仅在不使用 cgo 时使用它,至少目前是这样。我(minux)正在努力找出它们不兼容的原因。

在可执行文件中包含构建信息

gc 工具链链接器 cmd/link 提供了一个 -X 选项,可用于在链接时将任意信息记录到 Go 字符串变量中。格式为 -X importpath.name=val。其中 importpath 是包的导入语句中使用的名称(或主包的 main),name 是包中定义的字符串变量的名称,val 是您要将该变量设置为的字符串。使用 go 工具时,使用其 -ldflags 选项将 -X 选项传递给链接器。

假设此文件是包 company/buildinfo 的一部分

package buildinfo

var BuildTime string

您可以使用 go build -ldflags="-X 'company/buildinfo.BuildTime=$(date)'" 使用此包构建程序,以在字符串中记录构建时间。(使用 $(date) 假设您使用的是 Unix 风格的 shell。)

字符串变量必须存在,它必须是一个变量,而不是常量,并且其值不能由函数调用初始化。在 -X 选项中使用错误的名称没有警告。您通常可以通过对程序运行 go tool nm 来找到要使用的名称,但如果包名称有任何非 ASCII 字符或 "% 字符,则该方法将失败。


此内容是 Go Wiki 的一部分。