web-dev-qa-db-ja.com

x86-64でのmovqとmovabsqの違い

私はここの新人で、アセンブリ言語を勉強し始めました。ですから、私が間違っている場合、またはこの投稿が意味をなさない場合は削除してください。

X86-64 Intelアーキテクチャでのデータ移動命令について話している。私は、通常のmovq命令は32ビットの2の補数として表すことができる即時ソースオペランドしか持てないのに対し、movabsq命令は次のように任意の64ビットの即値を持つことができることを読みましたそのソースオペランドであり、宛先としてのみレジスタを持つことができます。

これについて詳しく説明してもらえますか?つまり、movabsq命令のみを使用して64ビットの即値を移動できるということですか?そして、即値からレジスターまで? 64ビットの即値をメモリに移動する方法がわかりません。あるいは、ここで何か重要なことを間違えたのかもしれません。

22
IgNite

NASM/Intel構文では、_mov r64, 0x..._は定数に基づいて MOVエンコーディング を選択します。即値オペランドで選択できる4つがあります。

  • 5バイト_mov r32, imm32_。 ( 64ビットレジスタを満たすために常に拡張されるゼロ拡張 )。 AT&T:mov/movl
  • 6バイト以上_mov r/m32, imm32_。メモリの宛先にのみ役立ちます。 AT&T:mov/movl
  • 7バイト以上_mov r/m64, sign-extended-imm32_。 メモリに8バイトを格納できます、または64ビットレジスタを負の値に設定できます。 AT&T:mov/movq
  • 10バイト_mov r64, imm64_。 (これは_mov r32, imm32_と同じ非ModRMオペコードのREX.W = 1バージョンです)AT&T:mov/movq/movabs

(バイトカウントは、レジスタの宛先、またはSIBバイトまたはdisp8/disp32を必要としないアドレッシングモードの場合のみです。opcode+ ModR/M + imm32のみです。)

一部のインテル構文アセンブラー(GASは除く)は、_mov rax, 1_などの32ビット定数を5バイト_mov r32, imm32_(NASMがこれを行う)に最適化しますが、他の(YASMなど)は7バイト_mov r/m64, sign-extended-imm32_。どちらも、特別なニーモニックを使用せずに、大きな定数に対してのみimm64エンコーディングを選択します。

または、equ定数を使用すると、YASMは残念ながら小さな定数でも10バイトバージョンを使用します。


AT&T構文のGASで

movabsqは、マシンコードのエンコードに64ビット値が含まれることを意味します:即時定数または絶対メモリアドレス(別のグループがありますal/ax/eax/rax from/toから絶対アドレスをロード/ストアするmovの特殊な形式、および64ビットバージョンは相対ではなく64ビットの絶対アドレスを使用します。AT&T構文はmovabsも呼び出します。たとえば、 _movabs 0x123456789abc0, %eax_)。

_movabs $1, %rax_のように数が少ない場合でも、10バイトバージョンを取得します。

この一部は、AT&T構文を使用したこの x86-64ガイドの新機能 で言及されています。


ただし、movニーモニック(qオペランドサイズサフィックスの有無にかかわらず)は、イミディエートのサイズに応じて、_mov r/m64, imm32_と_mov r64, imm64_の間で選択します。 ( x86-64 AT&T命令movqとmovabsqの違いは何ですか を参照してください。この回答の最初のバージョンは、movqの大規模なアセンブル時定数を使用してGASが何をしたかを誤って推測したために存在します。)

ただし、シンボルアドレスはリンク時まで認識されないため、アセンブラがエンコーディングを選択している場合は使用できません。少なくともLinux ELFオブジェクトを対象とする場合ファイル、GASは、movabsを使用しなかった場合は、32ビットのアブソリュートを意図していると想定します。 (YASMは、R_X86_64_32再配置で_mov rsi, string_に対して同じことを行いますが、NASMはデフォルトでmovabsになり、R_X86_64_64再配置を生成します。)

何らかの理由でシンボル名を(通常より優れたRIP相対LEAではなく)絶対イミディエイトとして使用する場合は、movabsが必要です

