2012-04-03 15 views
3
[bits 32] 
    global _start 

    section .data 
    str_hello  db "HelloWorld", 0xa 
    str_hello_length db  $-str_hello 

    section .text 

    _start: 

     mov ebx, 1    ; stdout file descriptor 
     mov ecx, str_hello  ; pointer to string of characters that will be displayed   
     mov edx, [str_hello_length] ; count outputs Relative addressing 
     mov eax, 4    ; sys_write 
     int 0x80    ; linux kernel system call 

     mov ebx, 0 ; exit status zero 
     mov eax, 1 ; sys_exit 
     int 0x80 ; linux kernel system call 

ここで基本的なことは、linuxのsys_writeシステムコールに渡すhello文字列の長さを持つ必要があることです。今、私はEQUを使うことができることをよく承知していますが、うまくいくでしょうが、ここで何が起こっているのかを実際に理解しようとしています。32ビットモードでNASM x86_64アセンブリ:なぜこの命令はRIP相対アドレッシングコードを生成するのですか?

私はEQUを使用すると基本的に値をロードしても問題ありません。

str_hello_length equ $-str_hello 
... 
... 
mov edx, str_hello_length 

私はDB

str_hello_length db $-str_hello 
... 
... 
mov edx, [str_hello_length]  ; of course, without the brackets it'll load the address, which I don't want. I want the value stored at that address 

代わりに、私はそれが期待するように、そのアドレスの値をロードすると、このラインを使用する場合は、アセンブラ出力はRIP相対アドレッシング、GDBに示すようにデバッガと私は単になぜ思っています。

mov 0x6000e5(%rip),%edx  # 0xa001a5 

は今、私の代わりにEAXレジスタを使用してみました(その後、EDXにEAXを移動する)、その後私は別の問題を取得しました。私は、GDBに述べたように、セグメンテーションフォールトを取得し終わる:

movabs 0x4b8c289006000e5,%eax 

はそう明らかに、別のレジスタが異なるコードを生成します。どういうわけか上位32ビットを切り捨てる必要があると思いますが、その方法はわかりません。

str_hello_lengthのアドレスでeaxをロードしてから、eaxが指すアドレスの内容を読み込み、すべてが大変です。

mov eax, str_hello_length  
mov edx, [eax] ; count 


; gdb disassembly 
mov $0x6000e5,%eax 
mov (%rax),%edx 

明らかにmemアドレスから値を間接的にロードしようとすると、異なるコードが生成されますか?私は本当に知らない。

私はこれらの命令の構文と操作を理解する上で助けが必要なので、有効なアドレスをロードする方法の理由をよく理解できます。うん、私はちょうどEQUに切り替えて私の気持ちがいいと思うけど、DB宣言とそのアドレスから読み込んでいることを理解するまで続けることができないと思う。

+0

これは32ビットモードではありません。 'rip'と' rax'は32ビットモードでは存在しません。ですから問題は、実際には64ビットコードを持つ32ビット用のコードを生成していると考えていることです。つまり、角括弧で明示的に 'rip'を指定しなくても、相対メモリアドレスを作成することはx86_64のデフォルトです。 – hirschhornsalz

+0

それは私が組み立ててリンクする方法かもしれません。私は持っている:アセンブルのためのnasm -g -f elf64 $ <とリンクするためのld -o $(PROJECT_NAME)$(OBJ)。 lf:i386の入力ファイル 'main.o 'のアーキテクチャがi386と互換性がない:x86-64出力 – Mathmagician

+0

リンクに' gcc -m32'を使うか、適切なldフラグ( ' -m elf_i386')。 – mirabilos

答えて

6

答えはありません。 x86-64は32ビットエミュレーションモードではRIP相対アドレッシングをサポートしていません(これはRIPが32ビットに存在しないため明白です)。何が起こっているのかは、nasmが64ビットとして実行しようとしている素敵な32ビットオペコードをコンパイルしていることです。 GDBはあなたの32ビットオペコードを64ビットとして逆アセンブルしており、64ビットではこれらのバイトがRIP相対のmovを意味することを伝えています。 x86-64上の64ビットと32ビットのオペコードは、シリコンで共通のデコードロジックを使用するために多くの部分がオーバーラップしています.GDBが逆アセンブルするコードは、あなたが書いた32ビットコードと似ているため混乱します実際にはプロセッサにガベージバイトを投げているだけです。

これは、nasmとは関係ありません。 32ビットプロセスで32ビットのnasmを使用するか、[BITS 64]のアセンブリコードをコンパイルしてください。

+0

