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

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

このコミットは、Go言語のcmd/objdumpツールにARMアーキテクチャ用の逆アセンブラ機能を追加するものです。これにより、ARMバイナリの機械語命令を人間が読めるアセンブリコードに変換できるようになります。

コミット

commit 9f2dfb856e89a8847ff2b1fc6ad95917093f3e28
Author: Russ Cox <rsc@golang.org>
Date:   Sun Jun 1 18:53:59 2014 -0400

    cmd/objdump: add arm disassembler
    
    Fixes #7452.
    
    LGTM=minux, iant
    R=minux, iant
    CC=golang-codereviews
    https://golang.org/cl/104770046

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

https://github.com/golang/go/commit/9f2dfb856e89a8847ff2b1fc6ad95917093f3e28

元コミット内容

cmd/objdump: add arm disassembler

このコミットは、cmd/objdumpツールにARM逆アセンブラを追加します。 関連するIssueは #7452 です。

変更の背景

この変更の背景には、Go言語のプロファイリングツールであるpprofweblistが、Darwin (macOS) 環境でARMアーキテクチャのバイナリを扱う際に問題が発生していたことがあります。具体的には、これらのツールが内部で逆アセンブラを必要とするにもかかわらず、cmd/objdumpにARM用の逆アセンブラが実装されていなかったため、listコマンドなどが正常に機能しませんでした。

Issue #7452 は、この問題に対処するためにARM逆アセンブラの追加を要求しており、このコミットはその解決策として導入されました。これにより、GoツールチェインがARMアーキテクチャのバイナリをより適切に分析・デバッグできるようになり、特にクロスプラットフォーム開発やARMベースのデバイス上でのGoアプリケーションのデバッグ能力が向上しました。

前提知識の解説

  • 逆アセンブラ (Disassembler): 機械語(CPUが直接実行するバイナリコード)を、人間が読めるアセンブリ言語のニーモニック(命令の略語)に変換するツールです。これにより、プログラムの低レベルな動作を理解したり、デバッグやリバースエンジニアリングを行ったりすることが可能になります。
  • cmd/objdump: Go言語の標準ツールの一つで、オブジェクトファイルや実行可能ファイルの情報を表示するためのコマンドラインツールです。一般的なUnix系のobjdumpコマンドに似ており、シンボルテーブル、セクション情報、そして逆アセンブルされたコードなどを表示できます。
  • ARMアーキテクチャ: Advanced RISC Machineの略で、モバイルデバイス、組み込みシステム、サーバーなど、幅広い分野で利用されているCPUアーキテクチャです。低消費電力と高性能を両立している点が特徴です。ARM命令セットは、固定長の32ビット命令(ARMモード)と可変長の16/32ビット命令(Thumbモード)など、複数の命令セットをサポートしています。
  • pprof: Go言語のプロファイリングツールで、CPU使用率、メモリ割り当て、ゴルーチンブロックなどのパフォーマンスデータを収集・可視化します。pprofは、実行中のプログラムのパフォーマンスボトルネックを特定するのに役立ちます。listコマンドは、特定の関数のアセンブリコードを表示するために逆アセンブラを利用します。
  • rsc.io/arm/armasm: このコミットで導入されたARM逆アセンブラの基盤となっている外部パッケージです。Russ Cox氏が開発したもので、ARM命令のデコードとアセンブリ表現への変換ロジックを提供します。

技術的詳細

このコミットの主要な技術的変更は、cmd/objdumpにARM命令のデコードとフォーマットを行うための新しいGoパッケージarmasmを統合したことです。

  1. armasm.goの追加: コミットの大部分を占めるsrc/cmd/objdump/armasm.goは、rsc.io/arm/armasmパッケージのコードをバンドルしたものです。このファイルは、ARM命令のバイナリ表現を解析し、対応するアセンブリ命令(ニーモニックとオペランド)に変換するためのロジックを含んでいます。具体的には、arm_instFormat構造体で命令のフォーマットを定義し、arm_Decode関数でバイト列から命令をデコードします。また、arm_GNUSyntaxarm_plan9Syntax関数は、それぞれGNUアセンブラ形式とPlan 9アセンブラ形式で命令を文字列として表現する機能を提供します。
  2. ビルドプロセスの更新: src/cmd/objdump/Makefileが更新され、armasm.goを生成するためのルールが追加されました。これは、bundleツール(code.google.com/p/rsc/cmd/bundle)を使用してrsc.io/arm/armasmパッケージをarmasm.goに組み込むことを示しています。これにより、armasmパッケージがobjdumpツールの一部としてビルドされるようになります。
  3. main.goの変更: src/cmd/objdump/main.goが修正され、ARMアーキテクチャがターゲットの場合に新しいARM逆アセンブラを使用するようにロジックが追加されました。これにより、objdumpツールが実行時に適切な逆アセンブラを選択できるようになります。
  4. テストの追加: src/cmd/objdump/objdump_test.goにARM逆アセンブラの機能を確認するための基本的なテストが追加されました。

