assembly - 该汇编语言代码是什么意思?

标签 assembly x86

我是一名学生,刚刚开始学习汇编语言。为了更好地理解它,我只是用C编写了一个简短的代码,然后将其转换为汇编语言。出乎意料的是我一点都不明白。

代码是:

#include<stdio.h>

int main()
{
    int n;
    n=4;
    printf("%d",n);
    return 0;
}

相应的汇编语言是:
.file   "delta.c"
    .section    .rodata
.LC0:
    .string "%d"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $32, %esp
    movl    $4, 28(%esp)
    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf
    movl    $0, %eax
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
    .section    .note.GNU-stack,"",@progbits

这些是什么意思?

最佳答案

让我们分解一下:

.file   "delta.c"

编译器使用它来告诉您程序集来自的源文件。这对汇编程序没有多大意义。
.section    .rodata

这将开始一个新的部分。 “rodata”是“只读数据”部分的名称。本节最后将数据写入可执行文件,该可执行文件将内存映射为只读数据。可执行镜像的所有“.rodata”页面最终都将由以下所有进程共享:
加载图像。

通常,源代码中任何无法优化为汇编内在函数的“编译时常数”最终都将存储在“只读数据段”中。
.LC0:
    .string "%d"
.LC0"部分是一个标签。它提供了一个符号名称,该符号名称引用了文件中紧随其后的再见字词。在这种情况下,“LC0”表示字符串“%d”。 GNU汇编器使用以下约定:以“L”开头的标签被视为“本地标签”。这具有技术上的意义,这对编写编译器和链接器的人来说最为有趣。在这种情况下,编译器使用它来引用对特定目标文件专用的符号。在这种情况下,它表示一个字符串常量。
.text

这将开始一个新的部分。 “文本”部分是目标文件中存储可执行代码的部分。
.globl  main

“.global”指令告诉汇编器将其后的标签添加到生成的目标文件“导出”的标签列表中。这基本上意味着“这是链接器应可见的符号”。例如,任何声明(或包括)兼容函数原型(prototype)的c文件都可以调用“C”中的“非静态”函数。这就是为什么您可以#include stdio.h然后调用printf的原因。编译任何非静态C函数时,编译器都会生成程序集,该程序集声明指向该函数开头的全局标签。将此与不应链接的内容(例如字符串文字)进行对比。目标文件中的汇编代码仍然需要一个标签来引用文字数据。这些是“本地”符号。
.type   main, @function

我不确定GAS(gnu汇编程序)如何处理“.type”指令。但是,这会指示汇编程序,标签“main”是指可执行代码,而不是数据。
main:

这定义了“主要”功能的入口点。
.LFB0:

这是一个“本地标签”,指的是功能的开始。
    .cfi_startproc

这是“调用框架信息”指令。它指示汇编器发出矮格式的调试信息。
    pushl   %ebp

这是汇编代码中功能“序言”的标准部分。它保存“ebp”寄存器的当前值。 “ebp”或“基本”寄存器用于存储函数中堆栈帧的“基本”。当在函数中调用函数时,“esp”(“堆栈指针”)寄存器可以更改,而“ebp”保持不变。始终可以相对于“ebp”访问函数的任何参数。通过ABI调用约定,在功能可以修改EBP寄存器之前,它必须保存它,以便可以在函数返回之前恢复原始值。
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8

我没有详细研究这些内容,但我相信它们与DWARF调试信息有关。
    movl    %esp, %ebp

GAS使用AT&T语法,该语法与Intel手册使用的语法相反。这意味着“将ebp设置为等于esp”。这基本上为该函数的其余部分建立了“基本指针”。
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $32, %esp

这也是该功能的一部分。这将对齐堆栈指针,然后从堆栈指针中减去足够的空间以容纳该函数的所有局部变量。
    movl    $4, 28(%esp)

这会将32位整数常量4加载到堆栈帧中的插槽中。
    movl    $.LC0, %eax

这会将上面定义的“%d”字符串常量加载到eax中。
    movl    28(%esp), %edx

这会将存储在堆栈中的偏移量28中的值“4”加载到edx。您的代码很可能是在关闭优化的情况下编译的。
    movl    %edx, 4(%esp)

然后,将值4移动到堆栈上,即调用printf时所需的位置。
    movl    %eax, (%esp)

这会将字符串“%d”加载到调用printf时所需的堆栈位置。
    call    printf

这称为printf。
    movl    $0, %eax

这会将eax设置为0。假设下一条指令是“leave”和“ret”,则等效于C代码中的“return 0”。 EAX寄存器用于保存函数的返回值。
    leave

该指令清除了调用框架。它将ESP设置回EBP,然后将EBP弹出修改后的堆栈指针。像下一条指令一样,这也是函数尾声的一部分。
    .cfi_restore 5
    .cfi_def_cfa 4, 4

这是更多DWARF的东西
    ret

这是实际的返回指令。它从仿函数返回
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
    .section    .note.GNU-stack,"",@progbits

关于assembly - 该汇编语言代码是什么意思?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17794533/

相关文章:

assembly - CPU如何向IO口写入数据?

assembly - ARM-Assembly : Arithmetic Shift/Logical Shift

visual-studio - Visual Studio:不同的DLL用于配置

assembly - 是否可以在汇编中取消引用内部的某些内容?

x86 - 为非核心频率和缓存分配技术设置封装范围的 MSR

linux - TCP 绑定(bind) Shellcode 不适用于 Shellcode 长度文件

c++ - 在 cpp 文件中运行 asm 过程

c - 缓冲区溢出 : EIP and jump correctly set but segfault

linux-kernel - 启动 Linux 内核所需的所有必要参数

linux - 我的汇编程序中出现段错误