【问题标题】:Segmentation fault when executing program compiled from x86 assembly?执行从 x86 程序集编译的程序时出现分段错误?
【发布时间】:2009-12-22 10:53:48
【问题描述】:

我是汇编语言的新手,我必须实现一个函数,在我的例子中是 sin(x),它可以从 C 源文件中调用。 我必须制作 2 个单独的文件:*.c 和 *.s 在 ubuntu 上通过 gcc 编译时一切正常,但是当程序执行时它给我错误“分段错误”...

要编译,我输入:

gcc -c -o sinc.o sinx.c -g3
gcc -c -o sins.o sinx.s -g3
gcc -o sinx sinc.o sins.o -g3

当我启动程序时:

./sinx

打印出来:

.......insert x:

我把 x 放进去,然后:

segmentation fault

然后它停止了。 这是两个文件:

/*-----------------------------------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
}
/*----------------------------------------------------------------------------------*/

和

#---------------------------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                                              #restore ebp
   ret                                                     #return

那么,错误在哪里?我怎么解决这个问题? 谢谢。

【问题讨论】:

    标签: c gcc assembly x86 segmentation-fault


    【解决方案1】:

    我建议创建一个非常简单的汇编语言函数,它只返回它传递的相同参数。这相当于 C 函数:

    float identity(float x) {
        return x;
    }
    

    完成这项工作将确保您在开始实际编写代码之前正确设置所有编译、组装、链接、调用约定等。一旦成功,编写一个函数将 1 添加到参数并返回。然后,在您进行一些练习后开始实现您的Sin() 函数。到目前为止,对于汇编语言的新手来说,您所获得的代码实在是太多了。

    【讨论】:

    • 我已经用这种语言编程了,但是这个“项目”比其他程序要困难一些,比如阶乘、部分和、幂等。我是新手,因为我' m 在第一步,但实际上我已经做了一些小步骤...:)
    • 好的,是时候破坏调试器了!祝你好运。 :)
    • @asm:你得到了很好的建议。 asm 有很多问题,您没有正确设置堆栈帧,没有初始化局部变量,没有正确管理 FPU 堆栈。先研究一下C代码生成的代码。
    【解决方案2】:

    您真的希望有人按原样为您调试吗?

    指示在调试器中逐步完成程序集,然后提出问题。 “我的寄存器值是 a,b,c ... ,并且我已经执行了指令等等”。为什么会有这个陷阱?作为准备,您应该至少阅读 intel 指令集参考中的捕获指令文本,可在此处获得:

    http://www.intel.com/products/processor/manuals/

    【讨论】:

      【解决方案3】:

      通常,ax 寄存器族(包括 eax)是在汇编中保存返回值。您是否检查并确认该值确实存储在那里。您可能想要使用信号处理程序并捕获 SIGSEGV,并使用 getpid() 对 gdb 进行 system(...) 炮击,并附加到进程并执行堆栈跟踪。

      作为 SIGSEGV 被捕获时在信号处理程序中使用的示例函数:

      字符缓冲区[150]; sprintf(buf, "echo where | gdb -a %d > mydump.data", getpid()); 系统(缓冲区);

      然后您可以检查文件 mydump.data 并检查它的确切位置。

      段错误是取消引用未初始化指针或未 malloc 的结果。

      希望这会有所帮助, 最好的祝福, 汤姆。

      【讨论】:

      • 好吧,我不知道 SIGSEGV 和提到的其他事情。我在某处读到,对于浮点数,结果在 FPU 的 st(0) 中返回...如果我写 factorial=fact(x),fact() 必须在 %eax 寄存器中返回整数值,但是如果该值是浮点数,它不能在一般的 32 位寄存器中返回……这是我读到的……
      • @asm:在这里查看我的其他答案...stackoverflow.com/questions/1843747/…
      【解决方案4】:

      这个汇编函数有不止一个错误:)

      1. 使用 gdb 可以发现在 fimul 指令上发生了段错误。
      2. 您不能将fimul 与这样的字面操作数一起使用;操作数应该是要乘以的 32 位值的地址,而不是文字值本身。
      3. 要更改st0的符号,使用fchs肯定更容易。
      4. 即使进行了这些更改,您的汇编程序函数也不起作用(但至少,它不会出现段错误,并且在参数为零的情况下它会返回正确的值!)

      【讨论】:

      • 嘿,好电话。但这不是字面上的立即操作数,即$-1。 AT&T 语法中的fimul -1 是从绝对地址-1 加载的,因此是段错误。 (fimul $-1 将是一个汇编时错误。)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-10-26
      • 2015-03-20
      • 1970-01-01
      • 2021-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多