Assembly subl在这里做什么?

Assembly subl在这里做什么?,assembly,Assembly,所以。。。我正在编译成汇编程序,使用gcc-S-O2-m32: void h(int y){int x; x=y+1; f(y); f(2); } 它给了我以下信息: .file "sample.c" .text .p2align 4,,15 .globl h .type h, @function h: pushl %ebp movl %esp, %ebp subl $24, %esp movl 8(%ebp), %eax movl %eax, (%esp

所以。。。我正在编译成汇编程序,使用gcc-S-O2-m32:

void h(int y){int x; x=y+1; f(y); f(2); }
它给了我以下信息:

.file   "sample.c"
.text
.p2align 4,,15
.globl h
.type   h, @function
 h:
pushl   %ebp
movl    %esp, %ebp
subl    $24, %esp
movl    8(%ebp), %eax
movl    %eax, (%esp)
call    f
movl    $2, 8(%ebp)
leave
jmp f
.size   h, .-h
.ident  "GCC: (GNU) 4.4.3 20100127 (Red Hat 4.4.3-4)"
.section    .note.GNU-stack,"",@progbits
movl rm,r   ' move value from register or memory to a register
movl r,rm   ' move a value from a register to a register or memory
movl imm,rm ' Move immediate value.
现在我知道了pushl和movel:它们将当前帧指针存储到堆栈上,然后将帧指针寄存器的值设置为堆栈指针的值

  • 但我不知道24%的次级贷款是什么。我知道它会将堆栈指针向下移动24个字节。对吗
  • 顺便问一下,伊梅德是什么
  • 为什么movl 8(%ebp),%eax使用8?是8字节吗?这是为了适应返回值+参数y到h吗?或者我完全离开这里了。这意味着从堆栈指针中回溯8个字节
  • movl$2,8(%ebp)做什么?它将contant 2复制到帧指针之前的位置8字节。当我们调用f时,帧指针是否发生了变化?如果是-则8(%ebp)指向f的参数位置
  • 离开做什么?它如何“删除”堆栈帧?我的意思是你不能只删除一段记忆。在文档中,它说它做了mov(esp,ebp),pop ebp

  • 谢谢

    编译器在堆栈上为局部变量和它可能具有的任何其他需求保留空间。我不确定它为什么要保留24个字节(它似乎不需要或不完全使用)

    调用函数
    f()
    时,它使用一个简单的
    movl
    到最后保留的位置,而不是使用push指令将参数放入堆栈:

    movl    8(%ebp), %eax    ; get the value of `y` passed in to `h()`
    movl    %eax, (%esp)     ; put that value on the stack for call to `f()`
    
    在我看来,这里发生的一件更有趣的事情是编译器如何处理对
    f(2)
    的调用:



    要回答您的问题,“顺便问一下immed?”-这就是指令参考用来指示值在指令操作码中进行编码,而不是出现在寄存器或内存位置等其他位置。

    要回答这些编号问题:

    1)
    subl$24,%esp

    表示esp=esp-24

    GNU AS使用AT&T语法,这与Intel语法相反。AT&T的目的地在右边,Intel的目的地在左边。此外,AT&T还明确了参数的大小。英特尔试图推断它或强迫你明确

    堆栈在内存中向下扩展,esp处和之后的内存是堆栈内容,低于esp的地址是未使用的堆栈空间。esp指向推到堆栈上的最后一个对象

    2)x86指令编码主要允许以下功能:

    .file   "sample.c"
    .text
    .p2align 4,,15
    .globl h
    .type   h, @function
     h:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    movl    8(%ebp), %eax
    movl    %eax, (%esp)
    call    f
    movl    $2, 8(%ebp)
    leave
    jmp f
    .size   h, .-h
    .ident  "GCC: (GNU) 4.4.3 20100127 (Red Hat 4.4.3-4)"
    .section    .note.GNU-stack,"",@progbits
    
    movl rm,r   ' move value from register or memory to a register
    movl r,rm   ' move a value from a register to a register or memory
    movl imm,rm ' Move immediate value.
    
    没有内存到内存的指令格式。(严格来说,您可以使用
    mov
    push mem
    pop mem
    ,执行内存到内存的操作,但都不能在同一条指令上使用两个内存操作数)

    “立即”表示该值被编码到指令中。例如,要在ebx中的地址存储15,请执行以下操作:

    movl$15,(%ebx)

    15是“立即”值

    括号使它将寄存器用作指向内存的指针

    3)
    movl 8(%ebp),%eax

    意味着

    • 取ebp的值
    • 添加8(但不修改ebp)
    • 将其用作地址(括号)
    • 从该地址读取32位值
    • 并将值存储在eax中
    esp是堆栈指针。 在32位模式下,堆栈上的每个推送和弹出都有4个字节宽。通常,大多数变量都会占用4个字节。所以你可以说8(%ebp)意味着,从堆栈顶部开始,给我堆栈中的int值2(4x2=8)

    通常,32位代码使用ebp指向函数中局部变量的开头。在16位x86代码中,没有办法将堆栈指针用作指针(很难相信,对吧?)。因此,人们所做的是将
    sp
    复制到
    bp
    并使用bp作为本地帧指针。当32位模式出现(80386)时,这就完全没有必要了,它确实有一种直接使用堆栈指针的方法。不幸的是,ebp使调试更容易,所以我们最终继续在32位代码中使用ebp(如果使用ebp,则进行堆栈转储非常容易)

    谢天谢地,amd64给了我们一个新的ABI,它不使用ebp作为帧指针,64位代码通常使用esp访问局部变量,ebp可用于保存变量

    4)上述说明

    5)
    leave
    是一条旧指令,它只执行
    movl%ebp,%esp
    popl%ebp
    ,并保存一些代码字节。它实际上做的是撤消对堆栈的更改并恢复调用方的ebp。被调用函数必须在x86 ABI中保留
    ebp

    在进入函数时,编译器执行subl$24,%esp以为局部变量和有时没有足够寄存器容纳的临时存储腾出空间

    在你脑海中“想象”堆栈框架的最佳方式是将其视为位于堆栈上的结构。虚拟结构的第一个成员是最近“推送”的值。因此,当推送到堆栈时,想象一下在结构的开头插入一个新成员,而其他成员都没有移动。当您从堆栈中“弹出”时,您将获得虚构结构的第一个成员的值,并且该结构的(第一)行将不存在

    堆栈帧操作主要是移动堆栈指针,以便在我们称为堆栈帧的虚拟结构中留出更多或更少的空间。从堆栈指针中减去只会在一个步骤中将多个假想成员放在结构的开头。添加到堆栈指针会使前这么多成员消失

    您发布的代码结尾不典型。
    jmp
    通常是
    ret
    。编译器在这方面做得很聪明,并进行了“尾部调用优化”,这意味着它只是清理了它需要的东西