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

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

このコミットは、Go言語のリンカ(cmd/ld)におけるELF実行ファイル生成時の-sフラグの挙動に関する回帰バグを修正するものです。具体的には、ホストオブジェクトファイルの生成サポートが追加された変更セット 98034d036d03 によって導入された問題に対処しています。

コミット

commit f2c3122307fc91b792226facbe1da1f48eec9be5
Author: Anthony Martin <ality@pbrane.org>
Date:   Thu Feb 14 18:43:54 2013 -0800

    cmd/ld: fix -s flag for ELF executables
    
    This fixes a regression introduced in changeset 98034d036d03
    which added support for producing host object files.
    
    R=rsc, minux.ma
    CC=dave, golang-dev
    https://golang.org/cl/7307107

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/f2c3122307fc91b792226facbe1da1f48eec9be5

元コミット内容

このコミットは、Go言語のリンカ(cmd/ld)がELF形式の実行ファイルを生成する際に、-sフラグ(シンボルテーブルの削除)が正しく機能しないという回帰バグを修正します。このバグは、変更セット 98034d036d03 によって導入されました。この変更セットは、ホストオブジェクトファイルの生成をサポートするために行われたものでしたが、その副作用として-sフラグの挙動に影響を与えていました。

変更の背景

Go言語のリンカは、Goプログラムをコンパイルして実行可能なバイナリを生成する重要なツールです。実行可能なバイナリには、デバッグ情報やシンボル情報が含まれることがありますが、これらはバイナリのサイズを増大させます。本番環境にデプロイされるバイナリでは、サイズを削減し、リバースエンジニアリングを困難にするために、これらの情報を削除することが一般的です。Goのリンカでは、-sフラグを使用することで、シンボルテーブルとデバッグ情報をバイナリから取り除くことができます。

しかし、以前の変更セット 98034d036d03 で、リンカがホストシステム固有のオブジェクトファイルを生成する機能が追加されました。この機能追加の際に、ELF実行ファイルの生成ロジックにおいて、-sフラグが指定されている場合でも.symtab(シンボルテーブル)と.strtab(文字列テーブル)セクションが誤って追加されてしまうという回帰バグが発生しました。これにより、-sフラグを使用してもバイナリサイズが期待通りに削減されず、シンボル情報が残ってしまう問題が生じていました。このコミットは、この回帰バグを修正し、-sフラグが意図通りに機能するようにすることを目的としています。

前提知識の解説

ELF (Executable and Linkable Format)

ELFは、Unix系オペレーティングシステム(Linux、BSDなど)で広く使用されている実行可能ファイル、オブジェクトコード、共有ライブラリ、およびコアダンプファイルの標準ファイル形式です。ELFファイルは、ヘッダ、プログラムヘッダテーブル、セクションヘッダテーブル、および様々なセクションで構成されます。

  • セクション: ELFファイル内のデータの論理的なまとまりです。コード、データ、シンボル情報などがセクションとして格納されます。
  • .symtab (Symbol Table Section): プログラム内のシンボル(関数名、変数名など)とそのアドレスや型などの情報が格納されるセクションです。デバッグやリンキングの際に使用されます。
  • .strtab (String Table Section): .symtabなどのセクションで参照される文字列(シンボル名など)が格納されるセクションです。文字列を効率的に管理するために使用されます。

リンカ (Linker)

リンカは、コンパイラによって生成された複数のオブジェクトファイルやライブラリを結合し、最終的な実行可能ファイルや共有ライブラリを生成するプログラムです。リンカの主な役割は以下の通りです。

  1. シンボル解決: 異なるオブジェクトファイル間で参照されるシンボル(関数や変数)を解決し、正しいアドレスにリンクします。
  2. 再配置: オブジェクトファイル内のアドレスを、最終的な実行可能ファイル内の正しいメモリ位置に調整します。
  3. セクション結合: 複数のオブジェクトファイルから同じ種類のセクション(例: コードセクション、データセクション)を結合します。

Go言語のリンカ (cmd/ld)

Go言語のツールチェインには、cmd/ldという独自のリンカが含まれています。これは、Goプログラムの特性(例: ガベージコレクション、goroutineスケジューラ)を考慮して設計されており、クロスコンパイルや静的リンクを容易に行えるように最適化されています。

