hello world详解


C语言hello world的源码:

#include <stdio.h>
int main() {
	printf("hello world!\n");
	return 0;
}

源码还是很简单的,本文主要探讨这段代码执行过程。


储存

这样看似简单的一段代码在电脑中是怎么存储的?

现在计算机系统使用ASCII标准来表示文本字符。

使用gcc编译源码,得到一个可执行文件,查看这个文件。

首先关注到这个ELF,它表明了Linux中编译的C语言程序是ELF格式的,而在windows中去PE的格式。一个可执行的二进制文件包含的不仅仅是机器指令,还包括各种数据、程序运行资源,机器指令只是其中的一部分。

Linux可执行文件格式-ELF结构详解 这篇博客讲解的很详细

我们这个代码的输出毋庸置疑的是输出的Hello world!,那么有没有一种办法不修改源码,改变输出的结果呢,答案是肯定的,我们可以在可执行文件操作。

在可执行文件中可以找到这样一行,这里的h,右边输出了关于h的信息,这里的68是16进制数,它的ascll值是104,那么就可以修改这里从而修改程序的运行结果

通过这种方式应该是可以写出游戏外挂的吧,不过出错的概率太大了。

这也可以说明Linux中一切都是文件。

生成

在生成可执行文件的过程又发生了什么

还是这个4个阶段。

预处理

这个过程主要根据以“#”开头的预编译,修改原始的C程序。

gcc -E hello.c -o hello.i

这里将#incldue<stdio.h>替换成了上面的代码。

编译

编译器将文本文件hello.i进行语法分析编译优化等生成hello.s。生成相应的汇编代码。

gcc -S hello.i -o hello.s

生成的汇编代码

.file   "hello.c"  ; 指定源文件的名称,用于调试信息
        .text      ; 指定代码段的开始,代码段用于存放指令
        .section        .rodata  ; 指定只读数据段的开始,只读数据段用于存放常量
.LC0:   ; 定义一个标签,用于标记一个地址,方便后面引用
        .string "hello world!"  ; 定义一个字符串常量,以$结尾
        .text   ; 重新指定代码段的开始
        .globl  main  ; 声明main函数是一个全局符号,可以被其他模块引用
        .type   main, @function  ; 声明main函数的类型是函数
main:   ; 定义main函数的标签,表示函数的入口地址
.LFB0:  ; 定义一个内部标签,用于调试信息
        .cfi_startproc  ; 指定函数的开始,用于生成调用帧信息
        endbr64  ; 一个特殊的指令,用于防止恶意代码的跳转
        pushq   %rbp  ; 将寄存器rbp的值压入栈中,保存原来的rbp值
        .cfi_def_cfa_offset 16  ; 指定当前栈帧的偏移量为16字节,用于调用帧信息
        .cfi_offset 6, -16  ; 指定寄存器rbp在栈中的偏移量为-16字节,用于调用帧信息
        movq    %rsp, %rbp  ; 将寄存器rsp的值赋给寄存器rbp,设置当前的栈帧基址
        .cfi_def_cfa_register 6  ; 指定寄存器rbp是当前栈帧的基址,用于调用帧信息
        leaq    .LC0(%rip), %rax  ; 将标签.LC0的地址加上寄存器rip的值,赋给寄存器rax,rip是指令指针,表示当前指令的地址
        movq    %rax, %rdi  ; 将寄存器rax的值赋给寄存器rdi,rdi是第一个函数参数的传递寄存器,这里是将字符串的地址作为参数传递
        call    puts@PLT  ; 调用puts函数,输出字符串,@PLT是一个链接器的技术,用于解析函数的实际地址
        movl    $0, %eax  ; 将立即数0赋给寄存器eax,eax是函数返回值的寄存器,这里是将0作为返回值
        popq    %rbp  ; 将栈顶的值弹出,赋给寄存器rbp,恢复原来的rbp值
        .cfi_def_cfa 7, 8  ; 指定当前栈帧的基址和偏移量,用于调用帧信息
        ret  ; 返回指令,从函数中返回,跳转到返回地址
        .cfi_endproc  ; 指定函数的结束,用于生成调用帧信息
