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アセンブリファイルが1つ必要です。

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

その後、パッケージでFuncを宣言して使用すれば、go buildはsysoを取得してパッケージにリンクすることができます。

注意事項

Goバイナリにデータをバンドルする

Goバイナリにデータをバンドルする方法はたくさんあります。たとえば、

3番目の代替手段の重要なトリックは、`gc`ツールチェーンのリンカが異なるアーキテクチャのCOFFオブジェクトファイルを問題なくバイナリにリンクできることです。そのため、サポートされているすべてのアーキテクチャのsysoファイルを提供する必要はありません。sysoファイルに命令が含まれていない限り、1つを使用してデータを埋め込むことができます。

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:

さらに2つのファイル。まず、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は、リンク時にGo文字列変数に任意の情報を記録するために使用できる`-X`オプションを提供します。形式は`-X importpath.name=val`です。ここで、`importpath`はパッケージ(またはメインパッケージの場合は`main`)のimportステートメントで使用される名前、`name`はパッケージで定義されている文字列変数の名前、`val`はその変数に設定する文字列です。goツールを使用する場合は、`-ldflags`オプションを使用して`-X`オプションをリンカに渡します。

このファイルが`company/buildinfo`パッケージの一部であるとしましょう。

package buildinfo

var BuildTime string

`go build -ldflags="-X 'company/buildinfo.BuildTime=$(date)''"`を使用してプログラムをビルドし、ビルド時間を文字列に記録できます。(`$(date)`の使用は、Unixスタイルのシェルを使用していると仮定しています。)

文字列変数は存在する必要があり、定数ではなく変数である必要があり、その値は関数呼び出しによって初期化されてはなりません。`-X`オプションで間違った名前を使用しても警告はありません。使用する名前は、プログラムで`go tool nm`を実行することで見つけることができますが、パッケージ名にASCII以外の文字、または`"`文字や`%`文字が含まれている場合は失敗します。


このコンテンツは、Go Wikiの一部です。