(OS XのMach-O64のようなターゲットでは、32ビットの絶対アドレスは決して有効ではないため、_movq $symbol, %rax_は常にimm64エンコーディングを選択する場合があります。SO人々は、コードがmovqと連携してデータアドレスをレジスターに入れると言ったと思います)


_$symbol_即時のLinux/ELFの例

_mov    $symbol, %rdi     # GAS assumes the address fits in 32 bits
movabs $symbol, %rdi     # GAS is forced to use an imm64


lea    symbol(%rip), %rdi  # 7 byte RIP-relative addressing, normally the best choice for position-independent code or code loaded outside the low 32 bits

mov    $symbol, %edi    # optimal in position-dependent code
_

GASを使用してオブジェクトファイルにアセンブルし(_.bss; symbol:_を使用)、これらの再配置を取得します。 _R_X86_64_32S_(符号付き)と_R_X86_64_32_(符号なし)と_R_X86_64_PC32_(PC相対)の32ビット再配置の違いに注意してください。

_0000000000000000 <.text>:
   0:   48 c7 c7 00 00 00 00    mov    $0x0,%rdi        3: R_X86_64_32S .bss
   7:   48 bf 00 00 00 00 00 00 00 00   movabs $0x0,%rdi        9: R_X86_64_64  .bss
  11:   48 8d 3d 00 00 00 00    lea    0x0(%rip),%rdi        # 18 <.text+0x18>  14: R_X86_64_PC32       .bss-0x4
  18:   bf 00 00 00 00          mov    $0x0,%edi        19: R_X86_64_32 .bss
_

非PIE実行可能ファイル(_gcc -no-pie -nostdlib foo.s_)にリンクすると、次のようになります。

_4000d4:       48 c7 c7 f1 00 60 00      mov    $0x6000f1,%rdi
4000db:       48 bf f1 00 60 00 00 00 00 00   movabs $0x6000f1,%rdi
4000e5:       48 8d 3d 05 00 20 00      lea    0x200005(%rip),%rdi     # 6000f1 <__bss_start>
4000ec:       bf f1 00 60 00            mov    $0x6000f1,%edi
_

そしてもちろん、32ビットの絶対再配置のため、これはPIE実行可能ファイルにリンクしません。 _movq $symbol, %rax_は、最新のLinuxディストリビューションでは通常の_gcc foo.S_では機能しません2ビット絶対アドレスは、x86-64 Linuxでは許可されなくなりましたか? 。 (正しいソリューションは、RIP相対LEA、または実際にmovabsを使用せずに静的実行可能ファイルを作成することです)。


movqは常に7バイトまたは10バイトの形式であるため、(後でNOPでパディングするのではなく)整列のために長い命令が必要でない限り、_mov $1, %rax_を使用しないでください。 効率的に使用できるメソッド最新のx86で命令長を拡張しますか? )。 _mov $1, %eax_を使用して、5バイト形式を取得します。

_movq $0xFFFFFFFF, %rax_は7バイト形式を使用できないことに注意してください。これは、sign-extended32ビット即値では表現できず、imm64エンコードまたは_%eax_宛先エンコーディング。 GASはこの最適化を行わないため、10バイトのエンコーディングのままです。あなたは間違いなく_mov $0xFFFFFFFF, %eax_を求めています。

直接ソースを持つmovabsは常にimm64形式です。

movabsは、64ビットの絶対アドレスとRAXをソースまたは宛先として持つ MOVエンコーディング にすることもできます:_REX.W + A3_ _MOV moffs64, RAX_など)。


64ビットの即値をメモリに移動する方法がわかりません。

それは別の質問であり、答えは次のとおりです。できません。 MOVのinsn ref手動エントリ は、これを明確にします。imm64即値オペランドを持つ唯一の形式は、r/m64ではなく、レジスタの宛先のみを持ちます。

値が符号拡張された32ビットの即値に収まる場合、movq $0x123456, 32(%rdi)はメモリに8バイトのストアを行います。制限は、上位32ビットがビット31のコピーでなければならないことです。これは、符号拡張されたimm32としてエンコード可能でなければならないためです。

20
Peter Cordes