.LFE0:  ; 定义一个内部标签,用于调试信息
        .size   main, .-main  ; 指定main函数的大小,用于调试信息
        .ident  "GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0"  ; 指定编译器的版本信息
        .section        .note.GNU-stack,"",@progbits  ; 指定一个特殊的段,用于告诉链接器栈的属性
        .section        .note.gnu.property,"a"  ; 指定一个特殊的段,用于告诉链接器一些属性信息
        .align 8  ; 指定对齐为8字节
        .long   1f - 0f  ; 定义一个长整数,值为两个标签之间的距离
        .long   4f - 1f  ; 定义一个长整数,值为两个标签之间的距离
        .long   5  ; 定义一个长整数,值为5
0:  ; 定义一个标签
        .string "GNU"  ; 定义一个字符串,表示属性的名称
1:  ; 定义一个标签
        .align 8  ; 指定对齐为8字节
        .long   0xc0000002  ; 定义一个长整数,表示属性的类型
        .long   3f - 2f  ; 定义一个长整数,表示属性的大小
2:  ; 定义一个标签
        .long   0x3  ; 定义一个长整数,表示属性的值
3:  ; 定义一个标签
        .align 8  ; 指定对齐为8字节
4:  ; 定义一个标签

汇编

汇编器将hello.s翻译成机器指令,并将这些指令打包成可重定向目标程序,并将结果保存在hello.o,hello.o是一个可执行文件。

gcc -c hello.s -o hello.o

既然是一个二进制文件那么这个文件就可以执行了吗?

显然是不可以的

这个时候的二进制文件就只包含了函数main的指令编码。

链接

在hello程序中调用了printf,而这个函数是由C编译器提供的,存在一个叫做printf.o的程序中,我们需要将这个文件和我们的hello.o结合在一起,链接器的工作就是 这样,结果得到一个hello的可执行文件。

gcc hello.o -o hello

运行

hello.c 程序已经被编译可执行的目标文件 hello,且存在磁盘上。那这个程序是如何运行起来的呢?

我们是在终端执行的hello,同时终端也就是shell同样也是一个程序,当我们从键盘输入./hello,并按下回车时,hello程序执行并输出结果到显示器。

这里就涉及两个进程,一个shell一个hello,在一个系统中可以同时运行多个进程,而每一个进程都好像独占使用硬件,这就是并发运行,操作系统实现这种机制叫做上下文切换。

最开始shell进程等待命令行输入,当运行hello程序,shell程序调用系统调用,系统调用就会将控制权传递给操作系统,操作系统保存shell的上下文,创建hello的上下文,将控制权给hello,当hello进程结束,操作系统回复shell进程上下文,继续等待下一条指令。

shell程序在接收到./hello后,将字符逐一读入寄存器 ,再将它存入内存中

当回车时,shell执行一系列的指令,将hello目标文件的代码和数据从磁盘拷贝到主存,加载hello文件(使用DMA技术)。

在 Linux 系统中,每个程序都有一个运行时的内存映像。

加载过程中..

  • 程序代码和数据:这里解释hello
  • 堆:malloc,new等在程序运行时动态扩展
  • 共享库:这里放的就是printf函数
  • 栈:编译器用这个实现函数调用
  • 内核虚拟内存:保留区域

当hello目标文件加载到存储器,处理器开始执行hello程序中的机器语言指令,这些指令将输出结果中的字节从储存器拷贝到寄存器,再从寄存器拷贝到显示设备

完整过程

源码解析

嗯,这程序也需要源码解析?

main

首先我们都知道函数的运行入口是main,但是为什么是main,可以不可以是别的。

其实是main函数最多还是一种默认的规定,在没有操作系统的环境来说任何都可以函数的入口,毕竟都是自己定义的,main函数和其他的任何函数都没有本质区别

image-20231115210202651

从汇编代码来看{都是创建相应的栈帧,将基地址指针的至压入栈中,再将栈指针的地址赋值给基地址指针

}就是返回当前栈帧

也就是正括号用来保护上层函数的栈帧,并设置当前函数的栈帧

反括号用来放弃当前函数的栈帧

参考视频


gcc main.o -o main 是调用 ld 来做链接的,相当于以下的命令:

ld /usr/lib64/crt1.o /usr/lib64/crti.o main.o -o main -lc -dynamic-linker /lib/ld-linux.so.2 

主要是crt1.o,crti.o这两个文件,crt1.o和crti.o是C语言程序的启动模块,它们是由C库提供的目标文件,用于在调用main函数之前做一些初始化工作。它们通常会被自动链接到应用程序中,但也可以通过编译选项来指定或忽略它们

