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,您需要一個 stub cmd/asm 組合語言檔案來呼叫它

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 檔案不包含指令,你就可以只使用一個來嵌入資料。

產生 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:

另外兩個檔案,第一個是 Plan 9 C 原始碼檔案,用來組裝 Go 的切片

/* 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 的一部分。