Makefile and gcc tips

Three basic things you should definitely know about the make and gcc

Here are three tips you should definately know for the make and gcc/g++ if you work with c/c++.

This is the script for the video here (https://www.youtube.com/watch?v=-YAQxbLEX10&t=1387s)

The relationship between make and gcc/g++

GCC is the GNU C Compiler, simply speaking, it can help you to tranform the code writtern by C language into the machine code that can be executed by the computer. Similarly, g++ can be used to compile the c++ source file.

The process of transforming the source code such as .c file into the executable file actually contains two step, the frist step is called the compiling process, the second step is called the linking process. This is easy to understand, we provide multiple source file to the gcc, and we got one executable file at last. The gcc first compile all source file into the object file, and then link multiple object file into one executable file.

What is make and makefile? The actual project may contains dozens of .c file, these files may also have multiple depedency path, it is complex to compile them together. Even if the project owner knows how to compile these things together, how could we share these things with other people that may try to compile this project?

We need to standardize this compiling process. A makefile can specifies a set of rules in terms of the target and their depedencies in the following format:

target: depedencies
command

The depedencies are simple to understand, we just need to specify the file on the disk, it can be the source file such as .c or the archive file such .so or .a file.

In order to express the key idea of the compiling, the makefile contains several implicit rules. For example, they specify that the .o file can be obtained from the -c file by compilation. and the excutable can be made by linking together .o files.

For example in this makefile, we specify two targets, the name of the first one is main, it depends on the main.o and hellofunc.o

the second target is the clean, there is no depedency, just executing the command, which tries to remove the compiled file. Of course we can also add command at the main target instead of using the emplicit rule associated with the makefile. By this way, we just declare the gcc command in the command section of the main target.

CC=gcc
CFLAGS=-Wall

main: main.o hellofunc.o
clean:
rm -f main main.o hellofunc.o

With the increasing complexity of the project, we may even do not want to write makefile by ourselves, in this case, more advanced tools appears such as cmake which can help us to create the makefile by more human readable rules expression. Furthermore, the tools such as spack and conda env can help us to mange the whole process of executing the cmake and installing, which are out of the topic of this vedio, but idea is that there is a whole software stack to manage the project compiling and the gcc/g++ and associated makefile is the foundamental for these softwares.

Makefile can be writtern in more efficient way, you may try to follow this tutorial (https://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/) to get more ideas here

Three key flags: -I -L and -l

Three most often used parameter in gcc is the -I -L and -l

the -I represent the specified direactory to find the header file. This is also known as the include path.

the -L represent the specified direactory to find the linked library. This is also known as the link path.

The -l represent the specific name of the library that should be linked into the target file.

Let’s use an exmaple to see what is the default include path of the gcc and how we added new include bath by the -I parameter (use example to show how to add the path and how is can be shoswn based on -v option of the gcc)

For the aspect of linking stage, gcc use the ld program to do the link things, you may search this to see what are default linking search path.

ld --verbose | grep SEARCH_DIR | tr -s ' ;' \\012

You may also use gcc -print-search-dirs to see what path are searched by gcc when it calls the ld.

We may also use ldd to check the executable file to see if it links to a correct libraray. This is useful sometimes when you links to the library with a different version. When we use the -L parameter, we just want to tell gcc that we want ld program to also search this path during the compiling stage. -l tries to tell gcc that we want to link this specific libraray.

There are different types of the binaray code

  • Executable code, we can execute it direactly and there is main function.
  • Archieve libraries, other code can call the functions in this library. libraries can be the shared library or the dynamic library.
  • Common object file, such as .o file, this code is not fully linked which main contains the unresolved symbols.

(when explain the nm things, pay attention to the namemangling between the c and cpp, the results can be different when you use the nm to check the contents)

.so file stands for the “shared object”, it saves the disk space since, if we need to call the function listed in the .so file, we do not recompile the whole program and we only need to source files associated with .so file. We can add specific shared library by adding it into the LD_LIBRARY_PATH env virables. gcc adopts the shared link in default if there is both .so and .a file. .so file is dynamically loaded and linked with the executable file during the execution stage. We may use the -fPIC if we want construct the shared library from the -o file (refer to this example http://www.yolinux.com/TUTORIALS/LibraryArchives-StaticAndDynamic.html)

.a file is the static libraries, when a program is linked against a static library, the machine code from the object file for any external functions used by the program is copied into the final executatble.

In the previous example, we see the .o file, this is compiled into the machine code, but it is not fully linked. it may contained the unresolved references to symbols defined in other object. We have .o file firstly and then use the .o file to construct the .so or .a file.

several useful command

discuss the commonly used command

gcc --verbose this can print out specific operation for gcc, such as searching path and include path

ldd this can check the linked libraray for the exetuable file, we may check if it can link to a correct library file

nm lists symbols contained in object files (how to understand the symbol (https://stackoverflow.com/questions/46515755/exactly-what-is-a-symbol-reference-in-an-object-file)
(https://en.wikipedia.org/wiki/Symbol_(programming))
)

readelf command can also list symbols information.

References

An introduction to GCC written by Brian Gough

https://transang.me/library-path-in-gcc/

http://www.yolinux.com/TUTORIALS/LibraryArchives-StaticAndDynamic.html

An introduction to GCC (for the GNU Compiler gcc and g++)

推荐文章