この変更により、objdumpはARMバイナリを正確に解析し、その内容をアセンブリレベルで表示できるようになりました。特に、armasm.go内のarm_Decode関数は、ARM命令の複雑なエンコーディングを解釈し、レジスタ、即値、メモリアドレスなどのオペランドを抽出する中心的な役割を担っています。

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

このコミットのコアとなるコードの変更は、主に以下のファイルに集中しています。

  1. src/cmd/objdump/armasm.go:
    • このファイルは新規追加されたもので、rsc.io/arm/armasmパッケージのコードをバンドルしたものです。
    • ARM命令のデコードロジック(arm_Decode関数)と、命令の引数を解析するarm_decodeArg関数が含まれています。
    • 命令のフォーマット定義(arm_instFormat)や、レジスタ、シフト、メモリ参照などの引数型定義(arm_Reg, arm_Shift, arm_Memなど)が含まれます。
    • GNUアセンブラ形式とPlan 9アセンブラ形式で命令を文字列化する関数(arm_GNUSyntax, arm_plan9Syntax)も含まれています。
  2. src/cmd/objdump/Makefile:
    • armasm.goを生成するためのビルドルールが追加されました。
    • armasm.go: bundle ./bundle -p main -x arm_ rsc.io/arm/armasm | gofmt >armasm.go
  3. src/cmd/objdump/main.go:
    • objdumpのメインロジックに、ARMアーキテクチャのバイナリを扱う際にarmasmパッケージの逆アセンブラを使用するための条件分岐が追加されました。
  4. src/cmd/objdump/objdump_test.go:
    • ARM逆アセンブラの基本的な動作を検証するためのテストケースが追加されました。

コアとなるコードの解説

armasm.goは、ARM命令のバイナリ表現を解析し、構造化されたGoのデータ型(arm_Inst)に変換する中心的な役割を担っています。

  • arm_instFormat: ARM命令のエンコーディングパターンを定義する構造体です。maskvalueを使って命令のビットパターンを照合し、opBitsでオペコードの一部を抽出し、argsで命令の引数をどのようにデコードするかを指定します。
  • arm_Decode(src []byte, mode arm_Mode) (inst arm_Inst, err error): この関数が、実際の逆アセンブル処理の入り口です。入力バイト列srcから32ビットのARM命令を読み取り、定義されたarm_instFormatsテーブルを走査して、命令に一致するフォーマットを探します。一致するフォーマットが見つかると、そのフォーマットの定義に基づいて命令のオペコードと引数を抽出し、arm_Inst構造体として返します。条件付き命令の処理や、特殊な命令(例: BKPT)のハンドリングも行われます。
  • arm_decodeArg(aop arm_instArg, x uint32) arm_Arg: arm_Decode関数から呼び出され、個々の命令引数(レジスタ、即値、メモリ参照など)を、命令のビットパターンxと引数の種類aopに基づいてデコードします。例えば、arm_arg_R_0は命令のビット0-3からレジスタ番号を抽出し、arm_arg_imm12は12ビットの即値を抽出します。
  • arm_GNUSyntax(inst arm_Inst) string / arm_plan9Syntax(inst arm_Inst, pc uint64, symname func(uint64) (string, uint64), text io.ReaderAt) string: これらの関数は、デコードされたarm_Inst構造体を、それぞれGNUアセンブラ形式またはPlan 9アセンブラ形式の文字列に整形します。これにより、objdumpコマンドの出力が、開発者にとって馴染みのあるアセンブリ表記で表示されるようになります。特にarm_plan9Syntaxは、Goのツールチェインで一般的に使用されるアセンブリ構文に準拠しており、PC相対アドレスの解決や、特定の命令(例: LDR)の特殊なフォーマット変換も行います。

これらのコンポーネントが連携することで、GoのobjdumpツールはARMバイナリを正確に逆アセンブルし、その内容を詳細に分析できるようになります。

関連リンク

参考にした情報源リンク