【问题标题】:assembly output of a simple C++ program一个简单的 C++ 程序的汇编输出
【发布时间】:2015-02-25 14:49:48
【问题描述】:

我试图理解一个简单的 c++ 程序的汇编输出。这是我的 C++ 程序。

void func()
{}

int main()
{
    func();
}

当我使用带有 --save-temps 选项的 g++ 来获取上述程序的汇编代码时,我得到以下汇编代码。

    .file   "main.cpp"
    .text
    .globl  _Z4funcv
    .type   _Z4funcv, @function
_Z4funcv:
.LFB0:
    .cfi_startproc
     pushq  %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   _Z4funcv, .-_Z4funcv
    .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
    call    _Z4funcv
    movl    $0, %eax
    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

根据我对汇编的了解,任何汇编程序都应该有 3 个部分,即数据、文本和 bss。文本部分也应以“global _start”开头。我在这个汇编代码中看不到它们中的任何一个。 有人可以帮我理解上面的汇编代码吗?如果您也可以与 C++ 代码相关,那就太好了。

非常感谢任何形式的帮助。

【问题讨论】:

  • 我认为您通常应该在映射文件中找到与对象(二进制)代码相对应的那些部分,而不是在汇编源代码中。
  • _start 很可能在您链接的 CRT(C 运行时)中。它将依次调用您的main 函数。此外,无需定义您不使用的部分。
  • 在第二行你有汇编指令.text。这是正文部分。因为没有全局变量,所以不需要其他部分。

标签: c++ assembly compilation g++


【解决方案1】:

嗯,这里是一行一行的……

 .file   "main.cpp"                # Debugging info (not essential)
    .text                          # Start of text section (i.e. your code)
    .globl  _Z4funcv               # Let the function _Z4funcv be callable
                                   # from outside (e.g. from your main routine)
    .type   _Z4funcv, @function    # Debugging info (possibly not essential)
_Z4funcv:                          # _Z4funcv is effectively the "name" of your 
                                   # function (C++ "mangles" the name; exactly 
                                   # how depends on your compiler -- Google "C++
                                   # name mangling" for more).
.LFB0:                             # Debugging info (possibly not essential)
    .cfi_startproc                 # Provides additional debug info (ditto)
     pushq  %rbp                   # Store base pointer of caller function
                                   # (standard function prologue -- Google 
                                   # "calling convention" or "cdecl")
    .cfi_def_cfa_offset 16         # Provides additional debug info (ditto)
    .cfi_offset 6, -16             # Provides additional debug info (ditto)
    movq    %rsp, %rbp             # Reset base pointer to a sensible place
                                   # for this function to put its local 
                                   # variables (if any).  Standard function
                                   # prologue.
    .cfi_def_cfa_register 6        # Debug ...
    popq    %rbp                   # Restore the caller's base pointer
                                   # Standard function epilogue
    .cfi_def_cfa 7, 8              # Debug...
    ret                            # Return from function
    .cfi_endproc                   # Debug...
.LFE0:                             # Debug...
    .size   _Z4funcv, .-_Z4funcv   # Debug...
    .globl  main                   # Declares that the main function
                                   # is callable from outside
    .type   main, @function        # Debug...
main:                              # Your main routine (name not mangled)
.LFB1:                             # Debug...
    .cfi_startproc                 # Debug...
    pushq   %rbp                   # Store caller's base pointer
                                   # (standard prologue)
    .cfi_def_cfa_offset 16         # Debug...
    .cfi_offset 6, -16             # Debug...
    movq    %rsp, %rbp             # Reset base pointer 
                                   # (standard prologue)
    .cfi_def_cfa_register 6        # Debug...
    call    _Z4funcv               # Call `func` (note name mangled)
    movl    $0, %eax               # Put `0` in eax (eax is return value)
    popq    %rbp                   # Restore caller's base pointer
                                   # (standard epilogue)
    .cfi_def_cfa 7, 8              # Debug...
    ret                            # Return from main function
    .cfi_endproc                   # Debug...
.LFE1:
    .size   main, .-main           # Debug...
    .ident  "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"  # fluff 
    .section    .note.GNU-stack,"",@progbits       # fluff

如果链接器使用标准 C 或 C++ 库(通常是,除非您另有说明),则链接器知道要查找 main(并且 start)。它将一些存根代码(包含start)链接到最终的可执行文件中。

所以,真的,唯一重要的部分是......

    .text                          
    .globl  _Z4funcv               
_Z4funcv:
    pushq  %rbp
    movq    %rsp, %rbp
    popq    %rbp
    ret
    .globl  main
main:
    pushq   %rbp
    movq    %rsp, %rbp
    call    _Z4funcv
    movl    $0, %eax
    popq    %rbp
    ret

如果您想从头开始,并且不想让所有复杂的标准库内容妨碍您的发现,您可以这样做并获得与您的 C++ 代码相同的结果:

   .text
    .globl  _func
_func:                   # Just as above, really
    push  %ebp
    mov    %esp, %ebp
    pop    %ebp
    ret
    .globl  _start
_start:                  # A few changes here
    push   %ebp
    mov    %esp, %ebp
    call    _func
    movl    $1, %eax     # Invoke the Linux 'exit' syscall
    movl    $0, %ebx     # With a return value of 0 (pick any char!)
    int $0x80            # Actual invocation

exit 系统调用有点痛苦,但很有必要。如果您没有它,它会尝试继续运行并运行“过去”您的代码的代码。由于这可能是重要的代码或数据,机器应该以分段错误错误阻止您。退出调用可以避免这一切。如果您使用的是标准库(这将在您的 C++ 示例中自动发生),则退出内容由链接器处理。

gcc -nostdlib -o test test.s 编译(注意gcc 被明确告知 使用标准库)。我应该说这是针对 32 位系统的,很可能不适用于 64 位系统。我没有要测试的 64 位系统,但也许一些有用的 StackOverflower 会提供 64 位转换。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-24
    • 1970-01-01
    • 2021-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-25
    相关资源
    最近更新 更多