从汇编中调用C函数 - 应用程序冻结在“call printf”,我不知道为什么

时间:2009-10-11 14:00:33

标签: assembly printf

我将参与一个大型的大会项目,但现在才开始学习这种新语言。我正在尝试制作一些简单的例子,比如你可能在highschool中找到c ++(总和两个数字,是数字素数等)。

现在我必须显示最多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 

3 个答案:

答案 0 :(得分:4)

可能是因为你的寄存器在调用printf之后都被搞砸了,你需要在printf之后保存你倾向于使用的寄存器,然后在调用之后恢复它们。

这是您应该做的事情syscall或其他可能会篡改您的注册的电话。

另外你应该看看gdb(gnu调试器)看起来你正在编写GAS,所以如果你在gnu / linux系统上试试:

gdb youprogram

然后运行它以查看它失败的位置。

答案 1 :(得分:2)

另一个问题是,在调用printf之后,您没有正确清理堆栈。您需要向ESP添加8,因为您按下了ECX(4字节)和一个地址(32位模式下为4字节)。

另外,请注意,至少在Windows上(使用MinGW),printf会在EAX(返回值),ECX和EDX上进行踩踏,并修改EFLAGS。就像我迄今为止使用的所有标准C函数一样。

答案 2 :(得分:1)

请注意,在C / C ++中你需要自己弹出寄存器(在Pascal调用转换中,例如程序发出“ret 8”)。