Segmentation fault when executing program compiled from x86 assembly?
I'm new to assembly language and I have to implement a function, in my case sin(x), that could be called from a C source file. I have to make 2 separate files: *.c and *.s When compiling through gcc on ubuntu all good, but when the program executes it gives me the error "Segmentation fault"...
To compile I type:
gcc -c -o sinc.o sinx.c -g3
gcc -c -o sins.o sinx.s -g3
gcc -o sinx sinc.o sins.o -g3
When I start the program:
./sinx
It prints:
.......insert x:
I put x in and then:
segmentation fault
and it stops. Here are the two files:
/*-----------------------------------Sin[x]----------------------------------------*/
extern float Sin(float x); //extern assembly function
#include <stdio.h> //necessary libraries
int main() // main function
{
float x; //allocate variable 'x' as a float
float sine; //allocate variable 'sine' as a float
printf("Calculate Sin[x], with 'x' in radians\n"); //purpose of the program
printf("Insert 'x': "); //user prompt
scanf("%f",&x); //get input value
sine=Sin(x); //calculate sin(x)
printf("Sin[%f]=%f\n",x,sine); //print sin(x)
return 0; //successful exit
}
/*----------------------------------------------------------------------------------*/
and
#---------------------------Sin[x]-----------------------------#
.data
.text
.globl Sin #global function visible by main function
#function sumplus
sumplus:
pushl %ebp
movl %esp,%ebp
fld1 #load 1 in ST(0)
movl 12(%ebp),%ecx #ecx=i
power:
fmul %st(0),%st(1) #ST(0)=ST(0)*ST(1),ST(1)=x
loop power #at the end ST(0)=x^i
movl 12(%ebp),%ecx #ecx=i
xorl %edx,%edx #reset edx used in mul
movl $1,%eax #set accumulator
factorial:
mul %ecx #eax=eax*ecx
loop factorial #at the end eax=i!
pushl %eax
fild -4(%ebp) #ST(0)=i!,ST(1)=x^i,ST(2)=x
popl %eax
fdiv %st(1),%st(0) #ST(1)=ST(1)/ST(0)=(x^i)/i!
fld 8(%ebp) #load partial result in ST(0)
fadd %st(0),%st(2) #ST(0)=updated partial result
fstp 8(%ebp) #store partial result into the stack
fcomip %st(0),%st(0) #pop i!
fcomip %st(0),%st(0) #pop x^i/i!,ST(0)=x
leave
ret
#function summinus
summinus:
pushl %ebp
movl %esp,%ebp
fld1 #load 1 in ST(0)
movl 12(%ebp),%ecx #ecx=i
power2:
fmul %st(0),%st(1) #ST(0)=ST(0)*ST(1),ST(1)=x
loop power2 #at the end ST(0)=x^i
movl 12(%ebp),%ecx #ecx=i
xorl %edx,%edx #reset edx used in mul
movl $1,%eax #set accumulator
factorial2:
mul %ecx #eax=eax*ecx
loop factorial2 #at the end eax=i!
pushl %eax
fild -4(%ebp) #ST(0)=i!,ST(1)=x^i,ST(2)=x
popl %eax
fdiv %st(1),%st(0) #ST(1)=ST(1)/ST(0)=(x^i)/i!
fld 8(%ebp) #load partial result in ST(0)
fsub %st(0),%st(2) #ST(0)=updated partial result
fstp 8(%ebp) #store partial result into the stack
fcomip %st(0),%st(0) #pop i!
fcomip %st(0),%st(0) #pop x^i/i!,ST(0)=x
leave
ret
#function develop
develop:
pushl %ebp
movl %esp,%ebp
sub $8,%esp #allocate room for local variables
movl $9,-4(%ebp) #store development-order,only odd values
fldz #load 0.0 in ST(0)
fstp -8(%ebp) #store ST(0) and pop to collect results
Cycle:
movl -4(%ebp),%eax #eax=i,development-order
xorl %edx,%edx #reset edx because of div
sub $1,%eax #i-1
movl $2,%ecx #divisor
div %ecx #eax=(i-1)/2,edx=0
div %ecx #eax=((i-1)/2)/2,remainder edx=0 or edx!=0
movl %edx,%ecx #ecx=remainder
jecxz Sumplus #n even,(-1)^n=+1
Summinus:
call summinus #n odd,(-1)^n=-1
jmp Restore #if sum- occured skip sum+
Sumplus:
call sumplus
Restore:
movl -4(%ebp),%ecx #restore counter
sub $2,-4(%ebp) #update order
loop Cycle #decrement ecx
fcomip %st(0),%st(0) #pop x
fld -8(%ebp) #load final result in ST(0)
leave
ret
#function sin
Sin:
pushl %ebp
movl %esp,%ebp
fld 8(%ebp) #push onto the stack 'x' value
fldpi #load pi into ST(0),x in ST(1)
ControlPi:
fcomi %st(1) #compare pi and x
jae LoadNPi #stop ControlPi, x is less than pi
fsub %st(1),%st(0) #store (x-pi) in ST(1)
jmp ControlPi #return to control
LoadNPi:
fimul -1 #(-pi) in ST(0),x in ST(1)
ControlPi2:
fcomi %st(1) #compare -pi and x
jbe PopStack #stop ControlPi2, x is greater than -pi
fadd %st(1),%st(0) #store (x+pi) in ST(1)
jmp ControlPi2 #return to control
PopStack:
fcomip %st,%st(0) #compare -pi to -pi then pop register stack
call develop #call develop function
leave 开发者_如何学C #restore ebp
ret #return
So,where are the errors? How can I solve this problem? Thank you.
I would suggest creating a very simple assembly language function that does nothing but return the same argument it's passed. This would be equivalent to the C function:
float identity(float x) {
return x;
}
Making this work will ensure that you have all the compiling, assembling, linking, calling conventions, etc all set up properly before you start actually writing code. Once that works, write a function to add 1 to the argument and return that. Then, start implementing your Sin()
function after you've got some practice. What you've got so far is a heck of a lot of code for somebody new to assembly language.
Are you really expecting somebody to debug this for you as is?
Instruction step through the assembly in the debugger, and then ask the question. "My registers values are a,b,c ... , and I have executed instruction blah". Why does this trap? As preparation, you should minimally have read the text for the trapping instruction in the intel instruction set reference, available here:
http://www.intel.com/products/processor/manuals/
Generally, the ax register family (eax included) is to hold the return value in assembly. Have you checked and confirmed that the value is indeed being stored there. You might want to use a signal handler and trap the SIGSEGV and to do a system(...)
shelling out to gdb using getpid()
and attach to the process and do a stack trace.
As an example function used in the signal handler when SIGSEGV is trapped:
char buf[150]; sprintf(buf, "echo where | gdb -a %d > mydump.data", getpid()); system(buf);
Then you can inspect the file mydump.data and check where exactly it is seg-faulting.
Segfaults are a result of a de-referencing of a uninitialized pointer or that is not malloc'd.
Hope this helps, Best regards, Tom.
There are more than one bug in this assembler function :)
- Using gdb reveals the segfault happens on the
fimul
instruction. - You can't use
fimul
with a litteral operand like that; the operand should be the address of the 32-bit value you want to multiply by, not the litteral value itself. - To change the sign of
st0
, it's definitely easier to usefchs
. - Even with these changes, your assembler function doesn't work (but at least, it does not segfault, and it does return the correct value in the case where the argument is zero!)
精彩评论