Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 10714] ファイルの概要

このコミットは、Go言語のリンカ(ld6l8l)に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言語のリンカ(ld6l8l)に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バイナリを生成できるようにするための複数の変更を含んでいます。

  1. 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セクションの名前です。
  2. ELFヘッダのOS ABI設定:

    • ELFヘッダのEI_OSABIフィールドは、バイナリがどのOS ABIに準拠しているかを示します。このコミットでは、HEADTYPE == Hnetbsd(NetBSDターゲット)の場合にeh->ident[EI_OSABI]ELFOSABI_NETBSDに設定されるように変更されています。これにより、NetBSDカーネルがバイナリを正しく識別できるようになります。
  3. リンカオプションの追加:

    • src/cmd/6l/doc.gosrc/cmd/8l/doc.goに、新しいリンカオプション-Hnetbsdが追加されています。これにより、ユーザーは明示的にNetBSD向けのバイナリを生成するようリンカに指示できるようになります。
    • src/cmd/6l/obj.csrc/cmd/8l/obj.cheaders配列に"netbsd", Hnetbsdが追加され、リンカが-Hnetbsdオプションを認識し、対応する内部ヘッダタイプHnetbsdにマッピングできるようになっています。
  4. TLS (Thread Local Storage) 処理の調整:

    • src/cmd/6l/pass.cでは、TLSアクセスに関する変更が行われています。ELFシステムでは、TLSは通常FSレジスタを基準としたオフセットでアクセスされます。このコミットでは、HEADTYPE == Hnetbsdの場合も、HlinuxHfreebsdHopenbsdと同様にFSレジスタを使用するように調整されています。これは、NetBSDが他のELFベースのUNIX系OSと同様のTLSメカニズムを採用しているためです。
  5. 文字変換関数の修正:

    • src/cmd/cc/godefs.cでは、touppertolowerといった文字変換関数を呼び出す際に、引数をucharにキャストする修正が行われています。これは、これらの関数がint型の引数を期待し、char型が符号付きである場合に負の値が渡されると未定義の動作を引き起こす可能性があるため、安全性を高めるための一般的なC言語の慣習です。
  6. isalpha関数の修正:

    • src/cmd/ld/lib.cでは、isalpha関数を呼び出す際に、引数をucharにキャストする修正が行われています。これもsrc/cmd/cc/godefs.cと同様に、isalphaint型の引数を期待し、char型が符号付きである場合に負の値が渡されると未定義の動作を引き起こす可能性があるためです。

このコミットは、NetBSDバイナリ生成の基盤を築くものですが、前述の通り、ELF noteセクションの欠如により、この時点では完全な実行可能バイナリは生成されません。

コアとなるコードの変更箇所

このコミットにおける主要な変更は、Goリンカの各コンポーネント(6l8lld)が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バイナリ生成に必要な最小限の変更を施すことです。

  1. プラットフォーム識別子の追加: Hnetbsdという新しいヘッダタイプが導入され、リンカがNetBSD向けのビルドであることを内部的に識別できるようになります。これにより、NetBSD固有の処理を条件分岐で追加することが可能になります。

  2. ダイナミックリンカパスの指定: netbsddynld変数の追加は、NetBSD上でGoバイナリが実行される際に、システムが正しいダイナミックリンカを見つけられるようにするために不可欠です。ELFバイナリのPT_INTERPセグメントには、このパスが埋め込まれます。

  3. ELFヘッダのOS ABI設定: EI_OSABIフィールドをELFOSABI_NETBSDに設定することは、NetBSDカーネルがGoバイナリを「NetBSDネイティブ」の実行可能ファイルとして認識するための重要なステップです。これにより、カーネルはバイナリに対して適切なシステムコールインターフェースやメモリ管理ポリシーを適用できます。

  4. ELF Noteセクション名の認識: ElfStrNoteNetbsdIdentの追加は、NetBSDが要求する.note.netbsd.identセクションの名前をリンカが認識できるようにするためのものです。ただし、このコミットの時点では、このセクションの「内容」を生成するロジックはまだ実装されていません。これが、コミットメッセージで「有効なNetBSDバイナリは作成されない」と述べられている理由です。NetBSDカーネルは、このセクションの存在と特定のフォーマットを期待しており、それがなければENOEXECエラーを返します。

  5. 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言語の内部構造やリンカの動作について解説している記事は、このコミットの理解を深めるのに役立ちます。