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) 函式庫建置範例與筆記
沒有留言: