ELF 共用物件需求
在以 ELF 為基礎的系統上,例如 Linux 和 Fuchsia
共用物件 (即 shared_library()
和 loadable_module()
目標
GN 語言) 必須使用 -fPIC
編譯器和連接器選項建構。
這與使用 -fPIE
的可執行程式碼不同。這會產生
程式碼小於 -fPIC
,但無法用於共用
如需儲存大量結構化物件
建議使用 Cloud Bigtable
Fuchsia 版本不支援為 Linux、 但是對 Fuchsia 來說,它會定義個別要編譯的工具鍊執行個體 執行檔和共用程式庫
這個獨立的工具鍊稱為 "shlib"工具鍊或甚至
內部建構規則中的隨附工具鍊,而且一律會由
在「基本工具鍊」標籤中加上 -shared
後置字串。例如:
//build/toolchain/fuchsia:x64-shared
是底座的 shlib 工具鍊
//build/toolchain/fuchsia:x64
工具鍊。
ELF 共用資料庫重新導向
Fuchsia 版本實作的功能可確保 ELF shared_library()
和 loadbable_module()
目標一律會建構在 shlib 工具鍊中
如上一節所定義
Fuchsia 開發人員不需要瞭解,因為這是
透明:照常編寫 shared_library()
目標,不需要
非常擔心 ELF / 非 ELF
本節接下來將說明實作方式,第一項需求為 並參考幾個實際範例,瞭解這麼做的重要性:
請考慮可以連結的 C++ 靜態程式庫 (例如 libutil
)
到執行檔或共用程式庫物件由於這兩種二進位檔
要求程式碼必須使用不同的編譯器旗標建構,其中一種方法
是定義兩個目標,如下所示:
# A variant of the library to be linked into executables.
static_library("libutil-static") {
sources = [ ... ]
...
}
# A variant of the library to be linked into shared libraries.
static_library("libutil-shared") {
sources = [ ... ]
...
if (is_fuchsia) {
cflags = [ "-fPIC" ] # Required for shared library code on Fuchsia.
}
}
executable('program') {
sources = [ ... ]
deps = [ ":libutil-static" ]
}
shared_library('foo') {
sources = [ ... ]
deps = [ ":libutil-shared" ]
}
這個方法雖然有效,但有幾個缺點:
程式庫需要兩個目標定義,而非一個 就需要保持同步狀態即使沒有建構 Fuchsia 二進位檔。
明確的
is_fuchsia
檢查和編譯器標記,額外需求 每個共用變化版本定義中 並降低這些定義的抽象化程度(其中有些可以用 GN 設定簡化,不過還是可以)。
凡是使用程式庫的目標都必須選取 兩個變化版本
如果想讓內容變得更加寫實,可以想想
libutil
程式庫也依附於另一個 liblog
靜態資料庫。
後者也需要同時提供靜態和共用的變化版本
如:
static_library("liblog-static") {
sources = [ ... ]
...
}
static_library("liblog-shared") {
sources = [ ... ]
if (is_fuchsia) {
cflags = [ "-fPIC" ]
}
}
static_library("libutil-static") {
...
deps = [ ":liblog-static" ]
}
static_library("libutil-shared") {
...
if (is_fuchsia) {
cflags = [ "-fPIC" ] # Required for shared library code.
}
deps = [ ":liblog-shared" ]
}
... same as above
可以透過以下依附元件圖表來說明:
program ->
libutil-static ->
liblog-static
foo ->
libutil-shared ->
liblog-shared
確保這些目標定義和依附元件皆正確無誤 同步作業繁瑣,建構規則也變得既精簡又實用。
使用專用工具鍊建構 ELF 共用物件程式碼,可避免發生
libutil
及其依附元件的目標重複。例如:
# The following definition is global and should normally be put
# in the BUILDCONFIG.gn file
if (is_fuchsia) {
# Name of the toolchain used to build ELF shared library code.
# This toolchain adds the `-fPIC` option to all build commands
# by default so there is no need to add it in target definitions.
shlib_toolchain = "${current_toolchain}-shared"
} else {
# Shared library code can be built directly in the current toolchain
# on non-Fuchsia platforms.
shlib_toolchain = current_toolchain
}
# The following is part of a BUILD.gn file
static_library("liblog") {
...
}
static_library("libutil") {
...
deps = [ ":liblog" ]
}
executable('program') {
sources = [ ... ]
deps = [ ":libutil" ]
}
shared_library("foo") {
sources = [ ... ]
deps = [ ":libutils($shlib_toolchain)" ]
}
現在對應至依附關係圖:
program ->
libutil ->
liblog
foo ->
libutil($shlib_toolchain) ->
liblog($shlib_toolchain)
上述配置可解決大部分原始問題,原因如下:
明確的
is_fuchsia
檢查和-fPIC
編譯器標記新增 已經完全從 Static 程式庫中 定義。libutil
目標不必擔心哪個變化版本 所選取依附元件的結構
另一方面,每個 shared_library()
執行個體仍需要
仔細選取其靜態程式庫和來源集依附元件
從 shlib_toolchain
連結至適當的變化版本
再也不是件繁重乏味的工作
Fuchsia 版本使用最後一個技巧來解決最後一個問題: 在基本工具鍊中使用重新導向群組目標,以 參照 shblib 工具鍊中真正的共用資料庫目標, 如:
# Set to true if the current toolchain's target platform is based
# on ELF and requires an shlib toolchain. This would normally be
# defined in BUILDCONFIG.gn for Fuchsia toolchains.
_requires_shlib_toolchain = ...
if (_requires_shlib_toolchain && current_toolchain != shlib_toolchain) {
# A simple group that depends on the same target built in
# the shblib_toolchain. Note that `public_deps` instead of `deps`
# is required when crossing toolchain boundaries for proper
# linking.
group("bar") {
public_deps = [ ":bar($shlib_toolchain)" ]
}
} else {
# The target that actually builds the shared library in
# the shlib_toolchain, or the base one for non-Fuchsia platforms.
# It will pick its dependencies in the same toolchain context.
shared_library("bar") {
...
deps = [ ":libutil" ]
}
}
executable("program2") {
deps = [ ":bar" ]
}
完成之後,她會在 Fuchsia 中建立以下依附元件圖表:
program2 -->
bar --> # redirection group
bar(shblib_toolchain) --> # real shared_library()
libutil(shlib_toolchain)
非 Fuchsia 基本工具鍊則具備:
program2 -->
bar --> # real shared_library()
libutil
為了進一步簡化使用,Fchsia 會重新定義
在 BUILDCONFIG.gn
中有 shared_library()
個範本,隱藏這個項目
從 BUILD.gn
檔案寫入
是最自然的方式,例如:
### This would appear in BUILDCONFIG.gn to ensure that `shared_library()`
### does ELF shared library redirection automatically when needed.
if (_requires_shlib_toolchain) {
template("shared_library") {
if (current_toolchain != shlib_toolchain) {
group(target_name) {
public_deps = [ ":${target_name}(${shlib_toolchain})" ]
}
} else {
# Ensure the built-in shared_library() function is called.
target("shared_library", target_name) {
forward_variables_from(invoker, "*")
}
}
}
}
### This would appear in regular BUILD.gn files
# Invokes the custom `shared_library()` template instead
# of the built-in one. On Fuchsia systems, this will create a
# redirection group that refers to a real shared_library()
# target in the shlib_toolchain. On a non-Fuchsia system, this
# simply defines a real shared_library() target.
shared_library("bar") {
...
deps = [ ":libutil" ]
}
executable("program2") {
...
deps = [ ":bar" ]
}
詳情請參閱 BUILDCONFIG.gn
中的 shared_library()
定義