C++背后-Helloworld[1]
来源:互联网 发布:sql注入直接写入一句话 编辑:程序博客网 时间:2024/04/30 10:43
笔者的实验环境:
1. Cygwin:运行在Windows上,如果您还没有此环境,请参考http://www.cygwin.com/进行安装与配置。
2. GCC家族一揽子工具:进行编译/反编译与分析,包括gcc,g++,objdump,as,nm,ld等。
好了,开始第一个入门程序。我们毫不例外的使用经典的HelloWorld程序,文件名为helloworld.cpp,内容如下:
#include <cstdio>
int main(int argc, char ** argv)
{
printf("Hello World/r/n");
return 0;
}
运行
g++ -o helloworld helloworld.cpp
不出意外的话,会在当前目录生成可执行文件helloworld.运行
./helloworld
就会看到输出
Hello World
非常简单,对吧!
接下来,运行
g++ -o helloworld helloworld.cpp -save-temps
注意这里加入了一个新的编译选项 -save-temps, 这个选项告诉编译器,“hi,哥们!请把你编译过程的中间文件都保存下来!莫删莫删!”。好了,看看当前目录下面都有什么文件?
helloworld.o --这是由helloworld.cpp编译出来的链接前的object文件。
helloworld.ii --这是编译器对源代码进行展开后的文件,主要是拷贝引用的其它头文件的内容,对代码中出现的宏进行展开和替换,对常量值进行替换等基本的代码展开操作。
helloworld.s --这是在生成object之前的汇编代码文件。
查看helloworld.ii,输入
vim helloworld.ii
就会看到里面包含的内容,这里列出一部分:
# 598 "/usr/include/stdio.h" 3 4
}
# 53 "/usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/cstdio" 2 3
# 97 "/usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/cstdio" 3
namespace std
{
using ::FILE;
using ::fpos_t;
using ::clearerr;
using ::fclose;
using ::feof;
using ::ferror;
using ::fflush;
using ::fgetc;
using ::fgetpos;
using ::fgets;
using ::fopen;
using ::fprintf;
using ::fputc;
using ::fputs;
using ::fread;
using ::freopen;
using ::fscanf;
using ::fseek;
using ::fsetpos;
using ::ftell;
using ::fwrite;
using ::getc;
using ::getchar;
using ::gets;
using ::perror;
using ::printf;
using ::putc;
using ::putchar;
using ::puts;
using ::remove;
using ::rename;
using ::rewind;
using ::scanf;
using ::setbuf;
using ::setvbuf;
using ::sprintf;
using ::sscanf;
using ::tmpfile;
using ::tmpnam;
using ::ungetc;
using ::vfprintf;
using ::vprintf;
using ::vsprintf;
}
# 2 "helloworld.cpp" 2
int main(int argc, char ** argv)
{
printf("Hello World-FengYanshuo./r/n");
return 0;
}
可以看到,这个文件只是把代码进行了简单扩展;没有什么实质变化。
看文件helloworld.s,输入
vim helloworld.s
哈哈,看到了吧,这是汇编代码:
1 .file "helloworld.cpp"
2 .def ___main; .scl 2; .type 32; .endef
3 .section .rdata,"dr"
4 LC0:
5 .ascii "Hello World-FengYanshuo./15/12/0"
6 .text
7 .align 2
8 .globl _main
9 .def _main; .scl 2; .type 32; .endef
10 _main:
11 pushl %ebp
12 movl %esp, %ebp
13 subl $8, %esp
14 andl $-16, %esp
15 movl $0, %eax
16 addl $15, %eax
17 addl $15, %eax
18 shrl $4, %eax
19 sall $4, %eax
20 movl %eax, -4(%ebp)
21 movl -4(%ebp), %eax
22 call __alloca
23 call ___main
24 movl $LC0, (%esp)
25 call _printf
26 movl $0, %eax
27 leave
28 ret
29 .def _printf; .scl 2; .type 32; .endef
第一行,很明显是说,这是有helloworld.cpp生成的或者说对应的源代码是helloworld.cpp.
第二行,.def __main是说有个叫做__main的函数,注意,这个可不是main函数,这是进入main函数前最先调用的函数。
后面,紧接着就是只读数据段,很显然,我们要print出来的字符串就放在里面。
接下来,声明了函数_main,注意这才是真正的main函数;后面从标签_main:开始一直到28行,就是main函数的实现。
第29行声明了函数_printf,其实这就是标准c++库里面printf函数。
很显然,这里面只有main函数的实现,没有__main和_printf的实现,这需要链接器在链接阶段去从其它object中找到所需的函数,然后进行地址分配。
好了,重点关注一下main函数的实现:
pushl %ebp
movl %esp, %ebp
这两句是典型的GCC编译器使用函数调用入口操作,GCC使用ebp寄存器做基址寄存器,函数入口需要进行圧栈,函数推出需要退栈;后面把esp的内容送入ebp,以后,在函数内部就使用ebp寄存器进行函数内的栈操作来存取函数参数,处理函数内的局部变量等。
第22行,调用__alloca不是很清楚,估计是为栈申请空间之类的;紧接着调用__main函数,然后通过
movl $LC0,(%esp)
把字符串的地址送给esp寄存器指示的地址;之后调用函数_printf, _printf也要进行圧栈操作,通过esp寄存器获得传入的字符串参数,然后执行输出操作。
第26行,把数字0送入寄存器eax,之后程序返回;gcc使用eax存储函数的返回值,基本上所有函数都在最后把结果送入eax然后返回。
怎么样,通过看编译后的汇编,是不是对编译过程和结果有更深的了解了呢?
接下来,看看helloworld.o里面都有什么?helloworld.o实际上是ELF格式的文件,说白了就是可执行可链接文件格式,运行:
$ objdump.exe -D helloworld.o
helloworld.o: file format pe-i386
Disassembly of section .text:
00000000 <_main>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 08 sub $0x8,%esp
6: 83 e4 f0 and $0xfffffff0,%esp
9: b8 00 00 00 00 mov $0x0,%eax
e: 83 c0 0f add $0xf,%eax
11: 83 c0 0f add $0xf,%eax
14: c1 e8 04 shr $0x4,%eax
17: c1 e0 04 shl $0x4,%eax
1a: 89 45 fc mov %eax,-0x4(%ebp)
1d: 8b 45 fc mov -0x4(%ebp),%eax
20: e8 00 00 00 00 call 25 <_main+0x25>
25: e8 00 00 00 00 call 2a <_main+0x2a>
2a: c7 04 24 00 00 00 00 movl $0x0,(%esp)
31: e8 00 00 00 00 call 36 <_main+0x36>
36: b8 00 00 00 00 mov $0x0,%eax
3b: c9 leave
3c: c3 ret
3d: 90 nop
3e: 90 nop
3f: 90 nop
Disassembly of section .rdata:
00000000 <.rdata>:
0: 48 dec %eax
1: 65 gs
2: 6c insb (%dx),%es:(%edi)
3: 6c insb (%dx),%es:(%edi)
4: 6f outsl %ds:(%esi),(%dx)
5: 20 57 6f and %dl,0x6f(%edi)
8: 72 6c jb 76 <_main+0x76>
a: 64 fs
b: 2d 46 65 6e 67 sub $0x676e6546,%eax
10: 59 pop %ecx
11: 61 popa
12: 6e outsb %ds:(%esi),(%dx)
13: 73 68 jae 7d <_main+0x7d>
15: 75 6f jne 86 <_main+0x86>
17: 2e cs
18: 0d .byte 0xd
19: 0a 00 or (%eax),%al
...
objdump这个工具对ELF文件进行反编译,分析ELF文件中的各个段,方便进行反向分析。可以看到,对于main函数,object文件和helloworld.s里面的几乎一样,只是在只读数据段会有些差别。
- C++背后-Helloworld[1]
- Linux C编程(1) HelloWorld
- HelloWorld.c
- C语言学习系列1-helloworld示例
- [c/c++]helloWorld
- c语言的背后
- object-c HelloWorld
- PSP开发--[C]HelloWorld
- 运行C可执行文件helloworld
- C语言 HelloWorld源程序
- C程序HelloWorld
- C#(一) helloworld
- Objective-C HelloWorld
- C语言简介+helloworld
- C's HelloWorld
- C++'s HelloWorld
- C—HelloWorld
- C语言Helloworld
- 日本Nihon Speed齿轮泵K1P系列
- apache去掉用户权限
- More Effective C++ 笔记(一)
- JDBC连接字符串大全
- 顺序表实操
- C++背后-Helloworld[1]
- J2EE学习中一些值得研究的开源项目
- DWR取session,request,response
- Oracle中SQLPLUS的常用指令收集与技巧
- myeclipse+tomcat+red5开发环境搭建
- COM本质论学习笔记(一)IDL
- 关于LoadImage
- 金玉良言十六句
- 将ASP.NET页面内地数据导出到Excel 或 Word里面