5

ここに新しい男がいますが、すでに質問があります。

Jeff Duntemann のアセンブリ ブックで使用されているサンプル コードを適用しています。データ レジスタに格納されている整数値をターミナルに出力したいですか?

以下のコードが行うことは、文字列を正常に出力し、正常に ECX に値をプッシュしますが、次のようになると:

                pop ecx
                mov eax,4                          
                mov ebx,1                          
                mov edx, ecx

                int 80h  

mov eax,4 などで指示したと思いますが、端末にedxの内容が表示されません。

「ポインタ」(しゃれた意図)を教えてくれる人はいますか?

参照用コード (2012 年 6 月 17 日修正) :

SECTION .data
    submessage: db "I am subtracting 5 from 10!", 10
    msglen: equ $-submessage

    ansmsg: db "Answer is:", 10
    msglen2: equ $-ansmsg
    EOL: db 10

SECTION .bss
    msg: resd 2                                         ; reserve space for 2 dwords

SECTION .text

global _start

_start: nop

;Displays test on shell
                    mov eax,4                           ;print to terminal     
                    mov ebx,1                                      
                    mov ecx, submessage
                    mov edx, msglen
                    int 80h                             ;"I am subtracting 5 from 10!"

                    mov eax,4                           ;print to terminal   
                    mov ebx,1                                            
                    mov ecx, ansmsg
                    mov edx, msglen2
                    int 80h                             ;"Answer is..."

;Subtraction operation below:                  
                            mov edx, 10
                            sub edx, 5
                            mov [msg], edx                ; store 5 in msg


; now we need to print  msg to terminal                            


                            mov eax, 4                  ;print to terminal 
                            mov ebx, 1
                            mov dword [msg+1], 0xa      ;helps prints something out! 

                    ;Encountered problem here= prints out 'Answe' instead of integer '5'

                            push dword 2                ; store size of msg
                            push dword [msg]            ; push to stack contents of msg 
                            int 80h                    

                            add esp, 3                  ;clean stack (2 push calls *4)
                            int 80h

; I like labels :)

sys_exit:                            mov eax,1                   ;exit status
                                    mov ebx,0                        
                                    int 80h  
                                        nop

PS-行のインデントがうまくいかない場合は、どうすれば改善できるか知りたいです。最初の学習の「ハンプ」を乗り越えると、IMHO学習アセンブリはより魅力的になります:)

4

2 に答える 2

2

まず、質問をありがとうございます。以前はよく知らなかった int 80h について勉強するきっかけになりました。

あなたのプログラムは現在の形で何をしますか? それは何かを印刷しますか?頭の中で正しく実行していれば、最初のメッセージを出力してからクラッシュするはずです。バッファリングが原因で、クラッシュが疑われる前の最初のメッセージさえ表示されない場合があります。

int 80h/eax=4 は write() 関数にマップされます。完全なドキュメントを取得するには、「man 2 write」を実行します。関数のプロトタイプは次のとおりです。

ssize_t write(int fd, const void *buf, size_t count);

したがって、eax = 4、ebx = fd、ecx = buf、edx = カウントです。コードのコメントをより詳しくしたい場合は、「mov eax,4」は「文字列をファイルに書き込む」ことを意味し、「mov ebx,1」は「STDOUT (端末) にマップされるファイル #1 を指定する」ことを意味します。 .

最初の int 80h 呼び出しで何かを出力することを期待しています。2 番目の int 80h 呼び出しは疑わしいものです。この時点で、eax と ebx は変更されていません。ただし、edx も変更されず、最初の文字列の文字列長が保持されます。さらに問題なのは、値 5 を ecx に入れていることです。ecx は、書き込まれる値ではなく、書き込まれる文字列へのポインターを保持します。したがって、そこに 5 を入れて割り込みを呼び出すと、int はアドレス 0x00000005 から始まる文字列を出力しようとします。これにより、ほぼ確実にセグメンテーション違反 (クラッシュ) が発生します。

また、int 80h ドキュメントを正しく読んでいる場合、例のプッシュ/ポップ スタック操作には関連性がありません。

数値を文字列として出力するのは、ASM ではちょっと面倒です。数値を文字列に変換する必要があり、int 80h を使用して出力できます。1桁だけ印刷したい場合にカンニングできる方法は次のとおりです。

  • .data セクションで、int2char: 'int2char: db 0' という新しいバイトを宣言します。
  • ecx から 5 を引いた後、48 を加えて数字を ASCII に変換します (これにより、5 ではなく「5」が得られます)。
  • ecx を int2char に格納する
  • int2char のアドレスを ecx に移動
  • 1文字だけを印刷したいので、edxを1に設定します