うわー!それはまさに問題の原因です! [ビット32]から[ビット64]に変更され、 "mov edx、[str_hello_length]"が実行され、アドレスからロードされます。だから、私は32ビットと64ビットのプログラムでは理解できないように思えます。 – Mathmagician

0

おそらく、str_hello_lengthのオフセットが32ビットを超えている可能性があります。 IA-32は、32ビットを超える置換をサポートしていません。これを回避する方法は、RIP相対アドレス指定を使用することです(RIPと到達しようとしているアドレス間の距離が32ビットに収まることを前提としています)。この場合、ベースはRIPであり、インデックスは命令の長さなので、命令にすでにベースまたはインデックスがある場合、RIP-相対は使用できません。

のは、あなたの様々な試みを見てみましょう:

str_hello_length equ $-str_hello 
... 
... 
mov edx, str_hello_length 

は、単に即座に移動し、ここにはメモリアクセスはありませんので、何のすべてのアドレッシングはありません。

次へ:

mov eax, str_hello_length  
mov edx, [eax] ; count 

今、最初の命令がまだメモリアクセスではありませんすぐにと動き、です。 2番目の命令はメモリアクセスを持っていますが、eaxをベースにしており、変位はありません。 RIP相対値は、変位がある場合にのみ関係するため、RIP相対値はここにはありません。最後に

str_hello_length db $-str_hello 
... 
... 
mov edx, [str_hello_length]  ; of course, without the brackets it'll load the address, which I don't want. I want the value stored at that address 

ここでは、あなたの変位としてstr_hello_lengthを使用しています。上記で説明したように、これはRIP相対アドレス指定になります。

+0

おっと、AMD64プロセッサを使用していることを忘れていましたが、違いがあるかどうかはわかりません。レスポンスありがとう!私はそれを調べます。 – Mathmagician

+0

いいえ、 'default rel'がなければ、' mov edx、[str_hello_length] 'はx86-64モードでアセンブルされても、32ビットの絶対アドレス指定を使います。 –

1

アセンブラには、32ビットモード(bits 32)を対象にしていますが、その32ビットマシンコードを64ビットオブジェクトファイルに入れて、次にそれを逆アセンブルするとどうなるかを見ていますx86-64マシンコード。

したがって、x86-32とx86-64の命令エンコーディングの違いが分かります。すなわちこれは、32ビットマシンコードを64ビットのとしてデコードするときに起こります。


mov 0x6000e5(%rip),%edx # 0xa001a5

32ビットx86は(NOレジスタ付き)は、32ビット絶対アドレスを符号化する2つの冗長な方法があることである。この場合にはキーを1:SIBバイトの有無にかかわらずに。 32ビットモードには、RIP相対(またはEIP相対)アドレッシングはありません。 RIP相対アドレッシングモードとして短い(ModR/M + disp32)フォームを再利用するx86-64

、32ビット絶対アドレスながら、まだ長いModR/M + SIB + disp32エンコーディングで利用可能です。 (もちろん、ベースレジスタもインデックスレジスタもエンコードしていないSIBバイトの場合)。

RIPからのオフセットは、実際にデータが配置される絶対的な静的アドレス(64ビットコード)、0x6000e5であることに注意してください。

コメントは実効絶対アドレスを示す逆アセンブラです。 RIP相対アドレッシングは、命令の後のバイト、すなわち次の命令の開始からカウントする。


movabs 0x4b8c289006000e5,%eax

デスティネーションレジスタは、ちょうどA1 disp32(32ビットモード)EAX、アセンブラあるなしにModR/Mバイトの32ビット絶対アドレスからeaxをロード短いmov符号化を選択すると。有効アドレスの代わりにIntel's manual calls this a moffs(メモリオフセット)。

x86-64モードでは、そのオペコードは64ビットの絶対アドレスをとります。 (アドレスを最初にレジスタに取り込まずに、64ビットの絶対アドレス(RIP相対アドレスではない)からロード/格納できる点でユニークです。したがって、デコードは64ビットアドレスの一部として次の命令の一部を消費し、そのアドレスの上位バイトのいくつかが出てきます。下位32ビットの0x6000e5は正しいですし、32ビットマシンコードとしてデコードする方法です。


Changed [bits 32] to [bit 64]

What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?を参照してください。

ネイティブの64ビットシステムコールを使用しない場合は、32ビットの実行可能ファイルをビルドする方がよい。 nasm -felf32を使用し、gcc -m32 -nostdlib -staticとリンクしてください。

+0

そして、あなたはgdbの 'set disassembly-flavor intel'を使って' disas'からIntel-syntaxの逆アセンブリを取得し、 'layout reg'モードでは逆アセンブリウィンドウから得ることができます。 –

関連する問題