2011-11-12 6 views
2

予想される出力に対してアセンブリで書いたさまざまなコードの出力をテストするPythonスクリプトを作成しようとしています。しかし、出力をファイルにリダイレクトするのが難しいです。 私は次のように書かれている:linuxのアセンブリコードをテキストファイルにリダイレクトした結果

extern printf 
LINUX equ  80H  ; interupt number for entering Linux kernel 
EXIT equ  1  ; Linux system call 1 i.e. exit() 
section .data 
    intfmt: db "%ld", 10, 0 

segment .text 
    global main 


main: 
    push rax 
    push rsi 
    push rdi 
    mov rsi, 10 
    mov rdi, intfmt 
    xor rax, rax 
    call printf 
    pop rdi 
    pop rsi 
    pop rax 
    call os_return  ; return to operating system 


os_return: 
    mov rax, EXIT  ; Linux system call 1 i.e. exit() 
    mov rbx, 0  ; Error code 0 i.e. no errors 
    mov rcx, 5 
    int LINUX  ; Interrupt Linux kernel 

私は、コンソールで次の操作を実行するprocede:

nasm -f elf64 basic.asm 
gcc -m64 -o basic basic.o 
./basic 

画面に10を出力します。 ただし入力した場合

./basic > basic.txt 
cat basic.txt 

basic.txtが空のファイルとして表示されます。 私の全体的な目標は、各アセンブリファイルをループしてファイルをコンパイルして実行し、このスクリプトの出力をファイルにリダイレクトするシェルスクリプトを作成することです。しかし、私はそれが単一のファイルで動作するようになるまで、これを行うことはできません。 私はprintfへの呼び出しと何か関係があると思いましたか?私はprintfがSTDOUTに書き込むという錯覚に陥っていましたが。

ありがとうございます!

+0

をした何かを学んだ願っていますコンソール。アセンブリがstd outに印刷されていることを確認しましたか? – VolatileDream

+0

'./basic | 'を実行すると、画面には何が表示されますか?猫? –

+0

John - ./basic |猫はまだ何も返さない Jex - 私はprintfのドキュメントを見て、それがstdoutに印刷すると言います。 –

答えて

7

リダイレクトは正しいです。問題は生成しているアセンブリ内になければなりません。

このような問題をデバッグするツールはstraceです。あなたは明確に所望の出力を見ることができます

