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

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

このコミットは、Go言語のツールチェインに含まれるobjdumpコマンドを、C言語で書かれた既存の実装からGo言語による新しい実装へと書き換えるものです。これにより、objdumpはGoツールチェインとの統合が強化され、クロスプラットフォームでの利用が容易になります。特に、pprofツールが提供するlistおよびweblistコマンドの基本的な機能サポートを目的としています。

コミット

commit 0d441a088d2cb23af32dae473aea989830d11055
Author: Russ Cox <rsc@golang.org>
Date:   Mon Apr 14 10:58:49 2014 -0400

    cmd/objdump: rewrite in Go
    
    Update cmd/dist not to build the C version.
    Update cmd/go to install the Go version to the tool directory.
    
    Update #7452
    
    This is the basic logic needed for objdump, and it works well enough
    to support the pprof list and weblist commands. A real disassembler
    needs to be added in order to support the pprof disasm command
    and the per-line assembly displays in weblist. That's still to come.
    
    Probably objdump will move to go.tools when the disassembler
    is added, but it can stay here for now.
    
    LGTM=minux.ma
    R=golang-codereviews, minux.ma
    CC=golang-codereviews, iant, r
    https://golang.org/cl/87580043

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

https://github.com/golang/go/commit/0d441a088d2cb23af32dae473aea989830d11055

元コミット内容

cmd/objdump: rewrite in Go

Update cmd/dist not to build the C version.
Update cmd/go to install the Go version to the tool directory.

Update #7452

This is the basic logic needed for objdump, and it works well enough
to support the pprof list and weblist commands. A real disassembler
needs to be added in order to support the pprof disasm command
and the per-line assembly displays in weblist. That's still to come.

Probably objdump will move to go.tools when the disassembler
is added, but it can stay here for now.

LGTM=minux.ma
R=golang-codereviews, minux.ma
CC=golang-codereviews, iant, r
https://golang.org/cl/87580043

変更の背景

この変更の主な背景には、Go言語のツールチェインにおけるobjdumpコマンドの統合と、クロスプラットフォーム対応の改善があります。

元々、objdumpはC言語で実装されており、特にmacOSのような特定のプラットフォームでは、pprofツールが正しく機能するために必要な情報(シンボル情報や行番号情報など)を抽出する上で課題がありました。C言語のコードはプラットフォーム固有の依存関係を持つことが多く、Goのクロスプラットフォーム開発の哲学とは相容れない部分がありました。

Go言語でobjdumpを再実装することで、以下の利点が得られます。

  1. クロスプラットフォーム互換性: Go言語は強力なクロスコンパイル機能を持ち、単一のコードベースから様々なOSやアーキテクチャ向けのバイナリを生成できます。これにより、objdumpもGoツールチェインの一部として、より広範な環境で一貫して動作するようになります。
  2. Goツールチェインとの統合: objdumpをGoで書くことで、Goの標準ライブラリ(特にdebug/elf, debug/macho, debug/pe, debug/gosymなど)を直接利用できるようになり、Goのバイナリ形式やデバッグ情報との連携がより密になります。
  3. メンテナンス性の向上: Go言語はC言語に比べてメモリ安全性や並行処理の扱いが容易であり、コードの可読性も高い傾向にあります。これにより、将来的な機能追加やバグ修正がより効率的に行えるようになります。
  4. pprofの機能強化: pprofはGoプログラムのプロファイリングを行うための重要なツールです。objdumppprofがバイナリのシンボル情報やアセンブリコードを解析するために利用されます。このコミットでは、特にpprof list(ソースコードとアセンブリの対応リスト表示)とpprof weblist(Webベースのソース/アセンブリ表示)の基本的なサポートを目的としています。将来的には、完全な逆アセンブル機能を追加することで、pprof disasm(完全な逆アセンブル表示)もサポートする計画です。

この変更は、Go Issue #7452に関連しており、Goツールチェイン全体の堅牢性と使いやすさを向上させる一環として行われました。

前提知識の解説

objdumpとは

objdumpは、オブジェクトファイルや実行可能ファイルの内容を解析し、人間が読める形式で表示するためのコマンドラインツールです。一般的な機能としては以下のようなものがあります。

  • 逆アセンブル (Disassembly): 機械語コードをアセンブリ言語に変換して表示します。これにより、プログラムが実際にどのように動作しているかを低レベルで理解できます。
  • セクション情報の表示: 実行可能ファイル内の様々なセクション(例: .text (コード), .data (初期化済みデータ), .bss (初期化なしデータ), .symtab (シンボルテーブル)など)の情報を表示します。
  • シンボルテーブルの表示: プログラム内の関数名、変数名などのシンボルとそのアドレス情報を表示します。デバッグやプロファイリングにおいて、特定のコードがどこにあるかを特定するのに役立ちます。
  • ヘッダ情報の表示: 実行可能ファイルの形式(ELF, Mach-O, PEなど)やアーキテクチャに関する情報を表示します。

このコミットにおけるobjdumpは、特にGoプログラムのバイナリを解析し、pprofツールが利用するシンボル情報や行番号情報を抽出する役割を担います。

pprofとは

pprofは、Go言語の標準的なプロファイリングツールです。CPU使用率、メモリ割り当て、ゴルーチンスタックトレースなど、様々な種類のプロファイルデータを収集し、視覚化することができます。pprofは、Goプログラムのパフォーマンスボトルネックを特定し、最適化を行う上で不可欠なツールです。

pprofの主な機能には以下のようなものがあります。

  • プロファイルデータの収集: runtime/pprofパッケージやHTTPインターフェースを通じて、実行中のGoプログラムからプロファイルデータを収集します。
  • プロファイルデータの解析と視覚化: 収集したプロファイルデータを解析し、テキスト形式、グラフ(Call Graph, Flame Graphなど)、Webインターフェースなど、様々な形式で表示します。
  • listコマンド: ソースコードと対応するアセンブリコードを並べて表示し、どの行がどれだけCPU時間を使っているかなどを視覚的に確認できます。
  • weblistコマンド: listコマンドのWebインターフェース版で、ブラウザでソースコードとアセンブリコードの対応を確認できます。
  • disasmコマンド: 特定の関数のアセンブリコードを完全に逆アセンブルして表示します。

このコミットでobjdumpがGoで書き直されたのは、pproflistおよびweblistコマンドがGoバイナリのシンボル情報や行番号情報を正確に取得できるようにするためです。

