Many people are familiar with the C/C ++ language. This is basically a programming language that every college student must learn. It is usually used as a programming language for Programming beginners, most of the courses are arranged in freshman year. When I first went to college, the children were still very good at learning, and they studied hard. Therefore, the C/C ++ language has a good grasp, not to mention compiling a program, that is, writing a program of hundreds of lines is not enough, but do they really know the C/C ++ program compilation steps?
I think many people are not very clear about it. If he has learned the "Compilation Principle", he may be able to give a rough picture. The "comfortable" development environment of VC shields a lot of compilation details, which undoubtedly lowers the entry barrier for beginners, but it also denies them the right to "know why, as a result, many things can only be memorized. If you encounter any related problems, you will be "stunned ". In fact, I learned how C/C ++ source code is converted into executable files step by step during programming in Linux.
In general, the C/C ++ source code can be converted into executable files on the corresponding platform only after four steps: preprocessing, compilation, assembly, and connection. Most of the time, the programmer can complete the above four steps through a single command. For example, the following section C's "Hello world !" Code:
File: HW. c
# Include stdio. h>
Int main (INT argc, char * argv [])
{
Printf ("Hello world! /N ");
Return 0;
}
If GCC is used for compilation, only one command is required to generate the executable file HW:
Xiaosuo @ gentux HW $ gcc-o hw. c
Xiaosuo @ gentux HW $./HW Hello world!
We can use the-V parameter to see what action GCC has done in the backend:
Reading specs from/usr/lib/GCC/i686-pc-linux-gnu/3.4.6/specs
Configured: /var/tmp/portage/sys-devel/gcc-3.4.6-r2/work/gcc-3.4.6/configure -- prefix =/usr -- bindir =/usr/i686-pc-linux-gnu/GCC-bin/3.4.6 -- export dedir =/ usr/lib/GCC/i686-pc-linux-gnu/3.4.6/include -- datadir =/usr/share/GCC-data/i686-pc-linux-gnu/3.4.6 -- Mandir =/usr/share/GCC-data/i686-pc-linux-gnu/ 3.4.6/man-infodir =/usr/share/GCC-data/i686-pc-linux-gnu/3.4.6/info -- With-gxx-include-Dir =/usr/lib/GCC/i686-pc-linux-gnu/3.4.6/ include/g ++-V3 -- Host = i686-pc-linux-gnu -- Build = i686-pc-linux-gnu -- disable-altivec -- enable-NLS -- without-included-gettext -- With-system-zlib -- disable-checking -- disable-werror -- enable-secureplt -- disable-libunwind-exceptions -- disable-multilib -- disable-libgcj -- enable-versions ages = C, c ++, f77 -- enable-shared -- enable-threads = POSIX -- enable-_ cxa_atexit -- enable-clocale = GNU
Thread model: POSIX
GCC version 3.4.6 (Gentoo 3.4.6-R2, ssp-3.4.6-1.0, pie-8.7.10)
/Usr/libexec/GCC/i686-pc-linux-gnu/3.4.6/PC3-quiet-v hw. c-quiet-dumpbase HW. c-mtune = pentiumpro-auxbase HW-version-O/tmp/ccyb6uwr. s
Ignoring nonexistent directory "/usr/local/include"
Ignoring nonexistent directory "/usr/lib/GCC/i686-pc-linux-gnu/3.4.6/.../i686-pc-linux-gnu/include"
# Include "..." search starts here:
# Include...> search starts here:
/Usr/lib/GCC/i686-pc-linux-gnu/3.4.6/include
/Usr/include
End of search list.
Gnu c version 3.4.6 (Gentoo 3.4.6-R2, ssp-3.4.6-1.0, pie-8.7.10) (i686-pc-linux-gnu)
Compiled by gnu c version 3.4.6 (Gentoo 3.4.6-R2, ssp-3.4.6-1.0, pie-8.7.9 ).
GGC Heuristics: -- Param GGC-Min-expand = 81 -- Param GGC-Min-heapsize = 97004
/Usr/lib/GCC/i686-pc-linux-gnu/3.4.6 /.. /.. /.. /.. i686-pc-linux-gnu/bin/AS-v-QY-O/tmp/ccq8uged. o/tmp/ccyb6uwr. s
GNU faster er version 2.17 (i686-pc-linux-gnu) using BFD version 2.17
/Usr/libexec/GCC/i686-pc-linux-gnu/3.4.6/collect2 -- Eh-frame-HDR-M elf_i386-dynamic-linker/lib/ld-linux.so.2-o hw/usr/lib/GCC/i686-pc-linux-gnu /3.4.6 /.. /.. /.. /crt1.o/usr/lib/GCC/i686-pc-linux-gnu/3.4.6 /.. /.. /.. /crti. o/usr/lib/GCC/i686-pc-linux-gnu/3.4.6/crtbegin. o-L/usr/lib/GCC/i686-pc-linux-gnu/3.4.6-L/usr/lib/GCC/i686-pc-linux-gnu/3.4.6-L/usr/lib/GCC/i686-pc-linux-gnu/3.4.6 /.. /.. /.. /.. /i686-pc-linux-gnu/lib-L/usr/lib/GCC/i686-pc-linux-gnu/3.4.6 /.. /.. /.. /tmp/ccq8uged. o-lgcc -- as-needed-lgcc_s -- no-as-needed-LC-lgcc -- as-needed-lgcc_s -- no-as-needed/usr/lib/GCC/i686-pc-linux-gnu/ 3.4.6/crtend. o/usr/lib/GCC/i686-pc-linux-gnu/3.4.6 /.. /.. /.. /crtn. O
After some redundant information is removed:
PC3 HW. C-o/tmp/ccyb6uwr. s
As-O/tmp/ccq8uged. O/tmp/ccyb6uwr. s
LD-o hw/tmp/ccq8uged. o
The preceding three commands correspond to preprocessing + compilation, assembly, and connection in the compilation step respectively. Pre-processing and compilation are carried out in a command (cc0). You can split it into the following two steps:
CPP-o hw. I HW. c
PC3 HW. I-o/tmp/ccyb6uwr. s
A streamlined makefile that can compile the above HW. c file is as follows:
. Phony: clean
ALL: HW
HW: HW. o
LD-dynamic-linker/lib/ld-linux.so.2-o hw/usr/lib/crt1.o/
/Usr/lib/crti. O/
/Usr/lib/GCC/i686-pc-linux-gnu/3.4.6/crtbegin. O/
HW. O-LC/
/Usr/lib/GCC/i686-pc-linux-gnu/3.4.6/crtend. O/
/Usr/lib/crtn. o
HW. O: HW. s
As-o hw. o hw. s
HW. S: HW. I
/Usr/libexec/GCC/i686-pc-linux-gnu/3.4.6/PC3-o hw. s HW. c
HW. I: HW. c
CPP-o hw. I HW. c
Clean:
Rm-rf hw. I HW. s HW. o
Of course, some paths in the makefile above are specific on my system, and you may be different from me.
Next we will follow the compilation order to see what the compiler has done in each step.
The first is preprocessing. The pre-processed file HW. I:
#1 "HW. c"
#1 ""
#1 ""
...
_ Extension _ typedef _ quad_t _ off64_t;
_ Extension _ typedef int _ pid_t;
_ Extension _ typedef struct {int _ Val [2];} _ fsid_t;
...
Extern int remove (_ const char * _ filename) _ attribute _ (_ nothrow __));
Extern int Rename (_ const char * _ old, _ const char * _ new) _ attribute _ (_ nothrow __));
...
Int main (INT argc, char * argv [])
{
Printf ("Hello world! /N ");
Return 0;
}
Note: because the file size is large, only a few representative contents are left.
We can see that the pre-processor adds the content of all the files (including recursive files) to the original C source file, and then outputs the content to the output file, in addition, it expands all macro definitions, so you cannot find any Macros in the output file of the Preprocessor. This also provides a simple way to view macro expansion results.
Step 2: Compile the C/C ++ code into assembly code:
. File "HW. c"
. Section. rodata
. Lc0:
. String "Hello world! /N"
. Text
. Globl main
. Type main, @ Function
Main:
Pushl % EBP
Movl % ESP, % EBP
Subl $8, % ESP
Andl $-16, % ESP
Movl $0, % eax
Addl $15, % eax
Addl $15, % eax
Shrl $4, % eax
Sall $4, % eax
Subl % eax, % ESP
Subl $12, % ESP
Pushl $. lc0
Call printf
Addl $16, % ESP
Movl $0, % eax
Leave
RET
. Size main,.-Main
. Section. Note. GNU-stack, "", @ progbits
. Ident "GCC: (GNU) 3.4.6 (Gentoo 3.4.6-R2, ssp-3.4.6-1.0, pie-8.7.10 )"
This Assembly file is much smaller than the pre-processed C/C ++ file, removing a lot of unnecessary things, such as useless type declarations and function declarations.
The third step is "assembly", which translates the assembly code output in the second step into machine code in a certain format, which is generally displayed as the elf target file in Linux.
Xiaosuo @ gentux HW $ file HW. o
HW. O: Elf 32-bit LSB relocatable, Intel 80386, Version 1 (sysv), not stripped
In the last step, connect the target file generated in the previous step with the target file and library file of the system library, and finally generate an executable file that can run on a specific platform. Why do we need to connect some target files (crt1.o, crti. O, etc.) in the system library? These target files are used to initialize or recycle the C runtime environment, such as the initialization of the heap memory allocation context environment. In fact, CRT is also the abbreviation of C runtime. This also implies that the program is not executed from the main function, but from an entry in the CRT. on Linux, this entry is _ start. The above makefile generates a dynamic connection executable file. To generate a static connection executable file, you must modify the corresponding segment in the makefile:
HW: HW. o
LD-M elf_i386-static-o hw/usr/lib/crt1.o/
/Usr/lib/crti. O/
/Usr/lib/GCC/i686-pc-linux-gnu/3.4.6/crtbegint. O/
-L/usr/lib/GCC/i686-pc-linux-gnu/3.4.6/
-L/usr/i686-pc-linux-gnu/lib/
-L/usr/lib //
HW. O -- start-group-lgcc-lgcc_eh-LC -- end-group/
/Usr/lib/GCC/i686-pc-linux-gnu/3.4.6/crtend. O/
/Usr/lib/GCC/i686-pc-linux-gnu/3.4.6/.../crtn. o
So far, an executable file is created. In normal projects, the compilation process is not so detailed. The first three steps are generally integrated and shown in makefile as follows:
HW. O: HW. c
Gcc-o hw. O-c hw. c
In fact, if you make any changes to HW. C, the first three steps are unavoidable in most cases. Therefore, writing them together does not cause any harm. On the contrary, you can use the -- pipe parameter to tell the compiler to use pipelines to replace temporary files, thus improving compilation efficiency.
This article is from the chinaunix blog. If you want to view the original text, click:Http://blog.chinaunix.net/u/15793/showart_284757.html