blob: 1919754e2ea3a7b9b705248643d07f1d3d00a2b8 [file] [log] [blame] [view]
Andrew Gerrand5bc444d2014-12-10 11:35:11 +11001# Introduction
2This page documents some less well-known (perhaps advanced) tricks for the ` gc ` toolchain (and the Go tool).
3
4# C code without ` cgo `
Andrew Gerrand5bc444d2014-12-10 11:35:11 +11005
6### Use ` syso ` file to embed arbitrary self-contained C code
7Basically, you write your assembly language in GNU as(1) format, but make sure
Ian Lance Taylora83b0422017-01-06 14:56:39 -08008all the interface functions are using Go's ABI (everything on stack, etc., please read [Go 1.2 Assembler Introduction](https://golang.org/doc/asm) for more details).
Andrew Gerrand5bc444d2014-12-10 11:35:11 +11009
10The most important step is compiling that file to file.syso (` gcc -c -O3 -o file.syso file.S `),
11and put the resulting syso in the package source directory.
12And then, suppose your assembly function is named Func, you need one stub
Ian Lance Taylora83b0422017-01-06 14:56:39 -080013[cmd/asm](https://golang.org/cmd/asm) assembly file to call it:
Andrew Gerrand5bc444d2014-12-10 11:35:11 +110014```
15TEXT ·Func(SB),$0-8 // please set the correct parameter size (8) here
16 JMP Func(SB)
17```
18then you just declare Func in your package and use it, go build will be able to
19pick up the syso and link it into the package.
20
21Notes:
22 * The binary produced won't use cgo, and the overhead is just an unconditional JMP that could be perfectly branch predicted. But, please be aware that because it doesn't use cgo, your assembly function is running on Go stack, and it **shouldn't use too much stack** (a safe value is less than ~100 bytes) or terrible things will happen. For compute kernels, this requirement isn't too restricting.
23 * Please make sure you‘ve included all library dependencies in your C code. ` libc ` is not available, and most notably, ` libgcc ` is also not available (esp. when you're using gcc ` __builtin_funcs `, please use ` nm(1) ` to double-check that your file doesn't contain any undefined symbols).
24 * It's also possible to call back Go functions from C code, but this is left as an exercise for the reader.
25 * this trick is supported on all Go 1.x releases.
26 * the Go linker is pretty capable in that you just need to prepare .syso file for each architecture, not for each OS/Arch combination (assuming you don't use OS-specific constructs, obviously), and the Go linker is perfectly capable to link, for example, Mach-O object files into ELF binaries. So be sure to name your syso file with names like ` file_amd64.syso `, ` file_386.syso `.
27
28# Bundle data into Go binary
29There are a lot of ways to bundle data in Go binary, for example:
30 * ` zip ` the data files, and append the zip file to end of Go binary, then use ` zip -A prog ` to adjust the bundled zip header. You can use ` archive/zip ` to open the program as a zip file, and access its contents easily. There are existing packages that helps with this, for example, https://godoc.org/bitbucket.org/tebeka/nrsc; This requires post-processing the program binary, which is not suitable for non-main packages that require static data. Also, you must collect all data files into one zip file, which means that it's impossible to use multiple packages that utilize this method.
nathanya42a2712014-12-10 22:08:31 -080031 * Embed the binary file as a ` string ` or ` []byte ` in Go program. This method is not recommended, not only because the generated Go source file is much larger than the binary files themselves, also because static large ` []byte ` slows down the compilation of the package and the ` gc ` compiler uses a lot of memory to compile it (this is a known bug of ` gc `). For example, see the [tools/godoc/static](https://godoc.org/golang.org/x/tools/godoc/static) package.
Andrew Gerrand5bc444d2014-12-10 11:35:11 +110032 * use similar ` syso ` technique to bundle the data. Precompile the data file as syso file using GNU ` as(1) `'s ` .incbin ` pseudo-instruction.
33
34The key trick for the 3rd alternative is that the linker for the ` gc ` toolchain has the ability to link COFF object files of a different architecture into the binary without problem, so you don't need to provide syso files for all supported architectures. As long as the syso file doesn't contain instructions, you can just use one to embed the data.
35
36The assembly template to generate the COFF .syso file:
37```
38/* data.S, as -o data.syso */
39.section .rdata,"dr" /* put in COFF section .rdata */
40.globl _bindataA /* no longer need to prepend package name here */
41.globl _ebindataA
42_bindataA:
43.incbin "dataA"
44_ebindataA:
45
46.globl _bindataB /* no longer need to prepend package name here */
47.globl _ebindataB
48_bindataB:
49.incbin "dataB"
50_ebindataB:
51```
52
53And two other files, first a Plan 9 C source file that assembles the slice for Go:
54```
55/* slice.c */
56#include "runtime.h"
57extern byte _bindataA[], _bindataB[], _ebindataA, _ebindataB;
58
59void ·getDataSlices(Slice a, Slice b) {
60 a.array = _bindataA;
61 a.len = a.cap = &_ebindataA - _bindataA;
62 b.array = _bindataB;
63 b.len = b.cap = &_ebindataB - _bindataB;
64 FLUSH(&a);
65 FLUSH(&b);
66}
67```
68
69And finally, the Go file that uses the embedded slide:
70```
71/* data.go */
72package bindata
Dave Day0d6986a2014-12-10 15:02:18 +110073
Andrew Gerrand5bc444d2014-12-10 11:35:11 +110074func getDataSlices() ([]byte, []byte) // defined in slice.c
75
76var A, B = getDataSlices()
77```
78
79Note: you will need an ` as(1) ` capable of generating the COFF syso file, you
80can build one easily on Unix:
81```
82wget http://ftp.gnu.org/gnu/binutils/binutils-2.22.tar.bz2 # any newer version also works
83tar xf binutils-2.22.tar.bz2
84cd binutils-2.22
85mkdir build; cd build
86../configure --target=i386-foo-pe --enable-ld=no --enable-gold=no
87make
88# use gas/as-new to assemble your data.S
89# all the other file could be discarded.
90```
91
92**Drawback** of this issue is that it seems incompatible to cgo, so only use it when you don't
Ian Lance Taylor6ae0d362017-01-06 15:12:43 -080093use cgo, at least for now. I (minux) is working on figuring out why they're incompatible.
94
95# Including build information in the executable
96
97The gc toolchain linker, [cmd/link](https://golang.org/cmd/link), provides a `-X` option that may be used to record arbitrary information in a Go string variable at link time. The format is `-X importpath.name=val`. Here `importpath` is the name used in an import statement for the package (or `main` for the main package), `name` is the name of the string variable defined in the package, and `val` is the string you want to set that variable to. When using the go tool, use its `-ldflags` option to pass the `-X` option to the linker.
98
99Let's suppose this file is part of the package `company/buildinfo`:
100
101```
102package buildinfo
103
104var BuildTime string
105```
106
Joe Shawf7b9c512017-03-23 09:40:12 -0400107You can build the program using this package using `go build -ldflags="-X 'company/buildinfo.BuildTime=$(date)'"` to record the build time in the string. (The use of `$(date)` assumes you are using a Unix-style shell.)
Ian Lance Taylor6ae0d362017-01-06 15:12:43 -0800108
Cuong Manh Le58610692018-06-26 13:39:22 +0700109The string variable must exist, it must be a variable, not a constant, and its value must not be initialized by a function call. There is no warning for using the wrong name in the `-X` option. You can often find the name to use by running `go tool nm` on the program, but that will fail if the package name has any non-ASCII characters, or a `"` or `%` character.