0
点赞
收藏
分享

微信扫一扫

c/c++与汇编实现解析 No.1

非衣所思 2022-02-23 阅读 110
c语言c++

文章目录


前言

Hello World!
写了这么久的Hello World!它是如何实现的?


一、源码

#include <stdio.h>

int main(void)
{
    printf("Hello World!\n");
    return 0;
}

编译可执行程序:

orz@DESKTOP-GRH6F95:~/learn/code/c/assembly$ gcc helloworld.c

运行:

orz@DESKTOP-GRH6F95:~/learn/code/c/assembly$ ./a.out
Hello World!

二、生成汇编代码

orz@DESKTOP-GRH6F95:~/learn/code/c/assembly$ gcc -O0 -S helloworld.c

-O0
Reduce compilation time and make debugging produce the expected results.
减少编译时间(意味着更少的优化),且使调试产生预期的结果。
这个是默认的。

-S
Stop after the stage of compilation proper; do not assemble.
编译阶段后停止,不做汇编。
生成汇编文件,文件名以后缀.s代替原来的后缀.c和.i等等。

三、汇编代码

        .file   "helloworld.c"
        .text
        .section        .rodata        //只读区
.LC0:                                  //位置标记,用来计算Hello world!的位置
        .string "Hello World!"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        endbr64                         //end branch,不知道啥用,不管
        pushq   %rbp                    //保存上一级帧指针到栈中
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp              //将rsp复制到rbp中,此时rbp为当前函数的帧指针
        .cfi_def_cfa_register 6
        leaq    .LC0(%rip), %rdi        //将rip + LC0的地址,放到rdi中
        call    puts@PLT                //调用puts函数,因此处printf只有一个参数,所以此时printf和puts等价,puts自带一个'\n'输出
        movl    $0, %eax                //eax赋值为0, return 0;
        popq    %rbp                    //从栈中恢复rbp成上一级帧指针
        .cfi_def_cfa 7, 8
        ret                             //返回 
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0"
        .section        .note.GNU-stack,"",@progbits
        .section        .note.gnu.property,"a"
        .align 8
        .long    1f - 0f
        .long    4f - 1f
        .long    5
0:
        .string  "GNU"
1:
        .align 8
        .long    0xc0000002
        .long    3f - 2f
2:
        .long    0x3
3:
        .align 8
4:

四、反汇编与硬编码

  • 查看main函数反汇编代码

命令:objdump --disassemble=main a.out

Disassembly of section .text:

0000000000001149 <main>:
    1149:       f3 0f 1e fa             endbr64
    114d:       55                      push   %rbp
    114e:       48 89 e5                mov    %rsp,%rbp
    1151:       48 8d 3d ac 0e 00 00    lea    0xeac(%rip),%rdi        # 2004 <_IO_stdin_used+0x4>
    1158:       e8 f3 fe ff ff          callq  1050 <puts@plt>
    115d:       b8 00 00 00 00          mov    $0x0,%eax
    1162:       5d                      pop    %rbp
    1163:       c3                      retq

Q: Hello World!的位置2004 怎么来的?
A: rip + 0xeac,rip等于下一条的指令位置即1158 + 0xeac = 2004。

Q: 为什么是下一条,而不是当前条?
A: 首先,cpu执行一条指令,简单分为三个步骤,①取指令,②译码,③执行。
rip在第②步之后指向下一条指令。所以第③步执行时rip为下一条指令的位置。

  • 查看rodata区数据

命令:objdump -z -d --section=.rodata a.out


a.out:     file format elf64-x86-64


Disassembly of section .rodata:

0000000000002000 <_IO_stdin_used>:
    2000:       01 00 02 00 48 65 6c 6c 6f 20 57 6f 72 6c 64 21     ....Hello World!
    2010:       00                                                  .

2004开始:

48656c6c6f20576f726c642100
‘H’‘e’‘l’‘l’‘o’’ ’‘W’‘o’‘r’‘l’‘d’!‘\0’

Q: 为什么’\n’不在里面?
A: puts输出自带一个’\n’。

举报

相关推荐

0 条评论