-s フラグ

Goのビルドコマンド(go buildなど)で-ldflags "-s"オプションを使用すると、リンカに-sフラグが渡されます。このフラグは、生成されるバイナリからシンボルテーブルとデバッグ情報を削除するようリンカに指示します。これにより、バイナリのサイズが削減され、本番環境でのデプロイに適した形になります。

回帰バグ (Regression Bug)

回帰バグとは、ソフトウェアの変更(新機能の追加、バグ修正など)によって、以前は正しく動作していた機能が動作しなくなるバグのことです。このコミットで修正された問題は、ホストオブジェクトファイルのサポート追加という変更によって、既存の-sフラグの機能が損なわれたため、回帰バグに該当します。

技術的詳細

このコミットの技術的な核心は、ELF実行ファイルのセクション生成ロジックにあります。ELFファイルでは、シンボルテーブル(.symtab)と文字列テーブル(.strtab)は、デバッグ情報やシンボル情報を提供するために重要なセクションです。通常、-sフラグが指定された場合、これらのセクションは最終的なバイナリには含まれるべきではありません。

問題のあったコードでは、elfshname(".symtab")elfshname(".strtab") という関数呼び出しが、-sフラグの有無にかかわらず常に実行されていました。これらの関数は、指定されたセクション名をELFセクションヘッダテーブルに追加する役割を持っています。つまり、-sフラグが指定されていても、リンカはこれらのセクションをバイナリに含める準備をしてしまっていたのです。

このコミットでは、これらの関数呼び出しを if(!debug['s']) という条件文で囲むことで修正しています。debug['s'] は、リンカの内部で-sフラグが指定されているかどうかを示すフラグです。この条件文により、-sフラグが指定されていない(つまり、シンボル情報が必要な)場合にのみ、.symtab.strtabセクションがELFセクションヘッダテーブルに追加されるようになります。これにより、-sフラグが正しく機能し、シンボル情報がバイナリから削除されるようになります。

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

変更は src/cmd/ld/elf.c ファイルの elfobj ラベルの付いたセクションにあります。

--- a/src/cmd/ld/elf.c
+++ b/src/cmd/ld/elf.c
@@ -1398,8 +1398,10 @@ elfobj:
 	eh->shstrndx = sh->shnum;
 
 	// put these sections early in the list
-	elfshname(".symtab");
-	elfshname(".strtab");
+	if(!debug['s']) {
+		elfshname(".symtab");
+		elfshname(".strtab");
+	}
 
 	for(sect=segtext.sect; sect!=nil; sect=sect->next)
 		elfshbits(sect);

コアとなるコードの解説

変更されたコードブロックは、ELF実行ファイルのセクションヘッダテーブルを構築する部分です。

  • eh->shstrndx = sh->shnum;: これは、セクション名文字列テーブルのインデックスを設定しています。
  • // put these sections early in the list: コメントは、これらのセクション(.symtab.strtab)をセクションリストの早い段階に配置することを示しています。これは、リンカがこれらのセクションを処理する順序に関連する可能性があります。
  • if(!debug['s']) { ... }: この条件文が今回の修正の核心です。
    • debug['s']: リンカの内部フラグで、コマンドラインオプションの-sが指定されているかどうかを示します。trueであれば-sが指定されていることを意味し、falseであれば指定されていないことを意味します。
    • !debug['s']: これは「-sフラグが指定されていない場合」という条件を意味します。
  • elfshname(".symtab");: .symtabセクションをELFセクションヘッダテーブルに追加する関数呼び出しです。
  • elfshname(".strtab");: .strtabセクションをELFセクションヘッダテーブルに追加する関数呼び出しです。

修正前は、elfshname(".symtab");elfshname(".strtab"); が常に実行されていたため、-sフラグが指定されていてもこれらのセクションが追加されていました。修正後は、if(!debug['s']) の条件が追加されたことで、-sフラグが指定されていない場合にのみこれらのセクションが追加されるようになり、-sフラグの意図通りの挙動が実現されました。

関連リンク

  • Go言語のリンカに関する公式ドキュメントやブログ記事(当時のものがあれば)
  • ELFファイルフォーマットに関する詳細な仕様書
  • Go言語のビルドオプションに関するドキュメント

参考にした情報源リンク