CFI指令是什么意思?(还有一些问题)

What do the CFI directives mean? (and some more questions)

本文关键字:问题 指令 是什么 意思 CFI      更新时间:2023-10-16

好的,这将是一个很长的问题。我试图了解"缓冲区溢出"是如何工作的。我正在阅读alph1的Smashing the stack,以获得乐趣和收益,并且刚刚获得了以下代码的反汇编:

void function(int a, int b, int c) {
   char buffer1[5];
   char buffer2[10];
}
void main() {
  function(1,2,3);
}

使用GCC的-S标志的反汇编给我:

    .file   "example1.c"
    .text
    .globl  function
    .type   function, @function
function:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $48, %rsp
    movl    %edi, -36(%rbp)
    movl    %esi, -40(%rbp)
    movl    %edx, -44(%rbp)
    movq    %fs:40, %rax
    movq    %rax, -8(%rbp)
    xorl    %eax, %eax
    movq    -8(%rbp), %rax
    xorq    %fs:40, %rax
    je  .L2
    call    __stack_chk_fail
.L2:
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   function, .-function
    .globl  main
    .type   main, @function
main:
.LFB1:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    $3, %edx
    movl    $2, %esi
    movl    $1, %edi
    call    function
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE1:
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
    .section    .note.GNU-stack,"",@progbits

.cfi指令不在Aleph1的论文中,我猜当时没有使用它们。我在SO上读过这个问题,我得到它们被GCC用于异常处理。我还阅读了关于SO的另一个问题,我得到。lfb0,。lfe0,。lfe1和。lfb1是标签,但我有以下疑问:

  1. 我得到。cfi指令用于异常处理,但我不明白他们的意思。我一直在这里,我看到一些定义,如:

。Cfi_def_cfa寄存器,offset

。cfi_def_cfa定义了一个计算CFA的规则:从注册并添加偏移量

然而,如果你看看我放在上面的反汇编,你找不到任何寄存器名(如EAX, EBX等),而不是你在那里找到一个数字(我通常发现'6'),我不知道这应该是一个寄存器。特别是,谁能解释一下.cfi_def_cfa_offset 16, .cfi_offset 6, -16, .cfi_def_cfa_register 6.cfi_def_cfa 7, 8是什么意思?另外,CFA是什么意思?我之所以问这个问题,是因为在书籍/论文中,程序序言大多是这样的:

 pushl %ebp
 movl %esp,%ebp
 subl $20,%esp

然而,现在我认为现代计算机的程序序是这样的:

    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $48, %rsp

最初我认为使用CFI指令而不是sub助记符来设置偏移量,但事实并非如此;尽管使用了CFI指令,sub命令仍然被使用。

  • 我知道每个程序都有标签。然而,为什么在一个过程中有多个嵌套的标签?在我的情况下,main有。lfb1和。lfe2标签。为什么需要多个标签?类似地,function过程也有标签。lfb0,。l2和。lfe0

  • 两个过程的最后3行似乎用于一些内务功能(告诉过程的大小,也许?),但我不确定它们是什么意思。有人能解释一下它们的意思和用途吗?

  • 编辑:

    (再加一个问题)

  • CFI指令占用空间吗?因为在过程"function"中,每个int参数占用4个字节,它的个数是3,所以所有参数占用12个字节的内存。接下来,第一个char数组占用8字节(将5字节四舍五入到8字节),下一个char数组占用12字节(将10字节四舍五入到12字节),因此整个char数组占用20字节。参数变量和局部变量加起来只需要12+20=32字节。

    但是在过程"function"中,编译器减去48个字节来存储值。为什么?

  • CFI代表呼叫帧信息。它是编译器描述函数中发生的事情的方式。它可以被调试器用来显示调用堆栈,被链接器用来合成异常表,用于堆栈深度分析和其他类似的事情。

    实际上,它描述了诸如处理器寄存器之类的资源存储在何处以及返回地址在何处。

    CFA代表调用帧地址,它表示调用函数的堆栈指针位置的地址。

    Lindy Dancer回答什么是cfi and cfa means (call frame information)和(call frame address)

    .L<num>表示标签,在x64 GCC名称中,所有以下格式的标签以.L开始,以a numeral结束,因此.L1 , .L2 , .L....infinity是标签

    根据Google和一些早期的SO答案BF<num>表示Function-Begin, EF<num>表示FUNCTION-END

    so .LBF0 , .LBF1 . LBF.....infinity and .LFE0 ,......., .LFE....infinity

    表示每个函数的函数开始和函数结束,编译器可能需要处理一些内部需求,所以你现在应该忘记它们,除非非常需要深入研究编译器的内部

    另一个标签.L2的存在是为了处理函数

    中的分支指令je
    je  .L2
    

    每个编译器都将对参数和局部变量的访问对齐并填充到一定的边界

    我不能确定,但x64默认对齐是16字节,我认为GCC所以如果你请求一个奇怪的预订,比如

    char foo[5] or
    BYTE blah [10]

    索引5 and 10不对齐,即使x86

    for 5 x86 compiler will assign 8 byte s and for 10 16 bytes

    like wise x64 gcc might assign 16 bytes for your每个请求

    实际上你不应该担心为什么编译器做它所做的

    当你试图理解汇编的逻辑时,只关注地址

    如果编译器决定它是will put x at rbp +/- X,它将在该变量的作用域或生命周期内also access it at the same location

    48是跳过参数和局部变量。5字节数组在8字节边界上对齐,10字节数组在16字节边界上对齐。每个参数占用8字节,所以3*8(参数)加上8 + 16(局部变量)得到24+24或48。您可以在gdb中通过请求这些东西的地址来看到它