crt1.o文件中包含了程序的入口函数start,以及两个未定义的符号_libc_start_main和main。_start函数会负责调用__libc_start_main函数来初始化libc,然后调用我们源代码中定义的main函数。_start函数也会把程序的执行状态以整数的方式传递给操作系统

crti.o文件中包含了.init和.fini两个段,它们分别用于执行初始化函数init()和终止函数fini()。.init段中包含了进程的初始化代码,即当程序开始执行时,系统会在调用main函数之前先执行.init中的代码。.fini段中包含了进程的终止代码,即当程序正常退出时(main函数返回之后),系统会安排执行.fini中的代码

crt1.o, crti.o, crtbegin.o, crtend.o, crtn.o参考文章

整个可执行程序真正的入口点是 crt1.o 中的 _start,而 main 函数是被 _start 调用的

#include <stdio.h>
void _start() {
    printf("Hello, World!\n");
    exit(0);
}

直接使用gcc -o main main.c编译,编译失败!

这里应该加上-nostartfiles,告诉编译器:我要使用自己的入口!

再次编译,仍然出错

虽然只是警告

这个警告的原因是没有添加#include <stdlib.h>这个头文件,这个头文件包含了exit,如果没有添加编译器只能自己推导。

编译完成同样可以成功输出结果。

_start才是操作系统眼中的函数启动

在C中_start()有什么用? | 那些遇到过的问题


反汇编

两种方式的反汇编对比

_start:

Disassembly of section .plt:

0000000000400360 <.plt>:
  400360:       ff 35 a2 0c 20 00       pushq  0x200ca2(%rip)        # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>
  400366:       ff 25 a4 0c 20 00       jmpq   *0x200ca4(%rip)        # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>
  40036c:       0f 1f 40 00             nopl   0x0(%rax)

0000000000400370 <puts@plt>:
  400370:       ff 25 a2 0c 20 00       jmpq   *0x200ca2(%rip)        # 601018 <puts@GLIBC_2.2.5>
  400376:       68 00 00 00 00          pushq  $0x0
  40037b:       e9 e0 ff ff ff          jmpq   400360 <.plt>

0000000000400380 <exit@plt>:
  400380:       ff 25 9a 0c 20 00       jmpq   *0x200c9a(%rip)        # 601020 <exit@GLIBC_2.2.5>
  400386:       68 01 00 00 00          pushq  $0x1
  40038b:       e9 d0 ff ff ff          jmpq   400360 <.plt>

Disassembly of section .text:

0000000000400390 <_start>:
  400390:       55                      push   %rbp
  400391:       48 89 e5                mov    %rsp,%rbp
  400394:       bf a8 03 40 00          mov    $0x4003a8,%edi
  400399:       e8 d2 ff ff ff          callq  400370 <puts@plt>
  40039e:       bf 00 00 00 00          mov    $0x0,%edi
  4003a3:       e8 d8 ff ff ff          callq  400380 <exit@plt>

main:

hello:     file format elf64-x86-64


Disassembly of section .plt:

0000000000400360 <.plt>:
  400360:       ff 35 a2 0c 20 00       pushq  0x200ca2(%rip)        # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>
  400366:       ff 25 a4 0c 20 00       jmpq   *0x200ca4(%rip)        # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>
  40036c:       0f 1f 40 00             nopl   0x0(%rax)

0000000000400370 <puts@plt>:
  400370:       ff 25 a2 0c 20 00       jmpq   *0x200ca2(%rip)        # 601018 <puts@GLIBC_2.2.5>
  400376:       68 00 00 00 00          pushq  $0x0
  40037b:       e9 e0 ff ff ff          jmpq   400360 <.plt>

0000000000400380 <exit@plt>:
  400380:       ff 25 9a 0c 20 00       jmpq   *0x200c9a(%rip)        # 601020 <exit@GLIBC_2.2.5>
  400386:       68 01 00 00 00          pushq  $0x1
  40038b:       e9 d0 ff ff ff          jmpq   400360 <.plt>

Disassembly of section .text:

0000000000400390 <_start>:
  400390:       55                      push   %rbp
  400391:       48 89 e5                mov    %rsp,%rbp
  400394:       bf a8 03 40 00          mov    $0x4003a8,%edi
  400399:       e8 d2 ff ff ff          callq  400370 <puts@plt>
  40039e:       bf 00 00 00 00          mov    $0x0,%edi
  4003a3:       e8 d8 ff ff ff          callq  400380 <exit@plt>
[root@VM-4-4-centos hello]# gcc -o hello hello.c
[root@VM-4-4-centos hello]# objdump -S hello

hello:     file format elf64-x86-64


Disassembly of section .init:

00000000004003e0 <_init>:
  4003e0:       48 83 ec 08             sub    $0x8,%rsp
  4003e4:       48 8b 05 0d 0c 20 00    mov    0x200c0d(%rip),%rax        # 600ff8 <__gmon_start__>
  4003eb:       48 85 c0                test   %rax,%rax
  4003ee:       74 05                   je     4003f5 <_init+0x15>
  4003f0:       e8 3b 00 00 00          callq  400430 <__gmon_start__@plt>
  4003f5:       48 83 c4 08             add    $0x8,%rsp
  4003f9:       c3                      retq   

Disassembly of section .plt:

0000000000400400 <.plt>:
  400400:       ff 35 02 0c 20 00       pushq  0x200c02(%rip)        # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>
  400406:       ff 25 04 0c 20 00       jmpq   *0x200c04(%rip)        # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>
  40040c:       0f 1f 40 00             nopl   0x0(%rax)

0000000000400410 <puts@plt>:
  400410:       ff 25 02 0c 20 00       jmpq   *0x200c02(%rip)        # 601018 <puts@GLIBC_2.2.5>
  400416:       68 00 00 00 00          pushq  $0x0
  40041b:       e9 e0 ff ff ff          jmpq   400400 <.plt>

0000000000400420 <__libc_start_main@plt>:
  400420:       ff 25 fa 0b 20 00       jmpq   *0x200bfa(%rip)        # 601020 <__libc_start_main@GLIBC_2.2.5>
  400426:       68 01 00 00 00          pushq  $0x1
  40042b:       e9 d0 ff ff ff          jmpq   400400 <.plt>

0000000000400430 <__gmon_start__@plt>:
  400430:       ff 25 f2 0b 20 00       jmpq   *0x200bf2(%rip)        # 601028 <__gmon_start__>
  400436:       68 02 00 00 00          pushq  $0x2
  40043b:       e9 c0 ff ff ff          jmpq   400400 <.plt>

Disassembly of section .text:

0000000000400440 <_start>:
  400440:       31 ed                   xor    %ebp,%ebp
  400442:       49 89 d1                mov    %rdx,%r9
  400445:       5e                      pop    %rsi
  400446:       48 89 e2                mov    %rsp,%rdx
  400449:       48 83 e4 f0             and    $0xfffffffffffffff0,%rsp
  40044d:       50                      push   %rax
  40044e:       54                      push   %rsp
  40044f:       49 c7 c0 c0 05 40 00    mov    $0x4005c0,%r8
  400456:       48 c7 c1 50 05 40 00    mov    $0x400550,%rcx
  40045d:       48 c7 c7 2d 05 40 00    mov    $0x40052d,%rdi
  400464:       e8 b7 ff ff ff          callq  400420 <__libc_start_main@plt>
  400469:       f4                      hlt    
  40046a:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

0000000000400470 <deregister_tm_clones>:
  400470:       b8 3f 10 60 00          mov    $0x60103f,%eax
  400475:       55                      push   %rbp
  400476:       48 2d 38 10 60 00       sub    $0x601038,%rax
  40047c:       48 83 f8 0e             cmp    $0xe,%rax
  400480:       48 89 e5                mov    %rsp,%rbp
  400483:       77 02                   ja     400487 <deregister_tm_clones+0x17>
  400485:       5d                      pop    %rbp
  400486:       c3                      retq   
  400487:       b8 00 00 00 00          mov    $0x0,%eax
  40048c:       48 85 c0                test   %rax,%rax
  40048f:       74 f4                   je     400485 <deregister_tm_clones+0x15>
  400491:       5d                      pop    %rbp
  400492:       bf 38 10 60 00          mov    $0x601038,%edi
  400497:       ff e0                   jmpq   *%rax
  400499:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)

