Homebrewで入るNASMは古すぎて、aptで入るNASMとは挙動が違う
自作CPUエミュの動作確認をしているときに、Homebrewで入るNASMとaptで入るNASMの挙動が違うことに気がついた。
挙動の違いを見る
簡単なコードをアセンブルして違いを見る。
BITS 32 org 0x7c00 sub esp, 16
aptで入るNASM
Ubuntuにaptで入るNASMを使って、アセンブルした結果をhexdumpで確認する。
$ nasm sub-test.asm -o sub-test-linux $ hexdump -C sub-test-linux 00000000 83 ec 10 |...| 00000003
0x83のopcodeは、メモリとレジスタの間で8bitの即値を操作する。 ModR/Mによってどんな操作をするのかが分かる。この場合はレジスタの値から8bitの即値を引いている。
Homebrewで入るNASM
macOSにHomebrewで入るNASMを使って、アセンブルした結果をhexdumpで確認する。
$ nasm sub-test.asm -o sub-test-mac $ hexdump -C sub-test-mac 00000000 81 ec 10 00 00 00 |......| 00000006
0x81のopcodeは、メモリとレジスタの間で32bitの即値を操作する。 この場合もModR/Mによってどんな操作をするのかが分かる。レジスタの値から8bitの即値を引いている。 オペランドに指定している0x16は8bitで収まるのに32bitの無駄に大きい値を扱っていて、うまくアセンブルできていないことがわかる。
バージョンの違いが原因だった
macOSでHomebrewを使ってインストールできる最新のNASMのバージョンは以下の通り。
$ nasm -v NASM version 0.98.40 (Apple Computer, Inc. build 11) compiled on Aug 24 2016
Ubuntuでaptを使ってインストールできる最新のNASMのバージョンは以下の通り。
$ nasm -v NASM version 2.10.09 compiled on Jun 28 2018
この通り、バージョンが大幅に違う。 appleが独自にメンテしているからバージョンの進み方が違うのかなと思ったけど、 単に古いだけっぽくて、最新のmacOS向けのビルドは以下のリンクで配布されている。 Index of /pub/nasm/releasebuilds/2.13/macosx
まとめ
このようにアセンブラのversionが違うと、出力されるopcodeが変わってくるので気をつけたい。