1. ホーム
  2. assembly

[解決済み] x86-64におけるmovqとmovabsqの違いについて

2022-02-08 23:31:57

質問

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

x86-64インテルアーキテクチャのデータ移動命令についてです。私が読んだのは、通常の movq 命令は、32ビットの2の補数で表現できる即値オペランドしか持てないのに対して movabsq は、ソースオペランドに任意の64ビット即値を持つことができ、デスティネーションはレジスタのみです。

もう少し詳しく教えてください。つまり、64ビットの即値は movabsq 命令のみですか?また、即値からレジスタにのみですか?64ビットの即値からメモリに移動する方法がわからないのですが。それとも、私はここで何か重要なことを勘違いしていたのでしょうか。

解決方法は?

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 . (と同じno-ModRMオペコードのREX.W=1版です)。 mov r32, imm32 ) AT&T: mov / movq / movabs

(バイト数のみは、レジスタのデスティネーション、またはSIBバイトやdisp8/disp32を必要としないアドレッシングモード:opcode + ModR/M + imm32だけです).

インテルシンタックスのアセンブラには、以下のような32ビット定数を最適化するものがあります (GASではありません)。 mov rax, 1 を5バイトの mov r32, imm32 (NASMはこうしています)、一方(YASMのように)他の人は7バイトの mov r/m64, sign-extended-imm32 . どちらも大きな定数のときだけ、特別なニーモニックを使わずにimm64エンコードを選択します。

または equ という定数の場合、YASMは小さい定数でも10バイト版を使います、残念ながら。


AT&T構文を持つGASの場合

movabsq は、マシンコードエンコーディングが64ビット値(即値定数または絶対メモリアドレス)を含むことを意味します。 (の特殊な形式がもう一つあります。 mov は、絶対アドレスから/への al/ax/eax/rax のロード/ストアで、その 64 ビットバージョンは相対ではなく 64 ビットの絶対アドレスを使用します。 AT&Tの構文では、それを movabs も同様に、例えば movabs 0x123456789abc0, %eax ).

のように小さい数字でも movabs $1, %rax 10バイトのままです。

この中には、以下のような記載があります。 x86-64ガイドの新機能 をAT&T構文で使用しています。


しかし mov ニーモニック( q オペランドサイズのサフィックスが mov r/m64, imm32mov r64, imm64 を、即時の大きさに応じて変更することができます。 (参照 x86-64のAT&T命令のmovqとmovabsqの違いは何ですか? の大きなアセンブル時定数に対して GAS が何をしたのかについて、この回答の最初のバージョンが間違っていたため、そのフォローアップが存在します。 movq .)

しかし、シンボルアドレスはリンク時まで分からないので、アセンブラがエンコーディングを選択するときには利用できません。 少なくとも Linux の ELF オブジェクトファイルをターゲットにする場合、GAS は movabs 32ビット・アブソリュートを意図していたことになります。 (YASMも同じように mov rsi, string をR_X86_64_32リロケーションで指定しましたが、NASMのデフォルトは movabs R_X86_64_64リロケーションを生成します)。

何らかの理由でシンボル名を(通常より良いRIP相対LEAの代わりに)絶対即値で使用したい場合、以下のものが必要です。 movabs

(OS XのMach-O64のようなターゲットで。 movq $symbol, %rax は、32ビットの絶対アドレスは決して有効ではないので、常にimm64エンコーディングを選択することができます。 SOにあるMacOSのQ&Aで、自分のコードが movq でレジスタにデータアドレスを入れることができます)。


Linux/ELFでの例で $symbol 即時

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 (符号付き) vs. R_X86_64_32 (符号なし) vs. (符号なし) R_X86_64_PC32 (PC相対)32bitリロケーション。

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

そしてもちろん、これはPIE実行ファイルにはリンクしません。なぜなら32ビットの絶対再配置があるからです。 movq $symbol, %rax では動作しません。 gcc foo.S 最近のLinuxディストロでは . x86-64 Linuxでは32ビットの絶対アドレスは使えなくなった? . (正しい解決策はRIP相対LEA、または静的な実行ファイルを作ることであって、実際に movabs ).


movq は常に7バイトまたは10バイトの形式である。 mov $1, %rax アラインメントのために長い命令が必要な場合以外は(後でNOPでパディングする代わりに)。 最近のx86では、どのような方法で効率よく命令長を伸ばせるのでしょうか? ). 使用方法 mov $1, %eax を使えば5バイトのフォームになります。

注目すべきは movq $0xFFFFFFFF, %rax は7バイトの形式を使うことができません。なぜなら、7バイトの形式は 符号拡張 32ビットの即値が必要で、imm64エンコーディングまたは %eax デスティネーションエンコーディングです。 GASはこの最適化をしてくれませんので、10バイトのエンコーディングから抜け出せないのです。 そのため mov $0xFFFFFFFF, %eax .

movabs を直接入力する場合は、常に imm64 形式になります。

( movabs にもすることができます。 MOVエンコーディング のように,64 ビットの絶対アドレスと RAX を送信元または送信先として指定します. REX.W + A3 MOV moffs64, RAX ).


64ビットの即時値をメモリに移動させる方法がわからないのですが。

それは別の質問で、答えは「できない」です。 その MOVのinsn refマニュアルエントリ imm64即時オペランドを持つ唯一の形式は、r/m64ではなく、レジスタの行き先を持つだけだからです。

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

関連する なぜ64ビットの即値はメモリに移動できないのか? - コンピュータ・アーキテクチャ / ISA設計上の理由。