[インデックス 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
を指定していました。ParseUint
のbase
に0
を指定した場合、文字列が0x
または0X
で始まる場合は16進数、0
で始まる場合は8進数、それ以外は10進数として解釈されます。しかし、objdump
の既存のドキュメントでは「0x
プレフィックスなし」と明記されており、ユーザーが0x
を付けずに16進数を入力することを想定していました。この場合、ParseUint
は0x
プレフィックスがないと10進数として解釈しようとするため、例えばff
という入力は10進数としては無効な文字を含んでいるためエラーとなっていました。
このコミットでは、この問題を解決するために以下の変更が行われました。
strings.TrimPrefix
の導入:flag.Arg(1)
(開始アドレス)とflag.Arg(2)
(終了アドレス)の文字列に対して、まずstrings.TrimPrefix(..., "0x")
を適用します。これにより、もしユーザーが0x
プレフィックスを付けてアドレスを入力した場合、そのプレフィックスが削除されます。TrimPrefix
は、プレフィックスが存在しない場合は元の文字列をそのまま返すため、0x
プレフィックスがない場合でも問題なく動作します。strconv.ParseUint
のbase
引数の変更:ParseUint
のbase
引数を0
から16
に変更しました。これにより、TrimPrefix
によって0x
プレフィックスが取り除かれた後の文字列が、常に16進数として解釈されるようになります。例えば、0xff
はff
となり、ff
は16進数として正しく解析されます。また、元々0x
プレフィックスなしで入力されていたff
のような文字列も、TrimPrefix
によって変更されずにそのままParseUint
に渡され、base: 16
によって正しく16進数として解析されます。
この変更により、objdump
は0x
プレフィックスの有無に関わらず、ユーザーが入力した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
ファイルで行われています。
-
コメントの変更:
// counters written in hexadecimal without a leading 0x prefix.
から// counters written in hexadecimal with optional leading 0x prefix.
へ変更されました。これにより、objdump
が0x
プレフィックスを許容するようになったことがドキュメントに反映されました。同様に、printUsage
関数内のヘルプメッセージも更新されています。 -
strings
パッケージのインポート:import ("strings")
が追加され、strings.TrimPrefix
関数を使用できるようになりました。 -
アドレス解析ロジックの変更:
- 変更前:
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進数として解釈されるようになりました。 - 変更前:
関連リンク
- Go Issue #7936:
cmd/objdump: accept 0x prefix for hex addresses
https://github.com/golang/go/issues/7936 - Go CL 100060043:
cmd/objdump: actually accept hex address without "0x" prefix.
https://golang.org/cl/100060043
参考にした情報源リンク
- Go言語
strconv
パッケージドキュメント: https://pkg.go.dev/strconv - Go言語
strings
パッケージドキュメント: https://pkg.go.dev/strings - Go言語
flag
パッケージドキュメント: https://pkg.go.dev/flag