2009-10-11 11 views
6

私は大きなアセンブリプロジェクトに取り組んでいますが、今この新しい言語を学び始めています。私はあなたが高等学校でC++を見つけるかもしれないようないくつかの簡単な例を作ろうとしています(合計2つの数字、数字の素数などです)。アセンブリからC関数を呼び出す - アプリケーションが "call printf"でフリーズする理由はわからない

今、nまでの素数をすべて表示する必要があります。問題は、アプリケーションが "call printf"でフリーズしてしまったことです。理由はわかりません。

これを手伝ってもらえますか?

.section .data 
prime_number_str: 
.asciz "%d " 

.section .text 

.global  _start 
_start: 
pushl $20 
call .first_prime_numbers 
addl $4, %esp 
pushl $0 
call exit 


.first_prime_numbers:  #argument first n numbers 
movl 4(%esp), %ecx #get the first argument 
do_test: 
pushl %ecx  #push function arguments 
call .prime 
addl $4, %esp  #restore the stack 

#if not prime jump to the next number 
cmpl $0, %eax 
je no_not_prime 

#print the number 
pushl %eax   #save eax 
pushl %ecx   #first argument 
pushl $prime_number_str  #text to print 
call printf 
addl $4, %esp 
popl %eax   #restore eax 

no_not_prime: 
loop do_test 
ret 


.prime:    #argument: number to check 
movl 4(%esp), %eax #get the first argument 

#divide the argument by 2 
xorl %edx, %edx    
movl $2, %ecx   
pushl %eax  #save the value of eax 
divl %ecx  
movl %eax, %ecx  #init the counter register 
popl %eax  #restore the value of eax 

movl $1, %ebx  #assume the argument is prime 
test_prime: 
# if ecx == 1 then return exit the function 
cmpl $1, %ecx  
jle return_value 

pushl %eax  #save the old value of eax 

#divide the value by the value of counter 
xorl %edx, %edx  
divl %ecx  

#if the reminder is 0 then the number is not prime 
cmpl $0, %edx 
popl %eax  #restore the value of eax 
je not_prime 


subl $1, %ecx  #decrease counter 
jmp test_prime  #try next division 

not_prime: 
movl $0, %ebx 
return_value: 
movl %ebx, %eax 
ret 

答えて

4

あなたのレジスタはすべてprintf呼ばれた後、めちゃくちゃされているので、それはおそらく、あなたがprintf後に使用する傾向があるレジスタを保存し、呼び出しの後にそれらを復元する必要があります。

これは、syscallやレジスタで改ざんする可能性のあるコールを行うときは、常に行うべきことです。

gdb youprogram 

をし、それが失敗した場所を確認するためにそれを実行します。あなたはGNU/Linuxシステムの試行であるので、もしあなたがガスをコーディングしているよう

また、あなたがgdb(GNUデバッガ)になっているはずですが見えます。

1

C/C++では、自分自身でレジスタをポップアップする必要があることに注意してください(パスカル呼び出し変換では、例えば "ret 8"が発行されます)。

2

もう1つの問題は、printfの呼び出し後にスタックを適切にクリーンアップしていないことです。 ECX(4バイト)とアドレス(32ビットモードでは4バイト)をプッシュしたため、ESPに8を追加する必要があります。

また、少なくともMinGWを使用するWindowsでは、printfがEAX(戻り値)、ECX、およびEDXでストップし、EFLAGSを変更することに注意してください。標準的なC関数のすべてと同じように、これまで私がこれまで使用してきた関数もそうです。

関連する問題