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

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

このコミットは、Go言語のツールであるcmd/objdumpの挙動に関するものです。objdumpは、Goバイナリを逆アセンブルするために使用されるコマンドラインツールです。このツールは、指定された開始アドレスから終了アドレスまでの機械語コードを読み取り、人間が読めるアセンブリ言語に変換して表示します。

main.goファイルは、objdumpコマンドのエントリポイントであり、コマンドライン引数の解析、バイナリファイルの読み込み、そして逆アセンブル処理の呼び出しを担当しています。

コミット

commit 5139293986cf202efe31d995e8dfb471b6f2038e
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Thu May 8 01:25:56 2014 -0400

    cmd/objdump: actually accept hex address without "0x" prefix.
    Fixes #7936.
    
    LGTM=alex.brainman, bradfitz, iant
    R=golang-codereviews, alex.brainman, bradfitz, iant
    CC=golang-codereviews
    https://golang.org/cl/100060043

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

https://github.com/golang/go/commit/5139293986cf202efe31d995e8dfb471b6f2038e

元コミット内容

cmd/objdump: actually accept hex address without "0x" prefix.
Fixes #7936.

LGTM=alex.brainman, bradfitz, iant
R=golang-codereviews, alex.brainman, bradfitz, iant
CC=golang-codereviews
https://golang.org/cl/100060043

変更の背景

このコミットは、Goのobjdumpコマンドが、逆アセンブルの開始アドレスと終了アドレスを指定する際に、16進数表記の0xプレフィックスの有無に関わらずアドレスを受け入れるようにするためのものです。

元々、objdumpのドキュメントと実装は、アドレスが「0xプレフィックスなしの16進数」として指定されることを想定していました。しかし、ユーザーが一般的な16進数表記である0xプレフィックスを付けてアドレスを入力した場合、objdumpはそれを正しく解析できず、エラーとなっていました。

この問題は、GoのIssue #7936として報告されており、このコミットはその問題を修正することを目的としています。ユーザーの利便性を向上させ、より柔軟な入力形式に対応するために、この変更が導入されました。

前提知識の解説

  • objdump: バイナリファイルを逆アセンブルするためのツールです。プログラムの実行可能コードを、CPUが理解する機械語から、人間が読めるアセンブリ言語の命令に変換して表示します。デバッグやパフォーマンス分析、セキュリティ分析などで利用されます。
  • プログラムカウンタ (PC): CPUが次に実行する命令のアドレスを保持するレジスタです。objdumpでは、逆アセンブルの開始点と終了点をこのPCアドレスで指定します。
  • 16進数表記: 数値を表現する方法の一つで、基数として16を用います。0-9とA-F(またはa-f)の16種類の記号を使用します。プログラミングにおいては、メモリのアドレスやバイナリデータを表現する際によく用いられます。0xプレフィックスは、その数値が16進数であることを示す一般的な慣習です(例: 0xFF)。
  • strconv.ParseUint: Go言語の標準ライブラリstrconvパッケージに含まれる関数です。文字列を符号なし整数(uint64)に変換するために使用されます。
    • ParseUint(s string, base int, bitSize int)というシグネチャを持ちます。
    • s: 変換する文字列。
    • base: 基数(2から36)。0を指定すると、文字列のプレフィックスに基づいて基数を自動的に推測します(例: 0xは16進数、0は8進数、それ以外は10進数)。
    • bitSize: 結果の整数型が収まるビット数(0から64)。
  • strings.TrimPrefix: Go言語の標準ライブラリstringsパッケージに含まれる関数です。文字列の先頭から指定されたプレフィックスを削除した新しい文字列を返します。プレフィックスが存在しない場合は、元の文字列をそのまま返します。
    • TrimPrefix(s, prefix string)というシグネチャを持ちます。
    • s: 処理対象の文字列。
    • prefix: 削除するプレフィックス文字列。

技術的詳細

このコミットの技術的な核心は、objdumpコマンドが受け取る開始アドレスと終了アドレスの文字列を、0xプレフィックスの有無に関わらず正しく16進数として解析できるようにすることです。

変更前は、strconv.ParseUint(flag.Arg(1), 0, 64)のように、base引数に0を指定していました。ParseUintbase0を指定した場合、文字列が0xまたは0Xで始まる場合は16進数、0で始まる場合は8進数、それ以外は10進数として解釈されます。しかし、objdumpの既存のドキュメントでは「0xプレフィックスなし」と明記されており、ユーザーが0xを付けずに16進数を入力することを想定していました。この場合、ParseUint0xプレフィックスがないと10進数として解釈しようとするため、例えばffという入力は10進数としては無効な文字を含んでいるためエラーとなっていました。

このコミットでは、この問題を解決するために以下の変更が行われました。

  1. strings.TrimPrefixの導入: flag.Arg(1)(開始アドレス)とflag.Arg(2)(終了アドレス)の文字列に対して、まずstrings.TrimPrefix(..., "0x")を適用します。これにより、もしユーザーが0xプレフィックスを付けてアドレスを入力した場合、そのプレフィックスが削除されます。TrimPrefixは、プレフィックスが存在しない場合は元の文字列をそのまま返すため、0xプレフィックスがない場合でも問題なく動作します。
  2. strconv.ParseUintbase引数の変更: ParseUintbase引数を0から16に変更しました。これにより、TrimPrefixによって0xプレフィックスが取り除かれた後の文字列が、常に16進数として解釈されるようになります。例えば、0xffffとなり、ffは16進数として正しく解析されます。また、元々0xプレフィックスなしで入力されていたffのような文字列も、TrimPrefixによって変更されずにそのままParseUintに渡され、base: 16によって正しく16進数として解析されます。

この変更により、objdump0xプレフィックスの有無に関わらず、ユーザーが入力した16進数アドレスを柔軟に受け入れられるようになりました。

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

--- a/src/cmd/objdump/main.go
+++ b/src/cmd/objdump/main.go
@@ -10,7 +10,7 @@
 //
 // Objdump disassembles the binary starting at the start address and
 // stopping at the end address. The start and end addresses are program
-// counters written in hexadecimal without a leading 0x prefix.
+// counters written in hexadecimal with optional leading 0x prefix.
 //
 // It prints a sequence of stanzas of the form:
 //
@@ -40,12 +40,13 @@ import (
 	"log"
 	"os"
 	"strconv"
+	"strings"
 )
 
 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")
-	fmt.Fprintf(w, "start and end are hexadecimal numbers with no 0x prefix.\n")
+	fmt.Fprintf(w, "start and end are hexadecimal numbers with optional leading 0x prefix.\n")
 }
 
 func usage() {
@@ -79,11 +80,11 @@ func main() {
 		log.Fatalf("reading %s: %v", flag.Arg(0), err)
 	}
 
-	start, err := strconv.ParseUint(flag.Arg(1), 0, 64)
+	start, err := strconv.ParseUint(strings.TrimPrefix(flag.Arg(1), "0x"), 16, 64)
 	if err != nil {
 		log.Fatalf("invalid start PC: %v", err)
 	}
-	end, err := strconv.ParseUint(flag.Arg(2), 0, 64)
+	end, err := strconv.ParseUint(strings.TrimPrefix(flag.Arg(2), "0x"), 16, 64)
 	if err != nil {
 		log.Fatalf("invalid end PC: %v", err)
 	}

コアとなるコードの解説

変更は主にsrc/cmd/objdump/main.goファイルで行われています。

  1. コメントの変更: // counters written in hexadecimal without a leading 0x prefix. から // counters written in hexadecimal with optional leading 0x prefix. へ変更されました。これにより、objdump0xプレフィックスを許容するようになったことがドキュメントに反映されました。同様に、printUsage関数内のヘルプメッセージも更新されています。

  2. stringsパッケージのインポート: import ("strings")が追加され、strings.TrimPrefix関数を使用できるようになりました。

  3. アドレス解析ロジックの変更:

    • 変更前:
      start, err := strconv.ParseUint(flag.Arg(1), 0, 64)
      // ...
      end, err := strconv.ParseUint(flag.Arg(2), 0, 64)
      
    • 変更後:
      start, err := strconv.ParseUint(strings.TrimPrefix(flag.Arg(1), "0x"), 16, 64)
      // ...
      end, err := strconv.ParseUint(strings.TrimPrefix(flag.Arg(2), "0x"), 16, 64)
      

    この変更がこのコミットの最も重要な部分です。 flag.Arg(1)flag.Arg(2)はそれぞれコマンドライン引数として渡された開始アドレスと終了アドレスの文字列です。 strings.TrimPrefix(..., "0x")によって、入力文字列の先頭に"0x"があればそれが削除されます。例えば、入力が"0x123"であれば"123"に、"456"であればそのまま"456"になります。 その後、strconv.ParseUintの第2引数(base)が0から16に変更されています。これにより、TrimPrefixによって処理された文字列が常に16進数として解析されるようになります。これにより、ユーザーが0xを付けても付けなくても、アドレスが正しく16進数として解釈されるようになりました。

関連リンク

参考にした情報源リンク