title: 【転載】Golang コンパイルの最適化における静的リンク
date: 2022-05-26 14:34:00
toc: false
index_img: https://puep.qpic.cn/coral/Q3auHgzwzM4fgQ41VTF2rAOrGvBASaprDnwukcq5GutBULkN9WJT9A/0
category:
- Go
tags: - リソース
- Java
- プロジェクト
- ファイル
- Golang
- 解決
- ソリューション
- フォーマット
- 異なる
- 発生
元の記事:Golang コンパイルの最適化における静的リンク - FranzKafka Blog。このサイトは、保存とフォーマットの調整のためにのみ使用され、営利目的の商業行為は行われません。
最近、Golang の学習を始めましたが、Golang のいくつかの特徴に非常に満足しています。Python が台頭したときに似たような言葉を思い出します - 人生は短いので、私は Python を使います。ただし、Python は解釈型言語に過ぎません。真のプログラマーとして、コンパイル型言語を書かないことは合理的ではありません。したがって、私は一方的に(冗談ですが)、コンパイル型言語を書かないプログラマーは適格なプログラマーではありませんと宣言します。さて、Golang 自体に戻ると、同様の文を言えます:バックエンドは苦しいですが、私は Golang を使います〜
約半月前、私は Golang の基礎知識を簡単に学び、自分の最初の Golang プログラムを書き始めました。言語の特徴、パブリックおよびプライベートの定義、Java と似たようなインターフェース、組み込みのフォーマットツール、非常に豊富な標準ライブラリと多くのサードパーティライブラリ、効率的なコンパイル、テスト、および実行など、すべてが私を C++ の主力選択に疑問を抱かせました。このものはとても良いので、なぜ C++ を書くのですか!
その後、小さなオープンソースプロジェクトに参加し、いくつかの機能を追加しました。コンパイルして実行してみたところ、問題はありませんでしたので、リリースを安心して公開しました。しかし、多くの人々から、古いバージョンのシステムでエラーが発生するとのフィードバックがありました。エラーメッセージは「GLIBC_2.28 が見つかりません」というものでした。しばらく Google で検索した結果、同様の問題が実際に多く存在することがわかりました。そこで、この問題を解決する方法を考えました。Google で多くの回答が見つかりましたが、基本的には 2 つの方法しかありませんでした:システムのアップグレードまたは glibc の依存関係のアップグレードです。これらの 2 つの選択肢を選びたくありませんでした(手間がかかるため)。問題全体を解決するための他の方法はないでしょうか。この記事は、この問題に対する記録です〜
背景:なぜエラーが発生するのか#
解決策を見つける前に、まずなぜこのエラーが発生するのかを理解する必要があります。エラーメッセージ自体から始めましょう。Glibc は Linux カーネルの実行に依存する基本的なコンポーネントライブラリであり、多くの標準 API インターフェースを提供しています。これらの標準インターフェースには、open、read、write、malloc、printf、dlopen などが含まれます。その重要性は想像に難くありません。
異なる Linux ディストリビューションの間で、Glibc のバージョンは異なります。これらの異なるバージョンが提供する API 機能も異なることは自然です。しかし、これが go プログラムのエラーにどのように関連しているのでしょうか。ここで、別のトピックを紹介する必要があります - CGO です。
CGO は、Go 言語で C 関数を呼び出すために設計されたものです。Linux または Unix カーネルは C 言語で実装されていることを皆さんも知っていると思います。C/C++ は多年の発展を経て、非常に包括的なエコシステムを持ち、多くの標準 API を備えています。したがって、Go 言語はこれらの API または C プログラムをラップしたものを直接利用することを考えました。このようなメカニズムを実現するためには、CGO を介する必要があります。以下は CGO の使用例です:
package cgoexample
/*
#include <stdio.h>
#include <stdlib.h>
void myprint(char* s) {
printf("%s\n", s);
}
*/
import "C"
import "unsafe"
func Example() {
cs := C.CString("Hello from stdio\n")
C.myprint(cs)
C.free(unsafe.Pointer(cs))
}
CGO を介して C が提供する標準 API インターフェースを呼び出す場合、Glibc ライブラリのバージョンの違いによる標準 API インターフェースの実装の違いに遭遇する可能性があります。これは Go プログラムにとって望ましくありません。そのため、CGO を使用する多くの標準ライブラリ(net 標準ライブラリ、os/user 標準ライブラリなど)では、Glibc のバージョンの要件が存在します。
エラーメッセージでは、このプログラムが Glibc 2.28 バージョンに依存する必要があることがわかります。したがって、ldd --version コマンドを使用してローカルマシンの Glibc バージョンを確認できます。
問題のあるシステムでは、Glibc のバージョンが 2.23 であることがわかります。しかし、コンパイルした go プログラムは 2.28 以上の Glibc バージョンを必要としています。Glibc のバージョンが低すぎるため、プログラムは正常に実行されず、エラーメッセージが表示されます。
解決策:エラーの解決方法#
ここではいくつかの解決策を選択できます。
解決策 1:最新のシステムにアップグレードすることです。一般的に、主要な Linux ディストリビューションは最新のバージョンをリリースする際に最新の Glibc バージョンを統合していますので、システムをアップグレードすることで最新の Glibc バージョンを入手できます。以下の表には、いくつかの人気のある Linux ディストリビューションに統合された Glibc バージョンが示されています:
glibc | rhel | centos | sle | debian | ubuntu | kylin | manylinux |
---|---|---|---|---|---|---|---|
2021/2/1 | 2.33 | 21.04 hirsute | |||||
2.32 | 20.10 groovy | ||||||
2020/2/1 | 2.31 | 11 bullseye | 20.04 LTS focal | ||||
2.3 | 19.10 eoan | ||||||
2.29 | 19.04 disco | ||||||
2018/8/1 | 2.28 | 8.3 Ootpa | 8.2.2004 | 10 buster | 18.10 cosmic | V10 Tercel | |
2018/2/1 | 2.27 | 18.04 LTS bionic | |||||
2.26 | 17.10 artful | ||||||
2.26 | 15-SP2 | ||||||
2016/8/4 | 2.24 | 9 stretch | 17.04 zesty | ||||
16.10 yakkety |
表からわかるように、Glibc 2.28 以上のバージョンを使用するには、CentOS 8.1、Fedora 30、Gentoo - 2020-01-22、Arch Linux 2020.01.22、ubuntu 18.10、Debian 8 のいずれかのシステムが必要です。
解決策 2:Glibc のバージョンをアップグレードすることです。実際、これはおすすめできない方法です。なぜなら、Glibc は Linux システムの非常に重要なコンポーネントであり、アップグレードまたはダウングレードのいずれかがシステムの正常な動作に影響を与える可能性があるからです。もちろん、特定の状況では、Glibc を個別にアップグレードして試すことができます。
解決策 3:go プログラムを静的リンク形式でコンパイルすることです。クロスコンパイル以外の場合、デフォルトでは CGO が有効になっており、コンパイルされたプログラムは動的リンク形式で実行可能ファイルが生成されます。この情報を確認するために、file コマンドや ldd コマンドを使用することができます。以下はその例です:
動的リンクは、共有ライブラリを動的にロードする形式でリンクするため、ソフトウェア自体のサイズを減らし、最大限のソフトウェアの再利用を実現するためのものですが、通常、ホストシステムの動的リンクされた共有ライブラリのバージョンに要件があります。一方、静的リンクは、コンパイル時にシステムに含まれる共有ライブラリをリソースとしてリンクし、実行時にはシステムの共有ライブラリに依存しません。
したがって、この問題に対しては、完全な静的リンク形式でコンパイルすることができます。
次のコマンドを使用して CGO を有効にし、静的リンクでコンパイルします:
CGO_ENABLED=1 go build -trimpath -ldflags='-extldflags=-static' -tags musl,osusergo,netgo,sqlite_omit_load_extension -o output -v main.go
コンパイルが成功したら、コマンドを使用して確認すると、すでに静的にリンクされていることがわかります。
その後、この実行ファイルを古いシステムに配置して実行すると、正常に動作することが確認できます。
参考資料: