My experiment environment:
1. cygwin: runs on Windows. If you do not have this environment, see http://www.cygwin.com/to install and configure the environment.
2. GCC family of tools: compilation/decompilation and analysis, including GCC, G ++, objdump, As, nm, LD, etc.
Now, start the first entry program. Without exception, we use the classic helloworld program named helloworld. cpp. The content is as follows:
# Include <cstdio>
Int main (INT argc, char ** argv)
{
Printf ("Hello World/R/N ");
Return 0;
}
Run
G ++-O helloworld. cpp
The executable file helloworld is generated in the current directory.
./Helloworld
The output is displayed.
Hello World
Very simple, right!
Next, run
G ++-O helloworld. cpp-Save-temps
Note that a new compilation option-save-temps is added here. This option tells the compiler, "Hi, buddy! Save all the intermediate files during the compilation process! Do not delete or delete !". Okay. What files are in the current directory?
Helloworld. O -- this is the object file before the link compiled by helloworld. cpp.
Helloworld. II -- this is the file after the compiler expands the source code. It mainly copies the content of other referenced header files and expands and replaces the Macros in the code, replace a constant value and perform other basic code expansion operations.
Helloworld. s -- this is the assembly code file before the object is generated.
View helloworld. II, enter
Vim helloworld. II
You will see the content in it, which is listed here:
#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;
}
As you can see, this file only extends the Code. There is no substantial change.
View the file helloworld. s and enter
Vim helloworld. s
Haha, see it. Here is the assembly code:
1. File "helloworld. cpp"
2. Def ___ main;. SCL 2;. Type 32;. endef
3. Section. RDATA, "Dr"
4 lc0:
5. ASCII "Hi 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
In the first line, it is obvious that this is generated by helloworld. cpp or the source code is helloworld. cpp.
In the second line,. Def _ main refers to a function called _ main. Note that this is not the main function. This is the first function called before entering the main function.
Followed by the read-only data segment. Obviously, the string to be printed is placed in it.
Next, declare the function _ main. Note that this is the real main function, followed by the implementation of the main function from the label _ main: to the 28 rows.
Row 3 declares the function _ printf, which is actually the printf function in the Standard C ++ library.
Obviously, only the main function is implemented, and the _ main and _ printf are not implemented. This requires the linker to find the required function from other objects in the Link phase, then allocate the address.
Well, focus on the implementation of the main function:
Pushl % EBP
Movl % ESP, % EBP
These two statements are typical GCC compilers that use function call entry operations. GCC uses the EBP register as the base address register, and the function entry needs to implement the rollback stack. Function roll-out requires rollback; the ESP content will be sent to EBP. Later, the EBP register will be used inside the function to perform stack operations in the function to access function parameters and process local variables in the function.
In row 3, it is not clear to call _ alloca. It is estimated that it applies for space for the stack, and then calls the _ main function, and then passes
Movl $ lc0, (% ESP)
Send the string address to the address indicated by the ESP register. Call the function _ printf and _ printf to perform the register stack operation, obtain the passed string parameters through the ESP register, and then perform the output operation.
In row 3, the number 0 is sent to the Register eax, and then the program returns it. GCC uses eax to store the return value of the function. Basically, all functions finally send the result to eax and then return it.
How can I get a deeper understanding of the compilation process and results by looking at the compiled assembly?
Next, let's take a look at what is in helloworld. O? Helloworld. O is actually a file in ELF format. To put it bluntly, it is an executable file format that can be linked. Run:
$ 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 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 call 25 <_ main + 0x25>
25: E8 00 00 00 call 2a <_ main + 0x2a>
2a: C7 04 24 00 00 00 00 movl $0x0, (% ESP)
31: E8 00 00 00 call 36 <_ main + 0x36>
36: B8 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
...
The objdump tool decompile the ELF File to analyze each segment in the ELF File for reverse analysis. As you can see, for the main function, the object file is almost the same as that in helloworld. S, but there will be some differences in the read-only data segment.