「H」/72/0x48が実行可能ファイルで2番目に一般的なバイトであるのはなぜですか?

「H」/72/0x48が実行可能ファイルで2番目に一般的なバイトであるのはなぜですか?

(この質問が72点なら投票しないでください!)

私はこれを実行しました:

cat /usr/bin/* |
  perl -ne 'map {$a{$_}++} split//; END{print map { "$a{$_}\t$_\n" } keys %a}' |
  grep --text . | sort -n | plotpipe --log y {1}

そしてこれを得ました:

バイト値の発生回数

(代数的なy軸を使用しても依然として指数関数的に見えます!上と下の間の距離は100倍以上大きくなります。)

数字を見てください:

:
31919597        ^H
32983719        ^B
33943030        ^O
39130281        \213
39893389        $
52237360        \211
53229196        ^A
76884442        \377
100776756       H
746405320       ^@

^@(NUL) が実行ファイルで最も一般的なバイトであることは驚くべきことではありません。 \377 (255) および ^A (1) も私にとって直感的に理解されます。

しかし、「H」(72)が実行可能ファイルで2番目に一般的なバイトであるのはなぜですか? 255と1よりも一般的なバイトですか?

背景

Perlスクリプトの場合は、Perlスクリプトで最小共通バイトを見つける必要があります。驚いたことに、Perlスクリプトを単にgrepingするのではなく、すべてのバイナリに対してコマンドを実行しました。私はNUL、1、255のような数バイトが目立つことを期待していましたが、「H」は確かにそうではありませんでした。

このグラフの入力は各バイト数(ソート)です。 y軸は数を表し、x軸は行番号(1バイトは256個の異なる値しか持てないため、1〜256)を表します。 y軸は対数スケールであるため、その差は指数よりも大きくなります。

答え1

そうだろう64ビットオペランドサイズプレフィックスamd64 機械語コード命令。

amd64実行可能ファイルでのみ発生することがわかります。

/bin/*比較すればhttp://ftp.debian.org/debian/pool/main/c/coreutils/coreutils_9.1-1_arm64.debhttp://ftp.debian.org/debian/pool/main/c/coreutils/coreutils_9.1-1_amd64.debそして http://ftp.debian.org/debian/pool/main/c/coreutils/coreutils_9.1-1_i386.deb、次の内容が表示されます。

$ for f (coreutils_9.1-1_*.deb) bsdtar xOf $f da\* | bsdtar xO ./bin/\* | xxd -p -c1 | sort | uniq -c | sort -rn | head -n 5 | grep -H --label="${${f:r}##*_}" .
amd64: 692417 00
amd64: 145689 ff
amd64:  81911 48
amd64:  48006 89
amd64:  45331 0f
arm64:1409826 00
arm64:  70391 ff
arm64:  67915 03
arm64:  49380 20
arm64:  41655 40
i386: 515346 00
i386: 171643 ff
i386:  78361 0e
i386:  69317 24
i386:  50497 83

0x48(72、 'H')はamd64の上位3つにのみあります。

ls私のamd64 Debianシステムから:

$ xxd -p -c1 =ls | sort | uniq -c | sort -rn | head -n 5
  39187 00
   7827 ff
   5565 48
   4181 20
   3393 0f

この実行可能ファイルのコードを分解すると、命令で多数の0x48バイトが見つかります。

$ objdump -d =ls | grep -cw 48
5353

ほとんどは最初の位置にあります。

$ objdump -d =ls | grep -wm10 48
    4000:       48 83 ec 08             sub    $0x8,%rsp
    4004:       48 8b 05 ad ff 01 00    mov    0x1ffad(%rip),%rax        # 23fb8 <__gmon_start__@Base>
    400b:       48 85 c0                test   %rax,%rax
    4012:       48 83 c4 08             add    $0x8,%rsp
    44b6:       68 48 00 00 00          push   $0x48
    4751:       48 89 f3                mov    %rsi,%rbx
    4754:       48 83 ec 68             sub    $0x68,%rsp
    4758:       48 8b 3e                mov    (%rsi),%rdi
    475b:       64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
    4764:       48 89 44 24 58          mov    %rax,0x58(%rsp)
$ objdump -d =ls | grep -Pc '^\s*[\da-f]+:\s+48'
5113

~によるとhttp://ref.x86asm.net/geek.html#x48、0x48は64ビットオペランドサイズ REX.Wデフォルトのオペランドの代わりに64ビットオペランドの操作を指定するOpcodeプレフィックス。

$ objdump -d =ls | pcregrep -o1 -o2 '^\s*[\da-f]+:\s+(48 .. ).*?\t(\S+)' | sort | uniq -c  | sort -rn | head
   1512 48 89 mov
   1040 48 8b mov
    630 48 8d lea
    372 48 85 test
    326 48 83 add
    198 48 39 cmp
    158 48 83 sub
     79 48 01 add
     72 48 83 cmp
     69 48 c7 movq

すべての命令は64ビットオペランドで実行されます。

関連情報