プロセッサが各命令のオペコードとオペランドを決定する方法:VIMでバイナリを表示する

プロセッサが各命令のオペコードとオペランドを決定する方法:VIMでバイナリを表示する

FreeBSD 10.3 でコマンドを実行すると、変更されてview /bin/lsいないバイナリを表示できます。

ここに画像の説明を入力してください。

その後、vim / viewでコマンドを実行すると、:%!xxd次のように16進形式のファイルを表示できます。私はページの下部でvimが1708行が追加され、74行が削除されたと発表したのを見ました。

ここに画像の説明を入力してください。

コマンドでvimを閉じてから:q!再度開き、view /bin/lsvimコマンドを実行する:%!xxd -bと、以下のようにバイナリ形式のファイルが表示されます。ページの下部には4555行が追加され、74行が削除されたことがわかります。

ここに画像の説明を入力してください。

今私は知りたいです:

  • vimでコマンドを実行する:%!xxdと、いくつかの行が追加され、一部の行が削除されるのはなぜですか?:%!xxd -b

  • 16進形式の場合、つまり%!xxdコマンドを実行するときの行アドレスは、00000000、00000010、00000020、00000030などです。これは、各行に16バイトが含まれているため、0x10の増分に意味があるためです。

  • バイナリ形式の場合、つまり%!xxd -bコマンド実行時の行アドレスは、00000000、00000006、0000000c、00000012などです。これは、各行に6バイトが含まれているため、0x06の増分に意味があるためです。

  • 以前は、各バイナリが1行の各プロセッサ命令を含み、各行の先頭にその命令の相対アドレスがあり、最初の命令はゼロで始まると考えていました。しかし、vimでバイナリを観察した結果、これは本当ではありません。今私は知りたいプロセッサが各命令のオペコードとオペランドを決定する方法、ディレクティブがバイナリファイル内で1行ずつフォーマットされていない場合。


修正する:

16進形式の最後の5行は次のとおりです。

00006a70: 0100 0000 3000 0000 0000 0000 4862 0000  ....0.......Hb..
00006a80: 3e03 0000 0000 0000 0000 0000 0100 0000  >...............
00006a90: 0100 0000 0100 0000 0300 0000 0000 0000  ................
00006aa0: 0000 0000 8665 0000 d500 0000 0000 0000  .....e..........
00006ab0: 0000 0000 0100 0000 0000 0000 0a         .............

バイナリ形式の最後の5行は次のとおりです。

00006aa4: 10000110 01100101 00000000 00000000 11010101 00000000  .e....
00006aaa: 00000000 00000000 00000000 00000000 00000000 00000000  ......
00006ab0: 00000000 00000000 00000000 00000000 00000001 00000000  ......
00006ab6: 00000000 00000000 00000000 00000000 00000000 00000000  ......
00006abc: 00001010                                               .

それで、16進形式と2進形式の合計バイト数が同じだと思います。つまり、コードの最後のバイトアドレスが両方とも同じであることを意味します0x6abc

答え1

vim で :%!xxd および :%!xxd -b コマンドを実行すると、一部の行が追加され、一部の行が削除されるのはなぜですか?

vim改行が計算さ0x0aれ、バイナリにこれらの内容が含まれているため(あなたのバージョンでは74行ls...)、バイナリのこの74行は元のバイナリと異なる形式を交換すると削除され、新しい行が削除されます。 (詳細)16進表示用に追加されました。vimただ見えるものを計算するだけです0x0a

次に、プロセッサが各命令のオペコードとオペランドをどのように決定するかを知りたいと思います。

魔法!それは複雑で、このトピックについて書かれた本がたくさんあります。つまり、リンカー(またはそれに対応する)特定のバイナリ形式(あなたの場合はELF、他の形式も存在しますが、a.out、Mach-Oなど)は開始アドレスを表します。

$ readelf -h /bin/ls
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x37f0
...

プログラムがメモリに昇格した後、オペコードの実行が開始されます。開始アドレスは通常、.textバイナリファイルセクションのどこかにあります(おそらくないかもしれません)。

$ objdump -DS /bin/ls | less -p .text
...

私のOpenBSDシステムには以下が表示されます。

Disassembly of section .text:

00000000000037f0 <revnamecmp-0x460>:
    37f0:       49 89 e4                mov    %rsp,%r12
    37f3:       48 83 ec 08             sub    $0x8,%rsp
    37f7:       48 83 e4 f0             and    $0xfffffffffffffff0,%rsp
    37fb:       48 83 c4 08             add    $0x8,%rsp
...

見てみる価値のある本には、Jeff Duntemannの「Assembly Language Step-by-Step」とRyan O'NeillのELF「Learning Linux Binary Analysis」があります。

関連情報