關於 web service, unity, blogger 等軟體工程筆記

Go with Cgo 靜態連結 (static linking) 函式庫建置範例與筆記

Edit icon 沒有留言
Go

延續上一篇 Go with Cgo 動態連結的文章,由於部署 (deployment) 需求的改變,合作廠商 IT 運維技術人員 (Ops) 對於 Linux 的不熟悉,期望部署不需要管理那些動態連結 (dynamic linking) 函式庫,因而開始研究改用靜態連結 (static linking) C++ 函式庫的可能性。

提示:Linux 測試環境 go version go1.10.1 linux/amd64 & gcc version 6.3.1 20170216 (Red Hat 6.3.1-3) (GCC);Windows 測試環境 go version go1.10.1 windows/amd64 & gcc version 7.3.0 (x86_64-posix-seh-rev0, Built by MinGW-W64 project)

關於範例

延續上一篇文章,調整其範例函式庫,其標頭檔 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)

修改靜態連結的作法,不再是全部靜態連結,而是只有指定函式庫靜態連結,其他則還是動態連結。

範例幾種的 linking 方式

幾種的 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

系列筆記

Reference

沒有留言: