g++使用
今天突然对while(cin>>a)很好奇,用到gcc来编译,然后尝试objdump反汇编,记录一下学习了什么新知识,然后复习大三上学习的编译原理,我好菜qwq。
C++的编译过程
一个完整的C++编译过程(例如g++ a.cpp生成可执行文件),总共包含以下四个过程:
编译预处理,也称预编译,可以使用命令g++ -E执行
编译,可以使用g++ -S执行
汇编,可以使用as 或者g++ -c执行
链接,可以使用g++ xxx.o xxx.so xxx.a执行
参考:https://zhuanlan.zhihu.com/p/151219726
我的代码
#include<iostream>
using namespace std;
int main()
{
int a;
int count=0;
while(cin>>a)
{
count++;
}
return 0;
}
1、预编译
输入指令
g++ -E while_test.cpp -o test.ii
得到由while_test.cpp预编译以后的test.ii文件
预编译又称为预处理,是做些代码文本的替换工作。处理#开头的指令,比如拷贝#include包含的文件代码,#define宏定义的替换,条件编译等,就是为编译做的预备工作的阶段主要处理#开始的预编译指令。预编译完成后,#include引入的内容 被全部复制进预编译文件中,除此之外,如果文件中有使用宏定义也会被替换处理。
这个预编译生成的文件很大,很多都看不懂QAQ,不知道是啥?
有一堆__extension__和extern。
经查阅应该是使用__extension__关键字告诉gcc不要抛出警告。
2、G++编译——C++语法错误检查
使用命令
g++ -S test.ii -o test.s
获得编程得到的汇编文件test.s
.file "while_test.cpp"
.text
.section .rodata
.type _ZStL19piecewise_construct, @object
.size _ZStL19piecewise_construct, 1
_ZStL19piecewise_construct:
.zero 1
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1
.text
.globl main
.type main, @function
main:
.LFB1493:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq %fs:40, %rax
movq %rax, -8(%rbp)
xorl %eax, %eax
movl $0, -12(%rbp)
.L3:
leaq -16(%rbp), %rax
movq %rax, %rsi
leaq _ZSt3cin(%rip), %rdi
call _ZNSirsERi@PLT
movq (%rax), %rdx
subq $24, %rdx
movq (%rdx), %rdx
movq (%rdx), %rdx
leaq _ZStL8__ioinit(%rip), %rsi
movq _ZNSt8ios_base4InitD1Ev@GOTPCREL(%rip), %rax
movq %rax, %rdi
call __cxa_atexit@PLT
.L8:
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1977:
je .L5
call __stack_chk_fail@PLT
.L5:
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1493:
.size main, .-main
.type _Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB1977:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
cmpl $1, -4(%rbp)
jne .L8
cmpl $65535, -8(%rbp)
jne .L8
leaq _ZStL8__ioinit(%rip), %rdi
call _ZNSt8ios_base4InitC1Ev@PLT
leaq __dso_handle(%rip), %rdx
leaq _ZStL8__ioinit(%rip), %rsi
movq _ZNSt8ios_base4InitD1Ev@GOTPCREL(%rip), %rax
movq %rax, %rdi
call __cxa_atexit@PLT
.L8:
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1977:
.size _Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
.type _GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB1978:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $65535, %esi
movl $1, %edi
call _Z41__static_initialization_and_destruction_0ii
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1978:
.size _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
.section .init_array,"aw"
.align 8
.quad _GLOBAL__sub_I_main
.hidden __dso_handle
.ident "GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0"
.section .note.GNU-stack,"",@progbits
movl %edi, -4(%rbp)
3、g++ 汇编阶段:生成目标代码 .o文件
有两种方式:
使用 g++ 直接从源代码生成目标代码 g++ -c *.s -o *.o
使用汇编器从汇编代码生成目标代码 as *.s -o *.o
使用命令
g++ -c test.s -o test.o
获得的.o文件是一堆乱码不可读
也可以直接使用as *.s, 将执行汇编、链接过程生成可执行文件a.out, 可以像上面使用-o 选项指定输出文件的格式。
4、g++ 链接阶段:生成可执行文件;Windows下生成.exe
生成目标文件:
g++ test.cpp -c -o test.o
g++ main.cpp -c -o main.o
链接生成可执行文件:
g++ main.o test.o -o a.out
我没有main文件,我的命令为
g++ test.o -o test.out
最后获得可执行文件test.out
好了到这里就完成了程序的编译链接生成可执行文件的过程啦!
objdump
这里我们来使用objdump来对.out文件进行反汇编
- -d:将代码段反汇编
- -S:将代码段反汇编的同时,将反汇编代码和源代码交替显示,编译时需要给出-g,即需要调试信息。
- -C:将C++符号名逆向解析。
- -l:反汇编代码中插入源代码的文件名和行号。
- -j section:仅反汇编指定的section。可以有多个-j参数来选择多个section。