Go with Cgo 靜態連結 (static linking) 函式庫建置範例與筆記
延續上一篇 Go with Cgo 動態連結的文章,由於部署 (deployment) 需求的改變,合作廠商 IT 運維技術人員 (Ops) 對於 Linux 的不熟悉,期望部署不需要管理那些動態連結 (dynamic linking) 函式庫,因而開始研究改用靜態連結 (static linking) C++ 函式庫的可能性。
關於範例
延續上一篇文章,調整其範例函式庫,其標頭檔 example.h:
#ifndef EXAMPLE_H__
#define EXAMPLE_H__
#ifdef WINDOWS
#define API __declspec(dllexport)
#else
#define API extern
#endif
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
API void Sort(int* nums, int n);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // EXAMPLE_H__
以及其實作:
#include "example.h"
#include <algorithm>
API void Sort(int* nums, int n)
{
std::sort(nums, nums + n);
}
依照上一篇流程,分別在 Windows 以及 Linux 平台,建置成動態連結函式庫 (dynamic library),產生函式庫 example.x64.dll 以及 libexample.so 。
Go 使用該函式庫的程式碼範例:
package main
import (
"fmt"
"unsafe"
)
/*
#cgo windows LDFLAGS: -lexample.x64
#cgo windows CFLAGS: -DWINDOWS
#cgo linux LDFLAGS: -lexample
#cgo LDFLAGS: -L"${SRCDIR}/libs"
#cgo CFLAGS: -I"${SRCDIR}/includes"
#include "example.h"
*/
import "C"
func Sort(nums []int32) {
C.Sort((*C.int)(unsafe.Pointer(&nums[0])), C.int(len(nums)))
}
func main() {
nums := []int32{0, 5, 9, 7, 1, 3, 4, 2, 8, 6}
println("Origin:", fmt.Sprint(nums))
Sort(nums)
println("Sorted:", fmt.Sprint(nums))
}
靜態連結先前準備 (prepare for static linking)
修改原先的專案檔,改成建置為靜態函式庫 (static library) 。
在 Windows 以及 Linux 平台分別建置為 example.x64.lib 以及 libexample.a。
全靜態連結 (Full static linking)
在 linker 階段全部改用靜態連結:
$ go build -ldflags="-extldflags '-static -lstdc++'"
可能會出現以下的連結錯誤:
example.cpp:(.text.startup+0xa): undefined reference to `std::ios_base::Init::Init()'
example.cpp:(.text.startup+0x19): undefined reference to `std::ios_base::Init::~Init()'
collect2: error: ld returned 1 exit status
必須加入參數告知 linker 連結 stdc++ 函數庫,具體作法可修改 Go 程式碼,加入 Cgo 參數宣告:
#cgo LDFLAGS: -lstdc++
或者是建置指令加入參數:
$ go build -ldflags="-extldflags '-static -lstdc++'"
另外在 Linux 系統可會遇到找不到函式庫的錯誤:
ld: cannot find -lpthread
ld: cannot find -lc
主要是因為系統沒有該些靜態連結函式庫 (stdc++),因此安裝下載便可以正常編譯:
$ yum -y install glibc-static
但是會出現警告:
/lib/../lib64/libpthread.a(libpthread.o): In function `sem_open':
(.text+0x6a23): warning: the use of `mktemp' is dangerous, better use `mkstemp'
從網路討論中,結論是不建議將標準函式庫 (standard library, e.g. stdc++) 靜態連結,除非知道自己在做些什麼。
函式庫靜態連結 (partially static linking)
修改靜態連結的作法,不再是全部靜態連結,而是只有指定函式庫靜態連結,其他則還是動態連結。
第一種方式,使用 -l: 指定函式庫名稱,強迫連結靜態函式庫:
#cgo windows LDFLAGS: -l:example.x64.lib
#cgo linux LDFLAGS: -l:libexample.a
#cgo LDFLAGS: -lstdc++
第二種方式,使用 -Wl,-Bstatic 宣告,告知 linker 切換靜態/ 動態連結:
#cgo windows LDFLAGS: -Wl,-Bstatic -lexample.x64
#cgo linux LDFLAGS: -Wl,-Bstatic -llibexample
#cgo LDFLAGS: -Wl,-Bdynamic -lstdc++
#cgo LDFLAGS: -lexample,linker 會優先尋找靜態函式庫
動態連結檢查
在 Windows 環境,可使用 Dependency Walker。
在 Linux 環境下,可使用 ldd 來檢查:
$ ldd example
系列筆記
- 在 Windows 環境建置動態連結函式庫 (Dynamic-link library),使用 MinGW gcc/g++ 以及 CodeBlock
- Go with Cgo 動態連結 (dynamic linking) 函式庫建置範例與筆記
- Go with Cgo 靜態連結 (static linking) 函式庫建置範例與筆記
, from golang.org, licensed under Creative Commons Attribution 3.0 License. [Gopher](https://github.com/golang/go/blob/master/doc/gopher/ref.png), was designed by Renee French, The design is licensed under the Creative Commons 3.0 Attributions license. Go](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi24HgsjnW0lnWe_MWPiEdB7W4v_XHObWmWdX4iaW0R1TBYFxJNoeeitrajSfsFnm8a8Sxpa7SKpvq5iMRybu6RcxSwElfCLM2XI0s38q4d9ZdlF67XsffghDI0RRW6KlT_-Qtfuq7DOzg/s780/golang-go-gopher.png)

沒有留言: