[インデックス 10714] ファイルの概要
このコミットは、Go言語のリンカ(ld
、6l
、8l
)にNetBSDバイナリをサポートするための最初の変更を導入するものです。具体的には、NetBSD向けのELF(Executable and Linkable Format)バイナリ生成に必要な調整が行われています。
コミット
commit 420fe2292139123a31dbd90420417f63d4191c76
Author: Christopher Nielsen <m4dh4tt3r@gmail.com>
Date: Mon Dec 12 15:42:11 2011 -0500
ld/6l/8l: First pass at changes to the linker to support NetBSD binaries.
This will not currently create valid NetBSD binaries because NetBSD requires
an ELF note section to run, otherwise the kernel will throw ENOEXEC. I was
unable to determine an elegant way to add the section, so I am submitting
what I have.
References:
http://www.netbsd.org/docs/kernel/elf-notes.html
http://mail-index.netbsd.org/netbsd-bugs/2001/08/03/0012.html
R=rsc
CC=golang-dev
https://golang.org/cl/5472049
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/420fe2292139123a31dbd90420417f63d4191c76
元コミット内容
Go言語のリンカ(ld
、6l
、8l
)にNetBSDバイナリのサポートを追加するための最初の試みです。
現在のところ、この変更だけでは有効なNetBSDバイナリは作成されません。NetBSDでは、実行時にELF noteセクションが必要であり、これが存在しない場合、カーネルはENOEXEC
エラーを返します。コミットの作者は、このセクションをエレガントに追加する方法を見つけられなかったため、現状のコードを提出しています。
参照として、NetBSDのELFノートに関するドキュメントと、関連するメーリングリストの議論が挙げられています。
変更の背景
このコミットの主な背景は、Go言語がサポートするオペレーティングシステムとアーキテクチャの範囲を拡大することにあります。特に、NetBSDというUNIX系OS上でGoプログラムが実行可能なバイナリとして生成されるように、リンカの機能を拡張する必要がありました。
当時のGo言語のリンカは、Linux、FreeBSD、OpenBSD、Windowsなどの主要なOS向けのバイナリ生成に対応していましたが、NetBSDはまだ完全にサポートされていませんでした。NetBSDは、その移植性とクリーンな設計で知られており、多くの異なるハードウェアプラットフォームで動作します。Go言語がより広範な環境で利用されるためには、このようなOSへの対応が不可欠でした。
しかし、NetBSDのELFバイナリには、他のOSとは異なる特定の要件、特に「ELF noteセクション」の存在が求められます。このセクションは、カーネルがバイナリを正しく認識し、実行するために必要なメタデータを含んでいます。このコミットは、このNetBSD固有の要件に対応するための初期段階の作業であり、完全な解決には至っていないことが明記されています。
前提知識の解説
1. リンカ (Linker)
リンカは、コンパイラによって生成されたオブジェクトファイル(機械語コードとデータを含むファイル)を結合し、実行可能なプログラムやライブラリを作成するソフトウェアツールです。複数のオブジェクトファイルを結合する際に、未解決のシンボル(関数や変数の参照)を解決し、最終的なバイナリのメモリレイアウトを決定します。
Go言語においては、6l
(amd64アーキテクチャ向け)、8l
(386アーキテクチャ向け)などがリンカとして機能していました。これらはGoツールチェーンの一部であり、Goソースコードから最終的な実行ファイルを生成するビルドプロセスにおいて重要な役割を担います。
2. ELF (Executable and Linkable Format)
ELFは、UNIX系オペレーティングシステム(Linux、FreeBSD、NetBSD、OpenBSDなど)で広く使用されている、実行可能ファイル、オブジェクトファイル、共有ライブラリの標準ファイル形式です。ELFファイルは、プログラムのコード、データ、シンボルテーブル、デバッグ情報など、実行に必要なすべての情報を含んでいます。
ELFファイルは、ヘッダ、プログラムヘッダテーブル、セクションヘッダテーブル、および様々なセクションで構成されます。
- ELFヘッダ: ファイルの種類(実行可能ファイル、オブジェクトファイルなど)、ターゲットアーキテクチャ、OS ABI(Application Binary Interface)などの基本的な情報を含みます。
- プログラムヘッダテーブル: 実行時にメモリにロードされるセグメント(コード、データなど)の情報を記述します。
- セクションヘッダテーブル: ファイル内の各セクション(
.text
、.data
、.bss
など)の情報を記述します。
3. ELF Noteセクション
ELF noteセクション(.note
セクション)は、ELFファイルに任意の情報を埋め込むためのメカニズムです。これは、特定のOSやシステムがバイナリを認識し、適切に処理するために必要なメタデータを提供するためによく使用されます。
NetBSDの場合、カーネルがGoバイナリを正しく実行可能と判断するためには、特定の形式のELF noteセクションが存在する必要があります。このセクションには、通常、OSのバージョン情報やその他のシステム固有の識別子が含まれます。このコミットの時点では、GoリンカはこのNetBSD固有のELF noteセクションを生成する機能が不足しており、それがバイナリがENOEXEC
エラーで実行できない原因となっていました。
4. ダイナミックリンカ (Dynamic Linker / Interpreter)
ダイナミックリンカ(またはプログラムインタープリタ)は、共有ライブラリ(ダイナミックリンクライブラリ、DLL)を使用するプログラムを実行する際に、実行時に必要なライブラリをロードし、プログラムとリンクする役割を担うプログラムです。ELFバイナリのヘッダには、このダイナミックリンカのパスが記述されており、OSのローダーはこのパスを参照してダイナミックリンカを起動します。
各UNIX系OSには、それぞれ独自のダイナミックリンカが存在し、そのパスも異なります。例えば、Linuxでは/lib64/ld-linux-x86-64.so.2
、FreeBSDでは/libexec/ld-elf.so.1
、OpenBSDでは/usr/libexec/ld.so
、そしてNetBSDでは/libexec/ld.elf_so
といったパスが一般的です。このコミットでは、NetBSD向けのダイナミックリンカのパスが追加されています。
技術的詳細
このコミットは、Go言語のリンカがNetBSD向けのELFバイナリを生成できるようにするための複数の変更を含んでいます。
-
NetBSD固有の定数とパスの追加:
netbsddynld
という新しい変数が導入され、NetBSDのダイナミックリンカのパス(/libexec/ld.elf_so
または/usr/libexec/ld.elf_so
)が定義されています。これは、生成されるELFバイナリのPT_INTERP
セグメントに埋め込まれ、NetBSDシステムがバイナリを実行する際に使用するダイナミックリンカを指定します。ElfStrNoteNetbsdIdent
という新しいELF文字列定数が追加され、NetBSD固有の.note.netbsd.ident
セクション名がリンカに認識されるようになります。これは、NetBSDがバイナリを識別するために使用するELF noteセクションの名前です。
-
ELFヘッダのOS ABI設定:
- ELFヘッダの
EI_OSABI
フィールドは、バイナリがどのOS ABIに準拠しているかを示します。このコミットでは、HEADTYPE == Hnetbsd
(NetBSDターゲット)の場合にeh->ident[EI_OSABI]
がELFOSABI_NETBSD
に設定されるように変更されています。これにより、NetBSDカーネルがバイナリを正しく識別できるようになります。
- ELFヘッダの
-
リンカオプションの追加:
src/cmd/6l/doc.go
とsrc/cmd/8l/doc.go
に、新しいリンカオプション-Hnetbsd
が追加されています。これにより、ユーザーは明示的にNetBSD向けのバイナリを生成するようリンカに指示できるようになります。src/cmd/6l/obj.c
とsrc/cmd/8l/obj.c
のheaders
配列に"netbsd", Hnetbsd
が追加され、リンカが-Hnetbsd
オプションを認識し、対応する内部ヘッダタイプHnetbsd
にマッピングできるようになっています。
-
TLS (Thread Local Storage) 処理の調整:
src/cmd/6l/pass.c
では、TLSアクセスに関する変更が行われています。ELFシステムでは、TLSは通常FS
レジスタを基準としたオフセットでアクセスされます。このコミットでは、HEADTYPE == Hnetbsd
の場合も、Hlinux
、Hfreebsd
、Hopenbsd
と同様にFS
レジスタを使用するように調整されています。これは、NetBSDが他のELFベースのUNIX系OSと同様のTLSメカニズムを採用しているためです。
-
文字変換関数の修正:
src/cmd/cc/godefs.c
では、toupper
やtolower
といった文字変換関数を呼び出す際に、引数をuchar
にキャストする修正が行われています。これは、これらの関数がint
型の引数を期待し、char
型が符号付きである場合に負の値が渡されると未定義の動作を引き起こす可能性があるため、安全性を高めるための一般的なC言語の慣習です。
-
isalpha
関数の修正:src/cmd/ld/lib.c
では、isalpha
関数を呼び出す際に、引数をuchar
にキャストする修正が行われています。これもsrc/cmd/cc/godefs.c
と同様に、isalpha
がint
型の引数を期待し、char
型が符号付きである場合に負の値が渡されると未定義の動作を引き起こす可能性があるためです。
このコミットは、NetBSDバイナリ生成の基盤を築くものですが、前述の通り、ELF noteセクションの欠如により、この時点では完全な実行可能バイナリは生成されません。
コアとなるコードの変更箇所
このコミットにおける主要な変更は、Goリンカの各コンポーネント(6l
、8l
、ld
)がNetBSDを新しいターゲットOSとして認識し、それに応じたELFバイナリの構造を生成できるようにするためのものです。
src/cmd/6l/asm.c
および src/cmd/8l/asm.c
-
NetBSDダイナミックリンカパスの追加:
--- a/src/cmd/6l/asm.c +++ b/src/cmd/6l/asm.c @@ -44,6 +44,7 @@ char linuxdynld[] = "/lib64/ld-linux-x86-64.so.2"; char freebsddynld[] = "/libexec/ld-elf.so.1"; char openbsddynld[] = "/usr/libexec/ld.so"; +char netbsddynld[] = "/libexec/ld.elf_so";
(
8l/asm.c
では/usr/libexec/ld.elf_so
) -
ELF文字列定数
ElfStrNoteNetbsdIdent
の追加:--- a/src/cmd/6l/asm.c +++ b/src/cmd/6l/asm.c @@ -95,6 +96,7 @@ enum { ElfStrPlt, ElfStrGnuVersion, ElfStrGnuVersionR, + ElfStrNoteNetbsdIdent, NElfStr };
-
NetBSDターゲットの条件分岐追加:
doelf
関数やasmb
関数内で、HEADTYPE != Hnetbsd
のチェックが追加され、NetBSD固有の処理が組み込まれています。特に、.note.netbsd.ident
セクション名がNetBSDターゲットの場合にのみ追加されるようになっています。--- a/src/cmd/6l/asm.c +++ b/src/cmd/6l/asm.c @@ -558,7 +560,7 @@ doelf(void) { Sym *s, *shstrtab, *dynstr; - if(HEADTYPE != Hlinux && HEADTYPE != Hfreebsd && HEADTYPE != Hopenbsd) + if(HEADTYPE != Hlinux && HEADTYPE != Hfreebsd && HEADTYPE != Hopenbsd && HEADTYPE != Hnetbsd) return; /* predefine strings we need for section headers */ @@ -570,6 +572,8 @@ doelf(void) elfstr[ElfStrText] = addstring(shstrtab, ".text"); elfstr[ElfStrData] = addstring(shstrtab, ".data"); elfstr[ElfStrBss] = addstring(shstrtab, ".bss"); + if(HEADTYPE == Hnetbsd) + elfstr[ElfStrNoteNetbsdIdent] = addstring(shstrtab, ".note.netbsd.ident"); addstring(shstrtab, ".elfdata"); addstring(shstrtab, ".rodata"); addstring(shstrtab, ".gosymtab");
-
ELFヘッダのOS ABI設定:
--- a/src/cmd/6l/asm.c +++ b/src/cmd/6l/asm.c @@ -1076,6 +1086,8 @@ asmb(void) \teh->ident[EI_MAG3] = 'F'; \tif(HEADTYPE == Hfreebsd) \t\teh->ident[EI_OSABI] = ELFOSABI_FREEBSD; + \telse if(HEADTYPE == Hnetbsd) + \t\teh->ident[EI_OSABI] = ELFOSABI_NETBSD;\ \telse if(HEADTYPE == Hopenbsd) \t\teh->ident[EI_OSABI] = ELFOSABI_OPENBSD; \teh->ident[EI_CLASS] = ELFCLASS64;
src/cmd/6l/doc.go
および src/cmd/8l/doc.go
-Hnetbsd
リンカオプションのドキュメント追加:--- a/src/cmd/6l/doc.go +++ b/src/cmd/6l/doc.go @@ -31,6 +31,8 @@ Options new in this version:\ Write Linux ELF binaries (default when $GOOS is linux) -Hfreebsd Write FreeBSD ELF binaries (default when $GOOS is freebsd) +-Hnetbsd + Write NetBSD ELF binaries (default when $GOOS is netbsd) -Hopenbsd Write OpenBSD ELF binaries (default when $GOOS is openbsd) -Hwindows
src/cmd/6l/obj.c
および src/cmd/8l/obj.c
headers
配列への"netbsd"
エントリ追加: リンカが-Hnetbsd
オプションを認識し、内部のHnetbsd
タイプにマッピングできるようにします。--- a/src/cmd/6l/obj.c +++ b/src/cmd/6l/obj.c @@ -44,16 +44,17 @@ char*\tthestring = "amd64"; char*\tparamspace = "FP"; Header headers[] = { - "plan9x32", Hplan9x32, - "plan9", Hplan9x64, - "elf", Helf, - "darwin", Hdarwin, - "linux", Hlinux, - "freebsd", Hfreebsd, - "openbsd", Hopenbsd, - "windows", Hwindows, - "windowsgui", Hwindows, - 0, 0 + "plan9x32", Hplan9x32, + "plan9", Hplan9x64, + "elf", Helf, + "darwin", Hdarwin, + "linux", Hlinux, + "freebsd", Hfreebsd, + "netbsd", Hnetbsd, + "openbsd", Hopenbsd, + "windows", Hwindows, + "windowsgui", Hwindows, + 0, 0 };
src/cmd/6l/pass.c
- TLS処理におけるNetBSDの考慮:
TLSアクセスで
FS
レジスタを使用する条件にHEADTYPE == Hnetbsd
が追加されています。--- a/src/cmd/6l/pass.c +++ b/src/cmd/6l/pass.c @@ -295,7 +295,7 @@ patch(void) \t}\ \t\t}\ \t\tif(HEADTYPE == Hlinux || HEADTYPE == Hfreebsd -\t\t|| HEADTYPE == Hopenbsd) { +\t\t|| HEADTYPE == Hopenbsd || HEADTYPE == Hnetbsd) { \t\t\t// ELF uses FS instead of GS. \t\t\tif(p->from.type == D_INDIR+D_GS) \t\t\t\tp->from.type = D_INDIR+D_FS;
src/cmd/cc/godefs.c
および src/cmd/ld/lib.c
- 文字変換関数の引数キャスト:
toupper
,tolower
,isalpha
などの関数呼び出しで、引数がuchar
にキャストされています。これは、C標準ライブラリの文字処理関数がint
型の引数を期待し、char
型が符号付きである場合に負の値が渡されると未定義の動作を引き起こす可能性があるため、安全性を高めるための修正です。
コアとなるコードの解説
このコミットの核心は、GoリンカがNetBSDを新しいターゲットプラットフォームとして認識し、そのためのELFバイナリ生成に必要な最小限の変更を施すことです。
-
プラットフォーム識別子の追加:
Hnetbsd
という新しいヘッダタイプが導入され、リンカがNetBSD向けのビルドであることを内部的に識別できるようになります。これにより、NetBSD固有の処理を条件分岐で追加することが可能になります。 -
ダイナミックリンカパスの指定:
netbsddynld
変数の追加は、NetBSD上でGoバイナリが実行される際に、システムが正しいダイナミックリンカを見つけられるようにするために不可欠です。ELFバイナリのPT_INTERP
セグメントには、このパスが埋め込まれます。 -
ELFヘッダのOS ABI設定:
EI_OSABI
フィールドをELFOSABI_NETBSD
に設定することは、NetBSDカーネルがGoバイナリを「NetBSDネイティブ」の実行可能ファイルとして認識するための重要なステップです。これにより、カーネルはバイナリに対して適切なシステムコールインターフェースやメモリ管理ポリシーを適用できます。 -
ELF Noteセクション名の認識:
ElfStrNoteNetbsdIdent
の追加は、NetBSDが要求する.note.netbsd.ident
セクションの名前をリンカが認識できるようにするためのものです。ただし、このコミットの時点では、このセクションの「内容」を生成するロジックはまだ実装されていません。これが、コミットメッセージで「有効なNetBSDバイナリは作成されない」と述べられている理由です。NetBSDカーネルは、このセクションの存在と特定のフォーマットを期待しており、それがなければENOEXEC
エラーを返します。 -
TLS処理の共通化: TLSアクセスにおける
FS
レジスタの使用は、多くのELFベースのUNIX系システムで共通の慣習です。NetBSDもこれに倣っているため、既存のLinuxやFreeBSD向けのロジックにNetBSDを追加することで、コードの重複を避けつつ正しいTLSアクセスを保証しています。
全体として、このコミットはNetBSDサポートの「骨格」を構築するものであり、NetBSD固有のELF noteセクション生成という残された課題を明確にしています。この課題は、その後のコミットで解決されることになります。
関連リンク
- Go言語のIssueトラッカー: このコミットは、おそらくGo言語のIssueトラッカーでNetBSDサポートに関するIssueに関連している可能性があります。当時のIssueを検索することで、より詳細な議論や背景が見つかるかもしれません。
- Go言語のリンカの進化: Go言語のリンカは時間の経過とともに進化しており、このコミット以降も多くの改善が加えられています。Goのソースコードリポジトリを辿ることで、NetBSDサポートがどのように完成したかを確認できます。
参考にした情報源リンク
- NetBSD ELF Notes Documentation:
- NetBSD Bugs Mailing List Archive:
- Go Gerrit Code Review:
- ELF (Executable and Linkable Format) の仕様:
- ELFの一般的な構造とセクションに関する情報は、様々なオンラインリソースや書籍で参照できます。例えば、Wikipediaの「Executable and Linkable Format」の項目などが参考になります。
- Go言語のリンカに関するドキュメントやブログ記事:
- Go言語の内部構造やリンカの動作について解説している記事は、このコミットの理解を深めるのに役立ちます。