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を使用するだけで問題なく動作することをよく知っていますが、ここで何が起こっているのかを本当に理解しようとしています。

したがって、基本的に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

私が期待するようにそのアドレスに値をロードする代わりに、アセンブラーはgdbデバッガーに示されているようにRIP-Relative Addressingを出力しますが、なぜだろうと思っています。

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

代わりにeaxレジスタを使用してみました(そしてeaxをedxに移動しました)が、別の問題が発生します。gdbに記載されているように、セグメンテーション違反が発生します。

movabs 0x4b8c289006000e5,%eax

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

「解決策」を見つけたのですが、次のようになります。eaxにstr_hello_lengthのアドレスをロードしてから、eaxが指すアドレスの内容をロードすると、すべてが厄介です。

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


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

どうやらmemアドレスから間接的に値をロードしようとすると、異なるコードが生成されますか?よくわかりません。

これらの命令の構文と操作を理解するのに助けが必要なだけなので、効果的なアドレスをロードする方法をよりよく理解できます。ええ、私はEQUに切り替えて陽気な道を進むことができたと思いますが、DB宣言とそのアドレスからのロードで何が起こっているのかを理解するまで、私は本当に先に進むことができないと感じています。

4

3 に答える 3

7

答えはそうではありません。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を使用するか、[BITS64]のアセンブリコードをコンパイルしてください。

于 2012-04-03T12:20:40.360 に答える
3

アセンブラに32ビットモード(を使用bits 32)をターゲットにするように要求していますが、その32ビットマシンコードを64ビットオブジェクトファイルに入れて、x86-64マシンコードとしてアセンブルするとどうなるかを調べています。 。

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


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

この場合の重要な点は、32ビットx86には、32ビット絶対アドレス(レジスタなし)をエンコードするための2つの冗長な方法があることです。SIBバイトがある場合とない場合です。32ビットモードには、RIP相対(またはEIP相対)アドレス指定がありません。

x86-64は、短い(ModR/M + disp32)形式をRIP相対アドレッシングモードとして再利用しましたが、32ビットの絶対アドレッシングは長いModR/M + SIB + disp32エンコーディングでも引き続き使用できます。(もちろん、ベースレジスタとインデックスレジスタをエンコードしないSIBバイトを使用します。おもしろい事実:この場合、スケールフィールドは使用されませんが、AMD64はこれらのビットに対して何もしないことを選択しました)。

rbpはSIBベースとして許可されていませんか?ModRMエンコーディングの「エスケープコード」について詳しく説明します。これにより、SIB-presentやno-base-regなどの特殊なケースをエンコードできます。

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

コメントは、実効絶対アドレスを示す逆アセンブラです。RIP相対アドレス指定は、命令の後のバイト、つまり次の命令の開始からカウントされます。


movabs 0x4b8c289006000e5,%eax

デスティネーションレジスタがEAXの場合、アセンブラ(32ビットモード)は、ModR/Mバイトを含まない32ビットの絶対アドレスからmovロードする短いエンコーディングを選択します。 Intelのマニュアルでは、これを実効アドレスではなく(メモリオフセット)と呼んでいます。eaxA1 disp32moffs

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


に変更[bits 32]されました[bit 64]

32ビットint0x80LinuxABIを64ビットコードで使用するとどうなるかを参照してください。。

ネイティブの64ビットシステムコールを使用しない場合は、32ビットの実行可能ファイルを作成することをお勧めします。を使用nasm -felf32し、とリンクしgcc -m32 -nostdlib -staticます。

于 2018-03-06T01:48:43.217 に答える
0

問題は、のオフセットがstr_hello_length32ビットより大きいことである可能性があります。IA-32は、32ビットを超える変位をサポートしていません。これを回避する方法は、RIPと到達しようとしているアドレスの間の距離が32ビットに収まるという(多くの場合正しい)仮定の下で、RIP相対アドレス指定を使用することです。この場合、ベースはRIPであり、インデックスは命令の長さであるため、命令にすでにベースまたはインデックスがある場合、RIP-Relativeは使用できません。

さまざまな試みを調べてみましょう。

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相対アドレス指定が行われます。

于 2012-04-03T08:30:08.047 に答える