00000000004004a0 <register_tm_clones>:
  4004a0:       b8 38 10 60 00          mov    $0x601038,%eax
  4004a5:       55                      push   %rbp
  4004a6:       48 2d 38 10 60 00       sub    $0x601038,%rax
  4004ac:       48 c1 f8 03             sar    $0x3,%rax
  4004b0:       48 89 e5                mov    %rsp,%rbp
  4004b3:       48 89 c2                mov    %rax,%rdx
  4004b6:       48 c1 ea 3f             shr    $0x3f,%rdx
  4004ba:       48 01 d0                add    %rdx,%rax
  4004bd:       48 d1 f8                sar    %rax
  4004c0:       75 02                   jne    4004c4 <register_tm_clones+0x24>
  4004c2:       5d                      pop    %rbp
  4004c3:       c3                      retq   
  4004c4:       ba 00 00 00 00          mov    $0x0,%edx
  4004c9:       48 85 d2                test   %rdx,%rdx
  4004cc:       74 f4                   je     4004c2 <register_tm_clones+0x22>
  4004ce:       5d                      pop    %rbp
  4004cf:       48 89 c6                mov    %rax,%rsi
  4004d2:       bf 38 10 60 00          mov    $0x601038,%edi
  4004d7:       ff e2                   jmpq   *%rdx
  4004d9:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)

00000000004004e0 <__do_global_dtors_aux>:
  4004e0:       80 3d 4d 0b 20 00 00    cmpb   $0x0,0x200b4d(%rip)        # 601034 <_edata>
  4004e7:       75 11                   jne    4004fa <__do_global_dtors_aux+0x1a>
  4004e9:       55                      push   %rbp
  4004ea:       48 89 e5                mov    %rsp,%rbp
  4004ed:       e8 7e ff ff ff          callq  400470 <deregister_tm_clones>
  4004f2:       5d                      pop    %rbp
  4004f3:       c6 05 3a 0b 20 00 01    movb   $0x1,0x200b3a(%rip)        # 601034 <_edata>
  4004fa:       f3 c3                   repz retq 
  4004fc:       0f 1f 40 00             nopl   0x0(%rax)

0000000000400500 <frame_dummy>:
  400500:       48 83 3d 18 09 20 00    cmpq   $0x0,0x200918(%rip)        # 600e20 <__JCR_END__>
  400507:       00 
  400508:       74 1e                   je     400528 <frame_dummy+0x28>
  40050a:       b8 00 00 00 00          mov    $0x0,%eax
  40050f:       48 85 c0                test   %rax,%rax
  400512:       74 14                   je     400528 <frame_dummy+0x28>
  400514:       55                      push   %rbp
  400515:       bf 20 0e 60 00          mov    $0x600e20,%edi
  40051a:       48 89 e5                mov    %rsp,%rbp
  40051d:       ff d0                   callq  *%rax
  40051f:       5d                      pop    %rbp
  400520:       e9 7b ff ff ff          jmpq   4004a0 <register_tm_clones>
  400525:       0f 1f 00                nopl   (%rax)
  400528:       e9 73 ff ff ff          jmpq   4004a0 <register_tm_clones>

000000000040052d <main>:
  40052d:       55                      push   %rbp
  40052e:       48 89 e5                mov    %rsp,%rbp
  400531:       bf e0 05 40 00          mov    $0x4005e0,%edi
  400536:       e8 d5 fe ff ff          callq  400410 <puts@plt>
  40053b:       b8 00 00 00 00          mov    $0x0,%eax
  400540:       5d                      pop    %rbp
  400541:       c3                      retq   
  400542:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  400549:       00 00 00 
  40054c:       0f 1f 40 00             nopl   0x0(%rax)

