【问题标题】:need help in writing assembly language in c code [closed]在用 c 代码编写汇编语言时需要帮助 [关闭]
【发布时间】:2014-05-11 21:18:46
【问题描述】:

我正在将汇编语言转换为 C 程序。我知道在下面的函数中,有一个参数设置为等于 0,并将其与某物进行比较(我不确定这是什么让我感到困惑)。如果 x 小于或等于与之比较的任何值,则函数将跳转到 f2 ,然后将 0 复制到局部变量中,但如果不是,它将 1 复制到局部变量中并将其复制到返回的寄存器 a 中.我不明白在前几行中将参数与什么进行比较。谁能指出我正确的方向?

这里是语言:

     pushl    %ebp
     movl     %esp, %ebp
     subl     $4, %esp
     cmpl     $0, 8(%ebp)
     jle . f2
     movl     $1, -4(%ebp)
     jmp. f3  
 .f2:
     movl     $0, -4(%ebp)

 .f3:
     movl     -4(%ebp), %eax
     leave
     ret

这就是我认为它在 C 中的样子:

         fn(int x)
         {
            x = 0;
           if    x    <=   ?   :
                int   y  =   0;
           else
                int y  = 1;
          }
          return y;

在此感谢您

【问题讨论】:

  • 为了回答这个问题,你需要:(1)能够理解上面的汇编代码,(2)熟悉C语法并至少写过一些简单的C 程序。如果你不能同时做这两件事,你就没有希望从要求其他人为你解决这个问题中学到任何东西。
  • @Greg -- 讽刺的是,随便 td;lr 阅读这段代码只会让人有点失望“就是这样吗?”。我想我通过了你的 (1)+(2) 测试;-)
  • 我熟悉C和汇编语言,那我先说清楚。我看到有一个参数设置为等于 0,并将它与我不确定的东西进行比较,这就是我感到困惑的原因。如果 x 小于或等于与之比较的任何值,则函数将跳转到 f2 ,然后将 0 复制到局部变量中,但如果不是,它将 1 复制到局部变量中并将其复制到返回的寄存器 a 中??
  • 嗯,听起来您实际上已经了解了大部分功能。您的问题没有那么详细,因为“对正在发生的事情感到困惑” 不会激发信心。也许您可以编辑您的问题以询问具体说明。表明你确实知道大部分发生的事情。

标签: c assembly


【解决方案1】:

(首先,抱歉,我会翻译成 Intel 语法,我真的无法完全理解 AT&T)

    push ebp
    mov ebp,esp
    sub esp,4

这是通常的函数序言;保存基指针,设置栈指针为基指针,在栈上为一个局部变量腾出空间(后称为[ebp-4]);我们称这个变量为int ret

    cmp dword ptr[ebp + 8], 0

比较ebp+80 的值,并相应地设置标志寄存器,以便以后任何条件跳转指令都可以根据比较结果进行操作。 ebp+8 的位置可能是一个 32 位的函数参数(ebp+4 通常是函数的返回值);我们称这个参数为int x

    jle .f2
    mov dword ptr[ebp-4], 1
    jmp .f3  
.f2:
    mov dword ptr[ebp-4], 0
.f3:

这很简单;如果在最后一次比较中,第一个操作数(AT&T 语法中的第二个操作数)小于或等于第二个操作数,跳转到标签.f2,否则直接跳转(并且,在mov 之后,跳转到.f3 )。

最终结果是,如果x&lt;=0ret=0,否则ret=0

    mov eax,dword ptr[ebp-4]

这会将ret 移动到eax,这是许多调用约定中保留返回值的位置。

    leave
    ret

这是标准函数尾声;它将ebpesp 修复为之前的状态,然后ret 返回给调用者。

所以,整个事情归结为:

int f(int x)
{
    int ret;
    if(x<=0)
        ret=0;
    else
        ret=1;
    return ret;
}

或者,更简洁:

int f(int x)
{
    return x>0;
}

顺便说一句,这一切看起来像gcc 的输出,优化被禁用:编译我用-m32 -c -S 编写的第一个函数我得到:

.LFE0:
    .size   g, .-g
    .globl  f
    .type   f, @function
f:
.LFB1:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    subl    $16, %esp
    cmpl    $0, 8(%ebp)
    jg  .L4
    movl    $0, -4(%ebp)
    jmp .L5
.L4:
    movl    $1, -4(%ebp)
.L5:
    movl    -4(%ebp), %eax
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret

在删除cfi-directives 等之后,这正是您发布的内容。

(加上-O3,它肯定变得更聪明了:

movl    4(%esp), %edx
xorl    %eax, %eax
testl   %edx, %edx
setg    %al
ret

)

【讨论】:

    【解决方案2】:

    在您使用的汇编语言语法中,$0 表示实际值为零。所以指令

     cmpl     $0, 8(%ebp)
    

    将内存位置8(%ebp) 中的值与零值进行比较。您已在示例代码中使用 x 表示此值,但它不需要是变量。

    8(%ebp) 部分表示内存访问。要了解这是什么,请考虑对该函数的调用可能是什么样的:

     pushl    $5
     call     fn
    

    这是将值5 压入堆栈,然后调用您的函数。然后call 指令将返回地址压入堆栈。因此,在调用此函数时,堆栈上有两个单词。函数中的第一条指令将另一个字(%ebp 的前一个值)压入堆栈,因此我们有:

     +----------------+
     | 5              |
     +----------------+
     | return address |
     +----------------+
     | old %ebp       |
     +----------------+ <- %ebp
    

    %ebp 的新值指向底部,如图所示。从此(此处为 32 位字)偏移 8 处的值是 5,即函数的参数。因此,C 中函数的开头可能需要看起来更像:

    int fn(int x)
    {
        if (x <= 0) ...
    

    【讨论】:

      猜你喜欢
      • 2011-03-25
      • 1970-01-01
      • 1970-01-01
      • 2014-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-29
      相关资源
      最近更新 更多