strace ./basic 
... 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa5bb8da000 
write(1, "10\n", 3)      = 3 
10 
write(1, "z\377n\f\377\177\0\0\0\0\0\0\0\0\0\0\202\377n\f\377\177\0\0\362\377n\f\377\177\0\0"..., 139905561665008 <unfinished ... exit status 0> 

、だけでなく、いくつかの「迷」書き込み:straceの下であなたのプログラムを実行し、表示されます。その書き込みはどこから来ますか?救助へ

GDB:

gdb -q ./basic 
Reading symbols from /tmp/basic...done. 

(gdb) catch syscall write 
Catchpoint 1 (syscall 'write' [1]) 
(gdb) r 

Catchpoint 1 (call to syscall 'write'), 0x00007ffff7b32500 in __write_nocancel() 
(gdb) bt 
#0 0x00007ffff7b32500 in __write_nocancel() at ../sysdeps/unix/syscall-template.S:82 
#1 0x00007ffff7acd133 in _IO_new_file_write (f=0x7ffff7dd7780, data=0x7ffff7ff8000, n=3) at fileops.c:1276 
#2 0x00007ffff7ace785 in new_do_write (fp=0x7ffff7dd7780, data=0x7ffff7ff8000 "10\n", to_do=3) at fileops.c:530 
#3 _IO_new_do_write (fp=0x7ffff7dd7780, data=0x7ffff7ff8000 "10\n", to_do=3) at fileops.c:503 
#4 0x00007ffff7accd9e in _IO_new_file_xsputn (f=0x7ffff7dd7780, data=0x601023, n=1) at fileops.c:1358 
#5 0x00007ffff7a9f9c8 in _IO_vfprintf_internal (s=0x7ffff7dd7780, format=<value optimized out>, ap=0x7fffffffda20) at vfprintf.c:1644 
#6 0x00007ffff7aaa53a in __printf (format=0x7ffff7ff8000 "10\n") at printf.c:35 
#7 0x000000000040054f in main() 

良い、これは書くことが期待呼び出しです。

(gdb) c 
10 

Catchpoint 1 (returned from syscall 'write'), 0x00007ffff7b32500 in __write_nocancel() at ../sysdeps/unix/syscall-template.S:82 
82 in ../sysdeps/unix/syscall-template.S 

これはsyscallからの単なる戻り値です。書き込みは成功しましたか? (上記の出力を確認したのでわかりましたが、確認してください)

(gdb) p $rax 
$1 = 3 

良い。書き込みは予想された3文字を書きました。

(gdb) c 

Catchpoint 1 (call to syscall 'write'), 0x0000000000400577 in os_return() 

これは私たちが期待していなかった書き込みです。どこから?

(gdb) bt 
#0 0x0000000000400577 in os_return() 
#1 0x0000000000400557 in main() 
(gdb) disas 
Dump of assembler code for function os_return: 
    0x0000000000400557 <+0>: movabs $0x1,%rax 
    0x0000000000400561 <+10>: movabs $0x0,%rbx 
    0x000000000040056b <+20>: movabs $0x5,%rcx 
    0x0000000000400575 <+30>: int $0x80 
=> 0x0000000000400577 <+32>: nop 
    0x0000000000400578 <+33>: nop 
    0x0000000000400579 <+34>: nop 
    0x000000000040057a <+35>: nop 
    0x000000000040057b <+36>: nop 
    0x000000000040057c <+37>: nop 
    0x000000000040057d <+38>: nop 
    0x000000000040057e <+39>: nop 
    0x000000000040057f <+40>: nop 
End of assembler dump. 
(gdb) quit 

だからあなたのシステムコールは、write(2)を実行する代わりにexit(2)を期待します。なぜこれが起こったのですか?

あなたは間違ってEXITを定義したので:

grep 'define .*NR_exit' /usr/include/asm/unistd*.h 
/usr/include/asm/unistd_32.h:#define __NR_exit    1 
/usr/include/asm/unistd_32.h:#define __NR_exit_group   252 
/usr/include/asm/unistd_64.h:#define __NR_exit    60 
/usr/include/asm/unistd_64.h:#define __NR_exit_group   231 

以上のことから、あなたはEXITは、64ビットモードで32ビットモードでは1が、60であることを伝えることができます。

NR_writeはどうですか? 64ビットモードで1ですか?

grep 'define .*NR_write' /usr/include/asm/unistd_64.h 
#define __NR_write    1 
#define __NR_writev    20 

実際にはそうです。だから私たちは "どこから迷いが出てきたのですか"という問題を解決しました。パズル。 60であることをEXITを固定し、straceの下で再実行、我々は今、次を参照してください。

... 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa5bb8da000 
write(1, "10\n", 3)      = 3 
10 
_exit(1)        = ? 

まだ右されていないこと。 でなく、_exit(1)としてください。 x86_64ABIを見て、自分のレジスタの使用が誤っていることが明らかになった:

はそれを修正(および偽mov rcx, 5を削除)など、システムコール番号は%raxにする必要がありますが、%rdiで引数、%rsi%rdx、我々は最終的に取得しますstraceから所望の出力:

... 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa5bb8da000 
write(1, "10\n", 3)      = 3 
10 
_exit(0)        = ? 

ので我々は上記の修正はまた、リダイレクトの問題を修正かどうかを確認する準備が整いました。

再実行リダイレクトされた出力と、straceの下:

strace ./basic > t 
... 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f08161eb000 
_exit(0)        = ? 

明らかにwriteへの私たちのコールが欠落しています。それはどこに行きましたか?

よく、stdoutの出力はデフォルトで行バッファリングされ、ファイルにリダイレクトされると完全にバッファリングされます。おそらくfflushコールがありませんか?

確かに、ちょうど終了する前にfflush(NULL)への呼び出しを追加することで問題を解決:

... 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8afd450000 
write(1, "10\n", 3)      = 3 
_exit(0)        = ? 

私はあなたが上で適切に入力をリダイレクトしている今日(私は;-)

+0

あなたの答えはそれを解決し、私は多くを学んだ!ありがとうございました! –

+1

+1。素晴らしい答え。私ができるなら、私は一回以上upvoteしたいと思います。 –

+0

+1。この回答は素晴らしいです。ケンの言葉 –