0000000000400550 <__libc_csu_init>:
  400550:       41 57                   push   %r15
  400552:       41 89 ff                mov    %edi,%r15d
  400555:       41 56                   push   %r14
  400557:       49 89 f6                mov    %rsi,%r14
  40055a:       41 55                   push   %r13
  40055c:       49 89 d5                mov    %rdx,%r13
  40055f:       41 54                   push   %r12
  400561:       4c 8d 25 a8 08 20 00    lea    0x2008a8(%rip),%r12        # 600e10 <__frame_dummy_init_array_entry>
  400568:       55                      push   %rbp
  400569:       48 8d 2d a8 08 20 00    lea    0x2008a8(%rip),%rbp        # 600e18 <__init_array_end>
  400570:       53                      push   %rbx
  400571:       4c 29 e5                sub    %r12,%rbp
  400574:       31 db                   xor    %ebx,%ebx
  400576:       48 c1 fd 03             sar    $0x3,%rbp
  40057a:       48 83 ec 08             sub    $0x8,%rsp
  40057e:       e8 5d fe ff ff          callq  4003e0 <_init>
  400583:       48 85 ed                test   %rbp,%rbp
  400586:       74 1e                   je     4005a6 <__libc_csu_init+0x56>
  400588:       0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
  40058f:       00 
  400590:       4c 89 ea                mov    %r13,%rdx
  400593:       4c 89 f6                mov    %r14,%rsi
  400596:       44 89 ff                mov    %r15d,%edi
  400599:       41 ff 14 dc             callq  *(%r12,%rbx,8)
  40059d:       48 83 c3 01             add    $0x1,%rbx
  4005a1:       48 39 eb                cmp    %rbp,%rbx
  4005a4:       75 ea                   jne    400590 <__libc_csu_init+0x40>
  4005a6:       48 83 c4 08             add    $0x8,%rsp
  4005aa:       5b                      pop    %rbx
  4005ab:       5d                      pop    %rbp
  4005ac:       41 5c                   pop    %r12
  4005ae:       41 5d                   pop    %r13
  4005b0:       41 5e                   pop    %r14
  4005b2:       41 5f                   pop    %r15
  4005b4:       c3                      retq   
  4005b5:       90                      nop
  4005b6:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  4005bd:       00 00 00 

00000000004005c0 <__libc_csu_fini>:
  4005c0:       f3 c3                   repz retq 

Disassembly of section .fini:

00000000004005c4 <_fini>:
  4005c4:       48 83 ec 08             sub    $0x8,%rsp
  4005c8:       48 83 c4 08             add    $0x8,%rsp
  4005cc:       c3                      retq   

可以看出使用_start函数的反汇编代码相较于main函数的还是要简短很多,这中间的过程就是链接的过程

crt1.o, crti.o, crtbegin.o, crtend.o, crtn.o参考文章

printf

printf做的工作之一就是系统调用,printf必须要做的系统调用就是write,因此代码可以直接替换如下

_start() {
    write(1, "Hello World\n", 12);
    exit(0);
}

这样一段没有头文件,没有main函数的C程序是可以正常运行的


使用内联汇编实现

_start()
{
    char* msg = "Hello World!";
	int len = 11;
	int result = 0;
    __asm__ __volatile__("movl %2, %%edx;\n\r" // 传入参数:要显示的字符串长度
             "movl %1, %%ecx;\n\r" //传入参赛:文件描述符(stdout
			 "movl $1, %%ebx;\n\r" //传入参数:要显示的字符串
			 "movl $4, %%eax;\n\r" //系统调用号:4 sys_write
			 "int  $0x80" //触发系统调用中断
             :"=m"(result) //输出部分:本例并未使用
             :"m"(msg),"r"(len) // 输入部分:绑定字符串和字符串长度变量
             :"%eax"); 
    	
	exit(0);
} 

这里的__volatile__说明不要编译器优化

当对代码进行O2优化,编译器可能自动改变改变我们的代码(汇编层面的)

测试代码:

int a = 0;

volatile int b = 0;

int nouse_vol() {
    while(a > 1) {

    }
    return 0;
}

int use_vol(){
    while(b > 1) {

    }
    return 0;
}

汇编的O2优化:

nouse_vol():
        xor     eax, eax
        ret
use_vol():
.L4:
        mov     eax, DWORD PTR b[rip]
        cmp     eax, 1
        jg      .L4
        xor     eax, eax
        ret
b:
        .zero   4
a:
        .zero   4

可以看到在O2优化下,在不使用volatile的情况下,编译器默认就没有执行while的汇编代码


使用汇编实现

section .text
  global _start
    _start:
      mov rdi, 1                    ;arg0
      mov rdx, 8                    ;arg2
      mov rbx, 0x0a646c726f576f6c6c6548   ;arg1 = Hello World\n
      push rbx
      mov rsi, rsp
      mov rax, 1
      syscall

      mov rax, 60                   ;exit
      syscall

return


文章作者: 园崎魅音
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 园崎魅音 !
  目录