1 桁以上の数値の変換は、これが機能するようになったら、演習として残します。

幸運を!

于 2012-06-09T01:02:30.783 に答える
1

プッシュメッセージは関連性があると思います。「hello world」プログラムの別の例を次に示します。

section     .text
    global _start                       ;must be declared for linker (ld)

_syscall:           
    int     0x80            ;system call
    ret

_start:                         ;tell linker entry point

    push    dword len       ;message length
    push    dword msg       ;message to write
    push    dword 1         ;file descriptor (stdout)
    mov     eax,0x4         ;system call number (sys_write)
    call    _syscall        ;call kernel

                         ;the alternate way to call kernel:
                         ;push   eax
                         ;call   7:0

    add     esp,12          ;clean stack (3 arguments * 4)

    push    dword 0         ;exit code
    mov     eax,0x1         ;system call number (sys_exit)
    call    _syscall        ;call kernel

                         ;we do not return from sys_exit,
                         ;there's no need to clean stack
section .data

    msg     db      "Hello, world!",0xa     ;our dear string
    len     equ     $ - msg                 ;length of our dear string

さて、私は上記のコードを書きませんでした、それはここから来ました:

http://www.cin.ufpe.br/~if817/arquivos/asmtut/index.html#helloworld

したがって、この作成者が値をスタックに移動し、スタックからパラメーターを取得するカーネル ルーチンを呼び出すことができることがわかります。これは実際には私にとってより理にかなっています.paramsがc関数に渡される方法だと思ったからです。特定のレジスタを介してパラメーターを渡すことは意味がありません。メソッドに 30 個のパラメーターがある場合、それをどのようにレジスターに渡すのでしょうか?

いずれにせよ、上記のコードをコンパイルしてリンクし、Macbook Pro で実行したところ、問題なく動作しました。

とにかく、上記の例と同じくらい素晴らしいですが、あまり教えてくれません。これは、レジスタから任意の値を出力する方法のより良い例だと思います。

だから私が欲しいのは、定義済みの文字列ではなく、実際のプログラミング構造です。変数。

簡単に、次のようにして 32 ビット変数を定義できます。

section .bss
    msg:     resd 2

これにより、「msg」と呼ばれる変数 (またはメモリの場所) が得られ、そこにデータを格納できます。resd で宣言することにより、予約するダブルワードの数を定義しています。ダブルワードは 32 ビットなので、2 つのダブルワードを予約することを宣言します。なぜ2?すぐにお話しします。

ここまでは順調ですね。

あとは、値をそのメモリ ロケーションに移動し、hello, world の例に従うだけですよね? だから私はこれをコード化しました:

section     .text
    global _start                       ;must be declared for linker (ld)

_syscall:           
    int     0x80            ;system call
    ret

_start:                     ;tell linker entry point
    mov dword [msg], 'h'    ;move the letter h into my memory location
    mov dword [msg+1], 0xa  ;this is so important, but other author's gloss over it
                            ; the line terminator is essential in order to
                            ; print something out
    push    dword 2         ;message length
    push    dword msg       ;message to write
    push    dword 1         ;file descriptor (stdout)
    mov     eax,0x4         ;system call number (sys_write)
    call    _syscall        ;call kernel

                         ;the alternate way to call kernel:
                         ;push   eax
                         ;call   7:0

    add     esp,12          ;clean stack (3 arguments * 4)

    push    dword 0         ;exit code
    mov     eax,0x1         ;system call number (sys_exit)
    call    _syscall        ;call kernel

                         ;we do not return from sys_exit,
                         ;there's no need to clean stack
section .bss
    msg:     resd 1

だからあまり変わっていません。初期化されていないデータ用の .bss セクションを追加しました。静的文字列よりもはるかに便利です。静的な値 (h の文字) を msg メモリの場所に移動していますが、このように簡単にレジスタを移動することもできます。

mov [msg], eax

次の行は非常に重要ですが、すべての「hello world」の作成者はそれをざっと見過ごしています。その 0xa は、標準出力が必要とする行ターミネータであるか、値を表示しません。これは、なぜ私のものが何も印刷されないのかを理解しようとして、長い間私を夢中にさせました。その行末記号が必要です。これが、変数を 1 つではなく 2 つの値を保持するように定義する必要がある理由でもあります。その行末記号を保存する必要があります。

あとは簡単です。パラメータをスタックに投げて、カーネルを呼び出します。これで、実際の変数データの場所から任意のデータを出力する方法の真の例が得られました。楽しみ!

于 2012-06-15T23:26:46.497 に答える