gcc compiler and makefile

1. From source code to executable program

There is a test.c file with the following content:

$ cat test.c
#include <stdio.h>
#define N 10

int main()
{
    // This is a comment, after preprocessing, you can't see it
    printf("N = %d\
", N);
    printf("hello world\
");
    return 0;
}

From the source file to the executable program, it needs to go through four steps: preprocessing, compiling, assembling, and linking.

1. Preprocessing

In the preprocessing stage, the following work will be mainly done:

Header file expansion, for example: #include in the above code will be expanded to the content in stdio.h after preprocessing;

Macro replacement, for example: the macro defined by #define N 10 in the above code will replace N in the code with 10;

Go to Comments to delete all comments.

Under Linux, you can use the gcc compiler to get the preprocessed file:

$ gcc -E test.c -o test.i

-E means to stop the test.c file after preprocessing,

-o indicates that the generated target file is test.i;

Viewed through vim, the test.c code we wrote has become more than 800 lines, of which the content above the 837 lines is the content in the header file stdio.h, which is expanded in the test.c file. You can notice that the defined macro N is also gone, the N in the code is replaced with 10, and the comments are gone.

2. Compile

$ gcc -S test.i -o test.s

Using the above command, you can stop compiling the test.i file and generate the target file test.s.

After compiling, if the code has a writing function, the compiler will generate a symbol table, which stores the address of the function. In the subsequent function call of the code, it will go to the symbol table to find the address of the corresponding function.

The file obtained after compilation is as above. At this time, the code becomes assembly language, but the computer still cannot recognize it. The computer can only recognize binary language. Therefore, assembly must be executed.

3. Compilation

$ gcc -c test.s -o test.o

Executing the above command on the command line will cause the gcc compiler to stop after compiling test.c and generate the test.o file.

Assembly will convert assembly language into binary language. When you look at the code, you will find a bunch of garbled characters:

You can use hexdump to view binary files,

It’s just the last step.

4. Link

When the printf function is called in the initial code, it will be linked with the standard library, so that our code can execute the printf function.

$ gcc test.o -o test.exe

The file generated at this point can be executed

It should be noted that the executable program can be generated directly by gcc test.c -o test.exe.

2. Dynamic link and static link

In the above code, the printf() function is called, but the function itself has not been implemented by itself, and in the header file stdio.h, only the function declaration is placed, and there is still no function implementation, then the printf() function Where exactly? The answer is libraries. In order to use the code in the library, it must be linked with the library.

1. Dynamic link

Dynamic linking is to generate a link with the library function when calling the library function. When the executable program is executed, it will jump to the library to execute when the corresponding command is executed. By default, the executable program generated by the compiler All are dynamic links. You can use the file command to check whether the executable program is statically linked or dynamically linked.

As shown in the figure, use the file command to view the executable program test.exe, and you can see the following information. Among them, executable indicates that it is an executable program, and dynamically linked indicates that it uses a dynamic link.

2. Static link

Static linking is to copy the code in the library into your own code to generate an executable program. Use the -static option at compile time to allow the generated executable program to be statically linked.

As shown in the figure, the generated test_static.exe file is a static link.

3. Advantages and disadvantages

Since the files generated by dynamic links need to jump to the library frequently for execution, all files generated by dynamic links will be slower than files generated by static links when executed;

At the same time, since the static link is to put the code in the library into its own executable program, the file generated by the static link is very large, and the size difference between the above two files is almost 100 times!

3. Makefile

Makefile is a file (it can also be written as makefile), which stores the dependencies and methods of the files. as the picture shows:

If test.exe does not exist in the current directory, it will look for the dependencies of test.exe, that is, test.exe is generated by test.o, and execute the dependency method of test.exe: gcc test.o -o test.exe.

If there is no test.o in the directory, it will continue to look for the dependencies of test.o, and then find the dependencies of test.o in the file: gcc -c test.s -o test.o, repeat the above content until you find test.o .c exists under the directory.

  1. make

Enter the make command directly on the command line, make will search for the makefile in the current directory, and execute the content if it finds it. By default, make will only execute the first command in the makefile, which is generated in the above file test.exe file, if the files in the dependencies are not in the directory, it will be executed until the first file is generated.

2.PHONY

Let’s add a clean command to the makefile to remove our generated files:

But after execution, the following message will appear:

make: `test.exe’ is up to date.

And clean has not been executed. First of all, it should be noted that because clean is not the first instruction, and there is no file dependent on it, it will not be executed, so you need to enter make clean to execute.

So now to solve the first problem, namely make: `test.exe’ is up to date.

This information shows that we have generated test.exe, and test.c has not been changed, which means that test.o will not be changed, that is, test.exe does not need to be regenerated, if .PHONY is used Indicates that no matter whether the dependent files are modified or not, it will be executed again.

As shown in the figure, if it is modified by .PHNOY, even if test.exe exists in the directory and test.o has not been changed, the corresponding dependent method will be executed.