Goツールチェインの構成要素

  • cmd/dist: Goのビルドシステムの一部であり、Goツールチェインの様々なコンポーネント(コンパイラ、リンカ、その他のツール)をビルドする役割を担います。このコミットでは、objdumpのC言語版をビルド対象から除外する変更が行われました。
  • cmd/go: Goコマンドラインツールの中核であり、Goプログラムのビルド、テスト、実行、パッケージ管理など、開発者が日常的に使用するほとんどの操作を制御します。このコミットでは、Goで書き直されたobjdumpをGoツールチェインのtoolディレクトリにインストールするよう設定が変更されました。

C言語とGo言語の比較(この文脈において)

  • C言語: システムプログラミングに適しており、ハードウェアに密接にアクセスできます。しかし、メモリ管理(ポインタ、手動でのメモリ解放)が複雑で、メモリリークやセグメンテーション違反などのバグが発生しやすいという側面があります。また、クロスプラットフォーム開発においては、プラットフォーム固有のAPIやコンパイラ、ライブラリの依存関係を管理する必要があります。
  • Go言語: Googleによって開発された静的型付けのコンパイル言語で、シンプルさ、効率性、並行処理のサポートを重視しています。ガベージコレクションによる自動メモリ管理、強力な標準ライブラリ、優れたクロスコンパイル機能が特徴です。システムプログラミングにも利用されますが、C言語よりも抽象度が高く、安全性が高いとされています。

objdumpのようなバイナリ解析ツールをGoで書き直すことは、C言語の持つ低レベルな制御能力の一部を犠牲にするかもしれませんが、Goの持つクロスプラットフォーム性、メモリ安全性、そしてGoツールチェインとのシームレスな統合という大きなメリットを享受できます。

Goの標準ライブラリパッケージ

このコミットで新しくGo言語で実装されたobjdumpは、Goの標準ライブラリの以下のパッケージを多用しています。

  • debug/elf: ELF (Executable and Linkable Format) 形式の実行可能ファイルやオブジェクトファイルを解析するためのパッケージです。LinuxやUnix系システムで広く使われています。
  • debug/macho: Mach-O (Mach Object) 形式の実行可能ファイルやオブジェクトファイルを解析するためのパッケージです。macOSやiOSで使われています。
  • debug/pe: PE (Portable Executable) 形式の実行可能ファイルやオブジェクトファイルを解析するためのパッケージです。Windowsで使われています。
  • debug/gosym: Goバイナリに埋め込まれたGo固有のシンボルテーブル(gosymtab)とPC-Lineテーブル(gopclntab)を解析するためのパッケージです。これにより、実行アドレス(PC)からファイル名、行番号、関数名へのマッピングが可能になります。

これらのパッケージを利用することで、Goで書かれたobjdumpは、異なるOSで生成されたGoバイナリを統一的な方法で解析できるようになります。

技術的詳細

新しいGo言語版のobjdump (src/cmd/objdump/main.go) は、主に以下の機能を提供します。

  1. バイナリ形式の自動判別と解析: loadTables関数は、入力されたバイナリファイルがELF、Mach-O、PEのいずれの形式であるかを自動的に判別し、それぞれの形式に対応するGoのdebug/elf, debug/macho, debug/peパッケージを使用して解析します。 この関数は、バイナリから以下の情報を抽出します。

    • textStart: コードセクション(.text)の開始アドレス。
    • textData: コードセクションの生データ。
    • symtab: Go固有のシンボルテーブル(.gosymtab)の生データ。
    • pclntab: Go固有のPC-Lineテーブル(.gopclntab)の生データ。
  2. Goシンボル情報の利用: 抽出されたsymtabpclntabは、debug/gosymパッケージのgosym.NewLineTablegosym.NewTableを使用して、Goのシンボル情報とPC-Lineマッピングを構築するために利用されます。これにより、特定のアドレスがどのファイル、どの行、どの関数に属するかを正確に特定できます。

  3. 基本的な逆アセンブル機能(暫定): 現在の実装では、完全な命令の逆アセンブルは行いません。代わりに、pprof listweblistが要求するPC(プログラムカウンタ)範囲に対して、ファイル名、行番号、関数名を基に「ダミーの命令」を出力します。具体的には、同じファイル、行、関数に属するPCの範囲をspanとして扱い、その範囲内の各PCに対して、対応するバイトコード(textDataから取得可能であれば)または?を表示します。 コミットメッセージにもあるように、これはpprof listweblistの基本的な動作をサポートするためのものであり、pprof disasmのような完全な逆アセンブル機能や、より詳細なアセンブリ表示には、将来的に「真の逆アセンブラ」の実装が必要とされています。

  4. Goツールチェインとの連携:

    • src/cmd/dist/build.cからC言語版objdumpのビルドとクリーンアップに関する記述が削除されました。これは、GoツールチェインがC言語版objdumpをビルドしなくなることを意味します。
    • src/cmd/go/pkg.gogoToolsマップに"cmd/objdump": toToolが追加されました。これにより、go install cmd/objdumpコマンドがGo言語版のobjdumpをGoのツールディレクトリ(通常は$GOPATH/binまたは$GOBIN)にインストールするようになります。

この変更により、GoのobjdumpはGoツールチェインのネイティブな一部となり、Goプログラムのデバッグとプロファイリングのワークフローが改善されます。

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

