这个汇编语言代码是什么意思?

Nit*_*ose 4 x86 assembly

我是一名学生,刚开始学习汇编语言.为了更好地理解它,我只是在C中编写了一个短文并将其转换为汇编语言.令人惊讶的是我有点不明白.

代码是:

#include<stdio.h>

int main()
{
    int n;
    n=4;
    printf("%d",n);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

相应的汇编语言是:

.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
Run Code Online (Sandbox Code Playgroud)

这些是什么意思?

Sco*_*ski 27

让我们分解一下:

.file   "delta.c"
Run Code Online (Sandbox Code Playgroud)

编译器使用它来告诉您程序集来自的源文件.这对汇编程序来说意义不大.

.section    .rodata
Run Code Online (Sandbox Code Playgroud)

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

通常,源代码中的任何"编译时常量"都无法优化到汇编内部函数中,最终将存储在"只读数据部分"中.

.LC0:
    .string "%d"
Run Code Online (Sandbox Code Playgroud)

.LC0"部分是一个标签.它提供了一个符号名称,引用文件中后面出现的字节.在这种情况下,"LC0"表示字符串"%d".GNU汇编程序使用以"L"开头的标签被视为"本地标签"的约定.这有一个技术含义,对编写编译器和链接器的人来说非常有趣.在这种情况下,编译器使用它来引用特定对象文件专用的符号.在这种情况下,它表示一个字符串常量.

.text
Run Code Online (Sandbox Code Playgroud)

这将开始一个新的部分."text"部分是存储可执行代码的目标文件中的部分.

.globl  main
Run Code Online (Sandbox Code Playgroud)

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

.type   main, @function
Run Code Online (Sandbox Code Playgroud)

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

main:
Run Code Online (Sandbox Code Playgroud)

这定义了"main"函数的入口点.

.LFB0:
Run Code Online (Sandbox Code Playgroud)

这是一个"本地标签",指的是函数的开头.

    .cfi_startproc
Run Code Online (Sandbox Code Playgroud)

这是一个"调用帧信息"指令.它指示汇编程序发出矮调格式的调试信息.

    pushl   %ebp
Run Code Online (Sandbox Code Playgroud)

这是汇编代码中函数"序言"的标准部分.它保存了"ebp"寄存器的当前值."ebp"或"base"寄存器用于在函数内存储堆栈帧的"基础"."esp"("堆栈指针")寄存器可以在函数内调用函数时更改,而"ebp"保持固定.始终可以相对于"ebp"访问函数的任何参数.通过ABI调用约定,在函数可以修改EBP寄存器之前,它必须保存它,以便在函数返回之前恢复原始值.

    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
Run Code Online (Sandbox Code Playgroud)

我没有详细研究过这些,但我相信它们与DWARF调试信息有关.

    movl    %esp, %ebp
Run Code Online (Sandbox Code Playgroud)

GAS使用AT&T语法,这与英特尔手册使用的语法相反.这意味着"设置ebp等于esp".这基本上为函数的其余部分建立了"基指针".

    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $32, %esp
Run Code Online (Sandbox Code Playgroud)

这也是该功能的epilouge的一部分.这会对齐堆栈指针,然后从中减去足够的空间来容纳函数的所有本地.

    movl    $4, 28(%esp)
Run Code Online (Sandbox Code Playgroud)

这将32位整数常量4加载到堆栈帧的插槽中.

    movl    $.LC0, %eax
Run Code Online (Sandbox Code Playgroud)

这会将上面定义的"%d"字符串常量加载到eax中.

    movl    28(%esp), %edx
Run Code Online (Sandbox Code Playgroud)

这将堆栈中偏移量28中存储的值"4"加载到edx.有可能你的代码是在关闭优化的情况下编译的.

    movl    %edx, 4(%esp)
Run Code Online (Sandbox Code Playgroud)

然后,将值4移动到堆栈上,在调用printf时需要的位置.

    movl    %eax, (%esp)
Run Code Online (Sandbox Code Playgroud)

这会将字符串"%d"加载到调用printf时所需的堆栈上的位置.

    call    printf
Run Code Online (Sandbox Code Playgroud)

这称为printf.

    movl    $0, %eax
Run Code Online (Sandbox Code Playgroud)

这将eax设置为0.鉴于下一条指令是"leave"和"ret",这相当于C代码中的"return 0".EAX寄存器用于保存函数的返回值.

    leave
Run Code Online (Sandbox Code Playgroud)

该指令清除呼叫帧.它将ESP设置回EBP,然后从修改后的堆栈指针中弹出EBP.像下一条指令一样,这是函数结尾的一部分.

    .cfi_restore 5
    .cfi_def_cfa 4, 4
Run Code Online (Sandbox Code Playgroud)

这是更多DWARF的东西

    ret
Run Code Online (Sandbox Code Playgroud)

这是实际的返回指令.它从功能中返回

    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
    .section    .note.GNU-stack,"",@progbits
Run Code Online (Sandbox Code Playgroud)

  • 当然,在现代操作系统上,每个可执行文件(第一近似)都是共享对象.所以`rodata`不仅仅是使用共享库在不同程序之间共享,如果多个实例同时运行,它也会在同一程序的不同实例之间共享.查看John Levine关于链接和加载主题的精美书籍以获取更多详细信息.http://www.iecc.com/linker/ (2认同)