このコミットにおける主要なコード変更は以下の4つのファイルに集中しています。

  1. src/cmd/dist/build.c:

    • buildorder配列とcleantab配列から"cmd/objdump"のエントリが削除されました。
    • これは、GoツールチェインのビルドプロセスからC言語版のobjdumpが除外されることを意味します。
    --- a/src/cmd/dist/build.c
    +++ b/src/cmd/dist/build.c
    @@ -1332,7 +1332,6 @@ static char *buildorder[] = {
     
     	"misc/pprof",
     
    -	"cmd/objdump",
     	"cmd/prof",
     
     	"cmd/cc",  // must be before c
    @@ -1409,7 +1408,6 @@ static char *cleantab[] = {
     	"cmd/cc",
     	"cmd/gc",
     	"cmd/go",	
    -	"cmd/objdump",
     	"cmd/prof",
     	"lib9",
     	"libbio",
    
  2. src/cmd/go/pkg.go:

    • goToolsマップに"cmd/objdump": toToolが追加されました。
    • これにより、Go言語版のobjdumpがGoツールチェインの標準ツールとして認識され、go installコマンドでインストールできるようになります。
    --- a/src/cmd/go/pkg.go
    +++ b/src/cmd/go/pkg.go
    @@ -313,6 +313,7 @@ var goTools = map[string]targetDir{
     	"cmd/fix":                              toTool,
     	"cmd/link":                             toTool,
     	"cmd/nm":                               toTool,
    +\t"cmd/objdump":                          toTool,
     	"cmd/pack":                             toTool,
     	"cmd/yacc":                             toTool,
     	"code.google.com/p/go.tools/cmd/cover": toTool,
    
  3. src/cmd/objdump/main.c:

    • C言語で書かれた既存のobjdumpの実装ファイルが完全に削除されました。
    --- a/src/cmd/objdump/main.c
    +++ /dev/null
    @@ -1,68 +0,0 @@
    -// Copyright 2012 The Go Authors. All rights reserved.
    -// Use of this source code is governed by a BSD-style
    -// license that can be found in the LICENSE file.
    -
    -/*
    - * objdump simulation - only enough to make pprof work on Macs
    - */
    -
    -#include <u.h>
    -#include <libc.h>
    -#include <bio.h>
    -#include <mach.h>
    -
    -void
    -usage(void)
    -{
    -	fprint(2, "usage: objdump binary start stop\n");
    -	fprint(2, "Disassembles binary from PC start up to stop.\n");
    -	exits("usage");
    -}
    -
    -void
    -main(int argc, char **argv)
    -{
    -	int fd, n;
    -	uvlong pc, start, stop;
    -	Fhdr fhdr;
    -	Biobuf bout;
    -	char buf[1024];
    -	Map *text;
    -
    -	ARGBEGIN{
    -	default:
    -		usage();
    -	}ARGEND
    -
    -	if(argc != 3)
    -		usage();
    -	start = strtoull(argv[1], 0, 16);
    -	stop = strtoull(argv[2], 0, 16);
    -
    -	fd = open(argv[0], OREAD);
    -	if(fd < 0)
    -		sysfatal("open %s: %r", argv[0]);
    -	if(crackhdr(fd, &fhdr) <= 0)
    -		sysfatal("crackhdr: %r");
    -	machbytype(fhdr.type);
    -	if(syminit(fd, &fhdr) <= 0)
    -		sysfatal("syminit: %r");
    -	text = loadmap(nil, fd, &fhdr);
    -	if(text == nil)
    -		sysfatal("loadmap: %r");
    -
    -	Binit(&bout, 1, OWRITE);
    -	for(pc=start; pc<stop; ) {
    -		if(fileline(buf, sizeof buf, pc))
    -			Bprint(&bout, "%s\n", buf);
    -		buf[0] = '\0';
    -		machdata->das(text, pc, 0, buf, sizeof buf);
    -		Bprint(&bout, " %llx: %s\n", pc, buf);
    -		n = machdata->instsize(text, pc);
    -		if(n <= 0)
    -			break;
    -		pc += n;
    -	}
    -	Bflush(&bout);
    -	exits(0);
    -}
    
  4. src/cmd/objdump/main.go:

    • Go言語による新しいobjdumpの実装ファイルが追加されました。
    • このファイルが、バイナリの解析、シンボル情報の抽出、そして基本的な逆アセンブル(PC-Lineマッピングに基づく)のロジックを含んでいます。
    --- /dev/null
    +++ b/src/cmd/objdump/main.go
    @@ -0,0 +1,162 @@
    +// Copyright 2012 The Go Authors. All rights reserved.
    +// Use of this source code is governed by a BSD-style
    +// license that can be found in the LICENSE file.
    +
    +// objdump simulation - only enough to make pprof work on Macs
    +
    +package main
    +
    +import (
    +	"bufio"
    +	"debug/elf"
    +	"debug/gosym"
    +	"debug/macho"
    +	"debug/pe"
    +	"flag"
    +	"fmt"
    +	"log"
    +	"os"
    +	"strconv"
    +)
    +
    +func printUsage(w *os.File) {
    +	fmt.Fprintf(w, "usage: objdump binary start end\n")
    +	fmt.Fprintf(w, "disassembles binary from start PC to end PC.\n")
    +}
    +
    +func usage() {
    +	printUsage(os.Stderr)
    +	os.Exit(2)
    +}
    +
    +func main() {
    +	log.SetFlags(0)
    +	log.SetPrefix("objdump: ")
    +
    +	flag.Usage = usage
    +	flag.Parse()
    +	if flag.NArg() != 3 {
    +		usage()
    +	}
    +
    +	f, err := os.Open(flag.Arg(0))
    +	if err != nil {
    +		log.Fatal(err)
    +	}
    +
    +	textStart, textData, symtab, pclntab, err := loadTables(f)
    +	if err != nil {
    +		log.Fatalf("reading %s: %v", flag.Arg(0), err)
    +	}
    +
    +	pcln := gosym.NewLineTable(pclntab, textStart)
    +	tab, err := gosym.NewTable(symtab, pcln)
    +	if err != nil {
    +		log.Fatalf("reading %s: %v", flag.Arg(0), err)
    +	}
    +
    +	start, err := strconv.ParseUint(flag.Arg(1), 0, 64)
    +	if err != nil {
    +		log.Fatalf("invalid start PC: %v", err)
    +	}
    +	end, err := strconv.ParseUint(flag.Arg(2), 0, 64)
    +	if err != nil {
    +		log.Fatalf("invalid end PC: %v", err)
    +	}
    +
    +	stdout := bufio.NewWriter(os.Stdout)
    +
    +	// For now, find spans of same PC/line/fn and
    +	// emit them as having dummy instructions.
    +	var (
    +		spanPC   uint64
    +		spanFile string
    +		spanLine int
    +		spanFn   *gosym.Func
    +	)
    +
    +	flush := func(endPC uint64) {
    +		if spanPC == 0 {
    +			return
    +		}
    +		fmt.Fprintf(stdout, "%s:%d\n", spanFile, spanLine)
    +		for pc := spanPC; pc < endPC; pc++ {
    +			// TODO(rsc): Disassemble instructions here.
    +			if textStart <= pc && pc-textStart < uint64(len(textData)) {
    +				fmt.Fprintf(stdout, " %x: byte %#x\n", pc, textData[pc-textStart])
    +			} else {
    +				fmt.Fprintf(stdout, " %x: ?\n", pc)
    +			}
    +		}
    +		spanPC = 0
    +	}
    +
    +	for pc := start; pc < end; pc++ {
    +		file, line, fn := tab.PCToLine(pc)
    +		if file != spanFile || line != spanLine || fn != spanFn {
    +			flush(pc)
    +			spanPC, spanFile, spanLine, spanFn = pc, file, line, fn
    +		}
    +	}
    +	flush(end)
    +
    +	stdout.Flush()
    +}
    +
    +func loadTables(f *os.File) (textStart uint64, textData, symtab, pclntab []byte, err error) {
    +	if obj, err := elf.NewFile(f); err == nil {
    +		if sect := obj.Section(".text"); sect != nil {
    +			textStart = sect.Addr
    +			textData, _ = sect.Data()
    +		}
    +		if sect := obj.Section(".gosymtab"); sect != nil {
    +			if symtab, err = sect.Data(); err != nil {
    +				return 0, nil, nil, nil, err
    +			}
    +		}
    +		if sect := obj.Section(".gopclntab"); sect != nil {
    +			if pclntab, err = sect.Data(); err != nil {
    +				return 0, nil, nil, nil, err
    +			}
    +		}
    +		return textStart, textData, symtab, pclntab, nil
    +	}
    +
    +	if obj, err := macho.NewFile(f); err == nil {
    +		if sect := obj.Section("__text"); sect != nil {
    +			textStart = sect.Addr
    +			textData, _ = sect.Data()
    +		}
    +		if sect := obj.Section("__gosymtab"); sect != nil {
    +			if symtab, err = sect.Data(); err != nil {
    +				return 0, nil, nil, nil, err
    +			}
    +		}
    +		if sect := obj.Section("__gopclntab"); sect != nil {
    +			if pclntab, err = sect.Data(); err != nil {
    +				return 0, nil, nil, nil, err
    +			}
    +		}
    +		return textStart, textData, symtab, pclntab, nil
    +	}
    +
    +	if obj, err := pe.NewFile(f); err == nil {
    +		if sect := obj.Section(".text"); sect != nil {
    +			textStart = uint64(sect.VirtualAddress)
    +			textData, _ = sect.Data()
    +		}
    +		if sect := obj.Section(".gosymtab"); sect != nil {
    +			if symtab, err = sect.Data(); err != nil {
    +				return 0, nil, nil, nil, err
    +			}
    +		}
    +		if sect := obj.Section(".gopclntab"); sect != nil {
    +			if pclntab, err = sect.Data(); err != nil {
    +				return 0, nil, nil, nil, err
    +			}
    +		}
    +		return textStart, textData, symtab, pclntab, nil
    +	}
    +
    +	return 0, nil, nil, nil, fmt.Errorf("unrecognized binary format")
    +}
    

コアとなるコードの解説

src/cmd/objdump/main.goの主要な部分を解説します。

main関数

main関数はobjdumpコマンドのエントリポイントです。

  1. 引数解析: flagパッケージを使用してコマンドライン引数を解析します。objdumpbinary start endの3つの引数を期待します。binaryは解析対象の実行可能ファイル、startendは逆アセンブルするPC(プログラムカウンタ)の範囲を指定します。
  2. ファイルオープン: 指定されたバイナリファイルを開きます。
  3. テーブルのロード: loadTables関数を呼び出し、バイナリファイルからコードセクション、Goシンボルテーブル、PC-Lineテーブルの生データを抽出します。
  4. Goシンボルテーブルの構築: 抽出した生データとdebug/gosymパッケージを使用して、gosym.NewLineTablegosym.NewTableを構築します。これにより、PCからファイル名、行番号、関数名へのマッピングが可能になります。
  5. PC範囲の解析: コマンドライン引数で指定されたstartendのPC値をuint64に変換します。
  6. 出力処理:
    • bufio.NewWriter(os.Stdout)を使用して標準出力への書き込みをバッファリングします。
    • for pc := start; pc < end; pc++ループで、指定されたPC範囲を1バイトずつ(または命令サイズずつ、将来的には)走査します。
    • tab.PCToLine(pc)を呼び出して、現在のPCに対応するファイル名、行番号、関数名を取得します。
    • spanPC, spanFile, spanLine, spanFn変数を使って、同じファイル/行/関数に属するPCの連続した範囲(span)を追跡します。
    • flush関数は、spanが変更されたとき(つまり、ファイル、行、または関数が変わったとき)に呼び出されます。flush関数は、現在のspanのファイル名と行番号を出力し、そのspan内の各PCに対して「ダミーの命令」(バイトコードまたは?)を出力します。
    • ループの最後にflush(end)を呼び出すことで、最後のspanも出力されます。
    • stdout.Flush()でバッファリングされた内容を実際に出力します。

loadTables関数

loadTables関数は、Go言語版objdumpの核心部分であり、様々な実行可能ファイル形式から必要な情報を抽出する役割を担います。

  1. ELF形式の解析: elf.NewFile(f)を試みます。成功した場合、ELFファイルとして解析を進めます。

    • .textセクションからtextStart(開始アドレス)とtextData(生データ)を取得します。
    • .gosymtabセクションからsymtab(Goシンボルテーブル)を取得します。
    • .gopclntabセクションからpclntab(Go PC-Lineテーブル)を取得します。
    • これらの情報が取得できれば、それを返します。
  2. Mach-O形式の解析: ELFとしての解析が失敗した場合、macho.NewFile(f)を試みます。成功した場合、Mach-Oファイルとして解析を進めます。

    • __textセクションからtextStarttextDataを取得します。
    • __gosymtabセクションからsymtabを取得します。
    • __gopclntabセクションからpclntabを取得します。
    • これらの情報が取得できれば、それを返します。
  3. PE形式の解析: Mach-Oとしての解析も失敗した場合、pe.NewFile(f)を試みます。成功した場合、PEファイルとして解析を進めます。

    • .textセクションからtextStartVirtualAddressを使用)とtextDataを取得します。
    • .gosymtabセクションからsymtabを取得します。
    • .gopclntabセクションからpclntabを取得します。
    • これらの情報が取得できれば、それを返します。
  4. 未認識の形式: いずれの形式としても認識できなかった場合、"unrecognized binary format"というエラーを返します。

このloadTables関数のおかげで、Go言語版objdumpは、Linux (ELF), macOS (Mach-O), Windows (PE) など、異なるプラットフォームでビルドされたGoバイナリを透過的に処理できるようになっています。

関連リンク

  • Go Issue #7452: cmd/objdump: rewrite in Go
    • このコミットが解決または関連するGoのIssueトラッカーのエントリです。詳細な議論や背景情報が含まれている可能性があります。
  • Go CL 87580043: https://golang.org/cl/87580043
    • このコミットに対応するGerrit Code Reviewのチェンジリストです。コードレビューのコメントや、より詳細な変更履歴を確認できます。

参考にした情報源リンク

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

このコミットは、Go言語のツールチェインに含まれるobjdumpコマンドを、C言語で書かれた既存の実装からGo言語による新しい実装へと書き換えるものです。これにより、objdumpはGoツールチェインとの統合が強化され、クロスプラットフォームでの利用が容易になります。特に、pprofツールが提供するlistおよびweblistコマンドの基本的な機能サポートを目的としています。

コミット

commit 0d441a088d2cb23af32dae473aea989830d11055
Author: Russ Cox <rsc@golang.org>
Date:   Mon Apr 14 10:58:49 2014 -0400

    cmd/objdump: rewrite in Go
    
    Update cmd/dist not to build the C version.
    Update cmd/go to install the Go version to the tool directory.
    
    Update #7452
    
    This is the basic logic needed for objdump, and it works well enough
    to support the pprof list and weblist commands. A real disassembler
    needs to be added in order to support the pprof disasm command
    and the per-line assembly displays in weblist. That's still to come.
    
    Probably objdump will move to go.tools when the disassembler
    is added, but it can stay here for now.
    
    LGTM=minux.ma
    R=golang-codereviews, minux.ma
    CC=golang-codereviews, iant, r
    https://golang.org/cl/87580043

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

https://github.com/golang/go/commit/0d441a088d2cb23af32dae473aea989830d11055

元コミット内容

cmd/objdump: rewrite in Go

Update cmd/dist not to build the C version.
Update cmd/go to install the Go version to the tool directory.

Update #7452

This is the basic logic needed for objdump, and it works well enough
to support the pprof list and weblist commands. A real disassembler
needs to be added in order to support the pprof disasm command
and the per-line assembly displays in weblist. That's still to come.

Probably objdump will move to go.tools when the disassembler
is added, but it can stay here for now.

LGTM=minux.ma
R=golang-codereviews, minux.ma
CC=golang-codereviews, iant, r
https://golang.org/cl/87580043

変更の背景

この変更の主な背景には、Go言語のツールチェインにおけるobjdumpコマンドの統合と、クロスプラットフォーム対応の改善があります。

元々、objdumpはC言語で実装されており、特にmacOSのような特定のプラットフォームでは、pprofツールが正しく機能するために必要な情報(シンボル情報や行番号情報など)を抽出する上で課題がありました。C言語のコードはプラットフォーム固有の依存関係を持つことが多く、Goのクロスプラットフォーム開発の哲学とは相容れない部分がありました。

Go言語でobjdumpを再実装することで、以下の利点が得られます。

  1. クロスプラットフォーム互換性: Go言語は強力なクロスコンパイル機能を持ち、単一のコードベースから様々なOSやアーキテクチャ向けのバイナリを生成できます。これにより、objdumpもGoツールチェインの一部として、より広範な環境で一貫して動作するようになります。
  2. Goツールチェインとの統合: objdumpをGoで書くことで、Goの標準ライブラリ(特にdebug/elf, debug/macho, debug/pe, debug/gosymなど)を直接利用できるようになり、Goのバイナリ形式やデバッグ情報との連携がより密になります。
  3. メンテナンス性の向上: Go言語はC言語に比べてメモリ安全性や並行処理の扱いが容易であり、コードの可読性も高い傾向にあります。これにより、将来的な機能追加やバグ修正がより効率的に行えるようになります。
  4. pprofの機能強化: pprofはGoプログラムのプロファイリングを行うための重要なツールです。objdumppprofがバイナリのシンボル情報やアセンブリコードを解析するために利用されます。このコミットでは、特にpprof list(ソースコードとアセンブリの対応リスト表示)とpprof weblist(Webベースのソース/アセンブリ表示)の基本的なサポートを目的としています。将来的には、完全な逆アセンブル機能を追加することで、pprof disasm(完全な逆アセンブル表示)もサポートする計画です。

この変更は、Go Issue #7452に関連しており、Goツールチェイン全体の堅牢性と使いやすさを向上させる一環として行われました。

前提知識の解説

objdumpとは

objdumpは、オブジェクトファイルや実行可能ファイルの内容を解析し、人間が読める形式で表示するためのコマンドラインツールです。一般的な機能としては以下のようなものがあります。

  • 逆アセンブル (Disassembly): 機械語コードをアセンブリ言語に変換して表示します。これにより、プログラムが実際にどのように動作しているかを低レベルで理解できます。
  • セクション情報の表示: 実行可能ファイル内の様々なセクション(例: .text (コード), .data (初期化済みデータ), .bss (初期化なしデータ), .symtab (シンボルテーブル)など)の情報を表示します。
  • シンボルテーブルの表示: プログラム内の関数名、変数名などのシンボルとそのアドレス情報を表示します。デバッグやプロファイリングにおいて、特定のコードがどこにあるかを特定するのに役立ちます。
  • ヘッダ情報の表示: 実行可能ファイルの形式(ELF, Mach-O, PEなど)やアーキテクチャに関する情報を表示します。

このコミットにおけるobjdumpは、特にGoプログラムのバイナリを解析し、pprofツールが利用するシンボル情報や行番号情報を抽出する役割を担います。

pprofとは

pprofは、Go言語の標準的なプロファイリングツールです。CPU使用率、メモリ割り当て、ゴルーチンスタックトレースなど、様々な種類のプロファイルデータを収集し、視覚化することができます。pprofは、Goプログラムのパフォーマンスボトルネックを特定し、最適化を行う上で不可欠なツールです。

pprofの主な機能には以下のようなものがあります。

  • プロファイルデータの収集: runtime/pprofパッケージやHTTPインターフェースを通じて、実行中のGoプログラムからプロファイルデータを収集します。
  • プロファイルデータの解析と視覚化: 収集したプロファイルデータを解析し、テキスト形式、グラフ(Call Graph, Flame Graphなど)、Webインターフェースなど、様々な形式で表示します。
  • listコマンド: ソースコードと対応するアセンブリコードを並べて表示し、どの行がどれだけCPU時間を使っているかなどを視覚的に確認できます。
  • weblistコマンド: listコマンドのWebインターフェース版で、ブラウザでソースコードとアセンブリコードの対応を確認できます。
  • disasmコマンド: 特定の関数のアセンブリコードを完全に逆アセンブルして表示します。

このコミットでobjdumpがGoで書き直されたのは、pproflistおよびweblistコマンドがGoバイナリのシンボル情報や行番号情報を正確に取得できるようにするためです。

Goツールチェインの構成要素

  • cmd/dist: Goのビルドシステムの一部であり、Goツールチェインの様々なコンポーネント(コンパイラ、リンカ、その他のツール)をビルドする役割を担います。このコミットでは、objdumpのC言語版をビルド対象から除外する変更が行われました。
  • cmd/go: Goコマンドラインツールの中核であり、Goプログラムのビルド、テスト、実行、パッケージ管理など、開発者が日常的に使用するほとんどの操作を制御します。このコミットでは、Goで書き直されたobjdumpをGoツールチェインのtoolディレクトリにインストールするよう設定が変更されました。

C言語とGo言語の比較(この文脈において)

  • C言語: システムプログラミングに適しており、ハードウェアに密接にアクセスできます。しかし、メモリ管理(ポインタ、手動でのメモリ解放)が複雑で、メモリリークやセグメンテーション違反などのバグが発生しやすいという側面があります。また、クロスプラットフォーム開発においては、プラットフォーム固有のAPIやコンパイラ、ライブラリの依存関係を管理する必要があります。
  • Go言語: Googleによって開発された静的型付けのコンパイル言語で、シンプルさ、効率性、並行処理のサポートを重視しています。ガベージコレクションによる自動メモリ管理、強力な標準ライブラリ、優れたクロスコンパイル機能が特徴です。システムプログラミングにも利用されますが、C言語よりも抽象度が高く、安全性が高いとされています。

objdumpのようなバイナリ解析ツールをGoで書き直すことは、C言語の持つ低レベルな制御能力の一部を犠牲にするかもしれませんが、Goの持つクロスプラットフォーム性、メモリ安全性、そしてGoツールチェインとのシームレスな統合という大きなメリットを享受できます。

Goの標準ライブラリパッケージ

このコミットで新しくGo言語で実装されたobjdumpは、Goの標準ライブラリの以下のパッケージを多用しています。

  • debug/elf: ELF (Executable and Linkable Format) 形式の実行可能ファイルやオブジェクトファイルを解析するためのパッケージです。LinuxやUnix系システムで広く使われています。
  • debug/macho: Mach-O (Mach Object) 形式の実行可能ファイルやオブジェクトファイルを解析するためのパッケージです。macOSやiOSで使われています。
  • debug/pe: PE (Portable Executable) 形式の実行可能ファイルやオブジェクトファイルを解析するためのパッケージです。Windowsで使われています。
  • debug/gosym: Goバイナリに埋め込まれたGo固有のシンボルテーブル(gosymtab)とPC-Lineテーブル(gopclntab)を解析するためのパッケージです。これにより、実行アドレス(PC)からファイル名、行番号、関数名へのマッピングが可能になります。

これらのパッケージを利用することで、Goで書かれたobjdumpは、異なるOSで生成されたGoバイナリを統一的な方法で解析できるようになります。

技術的詳細

新しいGo言語版のobjdump (src/cmd/objdump/main.go) は、主に以下の機能を提供します。

  1. バイナリ形式の自動判別と解析: loadTables関数は、入力されたバイナリファイルがELF、Mach-O、PEのいずれの形式であるかを自動的に判別し、それぞれの形式に対応するGoのdebug/elf, debug/macho, debug/peパッケージを使用して解析します。 この関数は、バイナリから以下の情報を抽出します。

    • textStart: コードセクション(.text)の開始アドレス。
    • textData: コードセクションの生データ。
    • symtab: Go固有のシンボルテーブル(.gosymtab)の生データ。
    • pclntab: Go固有のPC-Lineテーブル(.gopclntab)の生データ。
  2. Goシンボル情報の利用: 抽出されたsymtabpclntabは、debug/gosymパッケージのgosym.NewLineTablegosym.NewTableを使用して、Goのシンボル情報とPC-Lineマッピングを構築するために利用されます。これにより、特定のアドレスがどのファイル、どの行、どの関数に属するかを正確に特定できます。

  3. 基本的な逆アセンブル機能(暫定): 現在の実装では、完全な命令の逆アセンブルは行いません。代わりに、pprof listweblistが要求するPC(プログラムカウンタ)範囲に対して、ファイル名、行番号、関数名を基に「ダミーの命令」を出力します。具体的には、同じファイル、行、関数に属するPCの連続した範囲をspanとして扱い、その範囲内の各PCに対して、対応するバイトコード(textDataから取得可能であれば)または?を表示します。 コミットメッセージにもあるように、これはpprof listweblistの基本的な動作をサポートするためのものであり、pprof disasmのような完全な逆アセンブル機能や、より詳細なアセンブリ表示には、将来的に「真の逆アセンブラ」の実装が必要とされています。

  4. Goツールチェインとの連携:

    • src/cmd/dist/build.cからC言語版objdumpのビルドとクリーンアップに関する記述が削除されました。これは、GoツールチェインがC言語版objdumpをビルドしなくなることを意味します。
    • src/cmd/go/pkg.gogoToolsマップに"cmd/objdump": toToolが追加されました。これにより、go install cmd/objdumpコマンドがGo言語版のobjdumpをGoのツールディレクトリ(通常は$GOPATH/binまたは$GOBIN)にインストールするようになります。

この変更により、GoのobjdumpはGoツールチェインのネイティブな一部となり、Goプログラムのデバッグとプロファイリングのワークフローが改善されます。

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

このコミットにおける主要なコード変更は以下の4つのファイルに集中しています。

  1. src/cmd/dist/build.c:

    • buildorder配列とcleantab配列から"cmd/objdump"のエントリが削除されました。
    • これは、GoツールチェインのビルドプロセスからC言語版のobjdumpが除外されることを意味します。
    --- a/src/cmd/dist/build.c
    +++ b/src/cmd/dist/build.c
    @@ -1332,7 +1332,6 @@ static char *buildorder[] = {
     
     	"misc/pprof",
     
    -	"cmd/objdump",
     	"cmd/prof",
     
     	"cmd/cc",  // must be before c
    @@ -1409,7 +1408,6 @@ static char *cleantab[] = {
     	"cmd/cc",
     	"cmd/gc",
     	"cmd/go",	
    -	"cmd/objdump",
     	"cmd/prof",
     	"lib9",
     	"libbio",
    
  2. src/cmd/go/pkg.go:

    • goToolsマップに"cmd/objdump": toToolが追加されました。
    • これにより、Go言語版のobjdumpがGoツールチェインの標準ツールとして認識され、go installコマンドでインストールできるようになります。
    --- a/src/cmd/go/pkg.go
    +++ b/src/cmd/go/pkg.go
    @@ -313,6 +313,7 @@ var goTools = map[string]targetDir{
     	"cmd/fix":                              toTool,
     	"cmd/link":                             toTool,
     	"cmd/nm":                               toTool,
    +\t"cmd/objdump":                          toTool,
     	"cmd/pack":                             toTool,
     	"cmd/yacc":                             toTool,
     	"code.google.com/p/go.tools/cmd/cover": toTool,
    
  3. src/cmd/objdump/main.c:

    • C言語で書かれた既存のobjdumpの実装ファイルが完全に削除されました。
    --- a/src/cmd/objdump/main.c
    +++ /dev/null
    @@ -1,68 +0,0 @@
    -// Copyright 2012 The Go Authors. All rights reserved.
    -// Use of this source code is governed by a BSD-style
    -// license that can be found in the LICENSE file.
    -
    -/*
    - * objdump simulation - only enough to make pprof work on Macs
    - */
    -
    -#include <u.h>
    -#include <libc.h>
    -#include <bio.h>
    -#include <mach.h>
    -
    -void
    -usage(void)
    -{
    -	fprint(2, "usage: objdump binary start stop\n");
    -	fprint(2, "Disassembles binary from PC start up to stop.\n");
    -	exits("usage");
    -}
    -
    -void
    -main(int argc, char **argv)
    -{
    -	int fd, n;
    -	uvlong pc, start, stop;
    -	Fhdr fhdr;
    -	Biobuf bout;
    -	char buf[1024];
    -	Map *text;
    -
    -	ARGBEGIN{
    -	default:
    -		usage();
    -	}ARGEND
    -
    -	if(argc != 3)
    -		usage();
    -	start = strtoull(argv[1], 0, 16);
    -	stop = strtoull(argv[2], 0, 16);
    -
    -	fd = open(argv[0], OREAD);
    -	if(fd < 0)
    -		sysfatal("open %s: %r", argv[0]);
    -	if(crackhdr(fd, &fhdr) <= 0)
    -		sysfatal("crackhdr: %r");
    -	machbytype(fhdr.type);
    -	if(syminit(fd, &fhdr) <= 0)
    -		sysfatal("syminit: %r");
    -	text = loadmap(nil, fd, &fhdr);
    -	if(text == nil)
    -		sysfatal("loadmap: %r");
    -
    -	Binit(&bout, 1, OWRITE);
    -	for(pc=start; pc<stop; ) {
    -		if(fileline(buf, sizeof buf, pc))
    -			Bprint(&bout, "%s\n", buf);
    -		buf[0] = '\0';
    -		machdata->das(text, pc, 0, buf, sizeof buf);
    -		Bprint(&bout, " %llx: %s\n", pc, buf);
    -		n = machdata->instsize(text, pc);
    -		if(n <= 0)
    -			break;
    -		pc += n;
    -	}
    -	Bflush(&bout);
    -	exits(0);
    -}
    
  4. src/cmd/objdump/main.go:

    • Go言語による新しいobjdumpの実装ファイルが追加されました。
    • このファイルが、バイナリの解析、シンボル情報の抽出、そして基本的な逆アセンブル(PC-Lineマッピングに基づく)のロジックを含んでいます。
    --- /dev/null
    +++ b/src/cmd/objdump/main.go
    @@ -0,0 +1,162 @@
    +// Copyright 2012 The Go Authors. All rights reserved.
    +// Use of this source code is governed by a BSD-style
    +// license that can be found in the LICENSE file.
    +
    +// objdump simulation - only enough to make pprof work on Macs
    +
    +package main
    +
    +import (
    +	"bufio"
    +	"debug/elf"
    +	"debug/gosym"
    +	"debug/macho"
    +	"debug/pe"
    +	"flag"
    +	"fmt"
    +	"log"
    +	"os"
    +	"strconv"
    +)
    +
    +func printUsage(w *os.File) {
    +	fmt.Fprintf(w, "usage: objdump binary start end\n")
    +	fmt.Fprintf(w, "disassembles binary from start PC to end PC.\n")
    +}
    +
    +func usage() {
    +	printUsage(os.Stderr)
    +	os.Exit(2)
    +}
    +
    +func main() {
    +	log.SetFlags(0)
    +	log.SetPrefix("objdump: ")
    +
    +	flag.Usage = usage
    +	flag.Parse()
    +	if flag.NArg() != 3 {
    +		usage()
    +	}
    +
    +	f, err := os.Open(flag.Arg(0))
    +	if err != nil {
    +		log.Fatal(err)
    +	}
    +
    	textStart, textData, symtab, pclntab, err := loadTables(f)
    	if err != nil {
    		log.Fatalf("reading %s: %v", flag.Arg(0), err)
    	}
    
    	pcln := gosym.NewLineTable(pclntab, textStart)
    	tab, err := gosym.NewTable(symtab, pcln)
    	if err != nil {
    		log.Fatalf("reading %s: %v", flag.Arg(0), err)
    	}
    
    	start, err := strconv.ParseUint(flag.Arg(1), 0, 64)
    	if err != nil {
    		log.Fatalf("invalid start PC: %v", err)
    	}
    	end, err := strconv.ParseUint(flag.Arg(2), 0, 64)
    	if err != nil {
    		log.Fatalf("invalid end PC: %v", err)
    	}
    
    	stdout := bufio.NewWriter(os.Stdout)
    
    	// For now, find spans of same PC/line/fn and
    	// emit them as having dummy instructions.
    	var (
    		spanPC   uint64
    		spanFile string
    		spanLine int
    		spanFn   *gosym.Func
    	)
    
    	flush := func(endPC uint64) {
    		if spanPC == 0 {
    			return
    		}
    		fmt.Fprintf(stdout, "%s:%d\n", spanFile, spanLine)
    		for pc := spanPC; pc < endPC; pc++ {
    			// TODO(rsc): Disassemble instructions here.
    			if textStart <= pc && pc-textStart < uint64(len(textData)) {
    				fmt.Fprintf(stdout, " %x: byte %#x\n", pc, textData[pc-textStart])
    			} else {
    				fmt.Fprintf(stdout, " %x: ?\n", pc)
    			}
    		}
    		spanPC = 0
    	}
    
    	for pc := start; pc < end; pc++ {
    		file, line, fn := tab.PCToLine(pc)
    		if file != spanFile || line != spanLine || fn != spanFn {
    			flush(pc)
    			spanPC, spanFile, spanLine, spanFn = pc, file, line, fn
    		}
    	}
    	flush(end)
    
    	stdout.Flush()
    }
    
    func loadTables(f *os.File) (textStart uint64, textData, symtab, pclntab []byte, err error) {
    	if obj, err := elf.NewFile(f); err == nil {
    		if sect := obj.Section(".text"); sect != nil {
    			textStart = sect.Addr
    			textData, _ = sect.Data()
    		}
    		if sect := obj.Section(".gosymtab"); sect != nil {
    			if symtab, err = sect.Data(); err != nil {
    				return 0, nil, nil, nil, err
    			}
    		}
    		if sect := obj.Section(".gopclntab"); sect != nil {
    			if pclntab, err = sect.Data(); err != nil {
    				return 0, nil, nil, nil, err
    			}
    		}
    		return textStart, textData, symtab, pclntab, nil
    	}
    
    	if obj, err := macho.NewFile(f); err == nil {
    		if sect := obj.Section("__text"); sect != nil {
    			textStart = sect.Addr
    			textData, _ = sect.Data()
    		}
    		if sect := obj.Section("__gosymtab"); sect != nil {
    			if symtab, err = sect.Data(); err != nil {
    				return 0, nil, nil, nil, err
    			}
    		}
    		if sect := obj.Section("__gopclntab"); sect != nil {
    			if pclntab, err = sect.Data(); err != nil {
    				return 0, nil, nil, nil, err
    			}
    		}
    		return textStart, textData, symtab, pclntab, nil
    	}
    
    	if obj, err := pe.NewFile(f); err == nil {
    		if sect := obj.Section(".text"); sect != nil {
    			textStart = uint64(sect.VirtualAddress)
    			textData, _ = sect.Data()
    		}
    		if sect := obj.Section(".gosymtab"); sect != nil {
    			if symtab, err = sect.Data(); err != nil {
    				return 0, nil, nil, nil, err
    			}
    		}
    		if sect := obj.Section(".gopclntab"); sect != nil {
    			if pclntab, err = sect.Data(); err != nil {
    				return 0, nil, nil, nil, err
    			}
    		}
    		return textStart, textData, symtab, pclntab, nil
    	}
    
    	return 0, nil, nil, nil, fmt.Errorf("unrecognized binary format")
    }
    

コアとなるコードの解説

src/cmd/objdump/main.goの主要な部分を解説します。

main関数

main関数はobjdumpコマンドのエントリポイントです。

  1. 引数解析: flagパッケージを使用してコマンドライン引数を解析します。objdumpbinary start endの3つの引数を期待します。binaryは解析対象の実行可能ファイル、startendは逆アセンブルするPC(プログラムカウンタ)の範囲を指定します。
  2. ファイルオープン: 指定されたバイナリファイルを開きます。
  3. テーブルのロード: loadTables関数を呼び出し、バイナリファイルからコードセクション、Goシンボルテーブル、PC-Lineテーブルの生データを抽出します。
  4. Goシンボルテーブルの構築: 抽出した生データとdebug/gosymパッケージを使用して、gosym.NewLineTablegosym.NewTableを構築します。これにより、PCからファイル名、行番号、関数名へのマッピングが可能になります。
  5. PC範囲の解析: コマンドライン引数で指定されたstartendのPC値をuint64に変換します。
  6. 出力処理:
    • bufio.NewWriter(os.Stdout)を使用して標準出力への書き込みをバッファリングします。
    • for pc := start; pc < end; pc++ループで、指定されたPC範囲を1バイトずつ(または命令サイズずつ、将来的には)走査します。
    • tab.PCToLine(pc)を呼び出して、現在のPCに対応するファイル名、行番号、関数名を取得します。
    • spanPC, spanFile, spanLine, spanFn変数を使って、同じファイル/行/関数に属するPCの連続した範囲(span)を追跡します。
    • flush関数は、spanが変更されたとき(つまり、ファイル、行、または関数が変わったとき)に呼び出されます。flush関数は、現在のspanのファイル名と行番号を出力し、そのspan内の各PCに対して「ダミーの命令」(バイトコードまたは?)を出力します。
    • ループの最後にflush(end)を呼び出すことで、最後のspanも出力されます。
    • stdout.Flush()でバッファリングされた内容を実際に出力します。

loadTables関数

loadTables関数は、Go言語版objdumpの核心部分であり、様々な実行可能ファイル形式から必要な情報を抽出する役割を担います。

  1. ELF形式の解析: elf.NewFile(f)を試みます。成功した場合、ELFファイルとして解析を進めます。

    • .textセクションからtextStart(開始アドレス)とtextData(生データ)を取得します。
    • .gosymtabセクションからsymtab(Goシンボルテーブル)を取得します。
    • .gopclntabセクションからpclntab(Go PC-Lineテーブル)を取得します。
    • これらの情報が取得できれば、それを返します。
  2. Mach-O形式の解析: ELFとしての解析が失敗した場合、macho.NewFile(f)を試みます。成功した場合、Mach-Oファイルとして解析を進めます。

    • __textセクションからtextStarttextDataを取得します。
    • __gosymtabセクションからsymtabを取得します。
    • __gopclntabセクションからpclntabを取得します。
    • これらの情報が取得できれば、それを返します。
  3. PE形式の解析: Mach-Oとしての解析も失敗した場合、pe.NewFile(f)を試みます。成功した場合、PEファイルとして解析を進めます。

    • .textセクションからtextStartVirtualAddressを使用)とtextDataを取得します。
    • .gosymtabセクションからsymtabを取得します。
    • .gopclntabセクションからpclntabを取得します。
    • これらの情報が取得できれば、それを返します。
  4. 未認識の形式: いずれの形式としても認識できなかった場合、"unrecognized binary format"というエラーを返します。

このloadTables関数のおかげで、Go言語版objdumpは、Linux (ELF), macOS (Mach-O), Windows (PE) など、異なるプラットフォームでビルドされたGoバイナリを透過的に処理できるようになっています。

関連リンク

  • Go Issue #7452: cmd/objdump: rewrite in Go
    • このコミットが解決または関連するGoのIssueトラッカーのエントリです。詳細な議論や背景情報が含まれている可能性があります。
  • Go CL 87580043: https://golang.org/cl/87580043
    • このコミットに対応するGerrit Code Reviewのチェンジリストです。コードレビューのコメントや、より詳細な変更履歴を確認できます。

参考にした情報源リンク