LinuxLinux dynamic library and static library

?

?Personal homepage: @Sherry’s growth path
Learning community: Sherry’s path to growth (personal community)
Column link: Linux
The road is long and vast, and there are expectations for everything

Previous blog: [Linux] Linux process control

Article directory

  • Basic principles of dynamic and static libraries
  • Understand dynamic and static libraries
    • Pack
    • use
  • Packaging and use of dynamic libraries
    • Pack
    • use
  • Summarize:

Basic principles of dynamic and static libraries

The essence of dynamic and static libraries is “semi-finished products” of executable programs.

We all know that turning a bunch of source files and header files into an executable program requires the following four steps:

Preprocessing: Complete header file expansion, comment removal, macro replacement, conditional compilation, etc., and finally form the xxx.i file.
Compilation: Complete lexical analysis, syntax analysis, semantic analysis, symbol summary, etc. After checking, the code will be translated into assembly instructions, and finally the xxx.s file will be formed.
Assembly: Convert assembly instructions into binary instructions, and finally form the xxx.o file.
Link: Link each generated xxx.o file to finally form an executable program.

For example, to use test1.c, test2.c, test3.c, test4.c and main1.c to form an executable file, we need to first obtain the target files test1.o, test2.o, test3.o, test4 of each file. o and main1.o, and then link these object files to finally form an executable program.

If we also need to use test1.c, test2.c, test3.c, test4.c and the project’s main2.c or main3.c in another project to form an executable program respectively, then the steps for generating the executable program are also the same.

In fact, for source files that may be frequently used, such as test1.c, test2.c, test3.c, and test4.c here, we can convert their target files test1.o, test2.o, test3.o , test4.o for packaging. When these four target files are needed later, the target files in this package can be linked to each other, and this package can actually be called a library.

In fact, all libraries are essentially a collection of target files (xxx.o). The library files do not contain the main function but only contain a large number of methods for calling. Therefore, the dynamic and static libraries are essentially executable programs. “semi-finished products”.

Understanding dynamic and static libraries

Create a file and write the following code under Linux and generate an executable program.

#include <stdio.h>

int main()
{<!-- -->
printf("hello world\
"); //Library function
return 0;
}

This is the simplest code, and everyone knows the result, which is hello world.

Let’s use this simple code to get to know the dynamic and static libraries.

In this code, we can output hello world by calling printf. The main reason is that the gcc compiler also links the C standard library when generating the executable program.

Under Linux, we can use the ldd file name to view the library files that an executable program depends on.

Among them, libc.so.6 is the library file that the executable program depends on. We can find through the ls command that libc.so.6 is actually just a soft link.

In fact, the source files libc-2.17.so and libc.so.6 of the soft link are in the same directory. To further understand, we can use the file file name command to view the file type of libc-2.17.so.

At this point we can see that libc-2.17.so is actually a shared object file library. To be precise, this is a dynamic library.

In Linux, those with the .so suffix are dynamic libraries, and those with the .a suffix are static libraries.
In Windows, the .dll suffix is a dynamic library, and the .lib suffix is a static library.
The libc.so.6 that the executable program here depends on is actually a C dynamic library. When we remove the prefix lib of a dynamic and static library, and then remove the suffix .so or .a and the version number after it, what is left is this The name of the library.

The gcc/g++ compiler is dynamically linked by default. If you want to link statically, you can carry a -static option.

gcc -o mytest-s mytest.c -static

The executable program generated at this time is statically linked. It can be obviously found that the file size of the executable program generated by static linking is much larger than the file size of the executable program generated by dynamic linking.

The executable program generated by static linking does not depend on other library files. At this time, when we use the ldd file name command to view the library files on which the executable program depends, we will see the following information.

In addition, when we look at the executable programs generated by dynamic and static linking respectively
When looking at the file types, you can also see that they are dynamically linked and statically linked respectively.

Characteristics of dynamic and static libraries

static library

Static libraries copy the code of the library into the executable file when the program is compiled and linked. The generated executable program will no longer need the static library when running. Therefore, the size of the executable program generated using the static library is generally relatively small. big.

advantage:

After using a static library to generate an executable program, the executable program can run on its own and no longer needs the library.

shortcoming:

Using a static library to generate an executable program will take up a lot of space, especially when multiple static programs are loaded at the same time and these static programs all use the same library, then there will be a lot of duplicate code in the memory.

dynamic library

Dynamic libraries link the corresponding dynamic library code when the program is running. Multiple programs share the code of the library. An executable file linked with a dynamic library only contains a table of the entry addresses of the functions it uses, rather than the entire machine code of the target file where the external function is located.

Before the executable file starts running, the machine code of the external function is copied from the dynamic library on the disk to the memory by the operating system. This process is called dynamic linking. Dynamic libraries are shared among multiple programs, saving disk space. The operating system uses a virtual memory mechanism to allow a dynamic library in physical memory to be shared by all processes that use the library, saving memory and disk space.

advantage:

Save disk space, and when multiple programs using the same dynamic library are running at the same time, the library files will be shared through the process address space, and there will be no duplicate code in the memory.
shortcoming:

It must rely on dynamic libraries, otherwise it cannot run.
Packaging and use of static libraries
In order to make it easier to understand, when demonstrating the packaging and use of dynamic and static libraries below, the following four files are used as examples, including two source files add.c and sub.c, and two header files add.h and sub.h.

The contents of add.h are as follows:

#pragma once

extern int my_add(int x, int y);

The contents of add.c are as follows:

#include "add.h"

int my_add(int x, int y)
{<!-- -->
return x + y;
}

The contents of sub.h are as follows:

#pragma once

extern int my_sub(int x, int y);

The contents of sub.c are as follows:

#include "sub.h"

int my_sub(int x, int y)
{<!-- -->
return x - y;
}

The code content is very simple, so I won’t elaborate too much.

Packaging

Next we will use these four files to package and generate a static library:

Step 1: Let all source files generate corresponding target files

Step 2: Use the ar command to package all target files as static libraries

The ar command is GNU’s archiving tool and is often used to package target files into static libraries. Below we use the -r option and -c option of the ar command for packaging.

-r (replace): If the target file in the static library file is updated, replace the old target file with the new target file.
-c(create): Create a static library file.

ar -rc libcal.a add.o sub.o

In addition, we can use the -t option and -v option of the ar command to view the files in the static library.

-t: List files in static libraries.
-v(verbose): Display detailed information.

ar -tv libcal.a

Step 3: Organize the header files and generated static libraries

When we give our library to others, we actually need to give them two folders. One folder contains a collection of header files, and the other folder contains all the library files.

Therefore, here we can put the two header files add.h and sub.h into a directory named include, put the generated static library file libcal.a into a directory named lib, and then put these Both directories are placed under mathlib. At this time, mathlib can be used by others.

Use Makefile

Of course, we can write all the above commands to be executed into the Makefile. Later, when we want to generate static libraries and organize header files and library files, we can do it in one step, without having to type so much every time we regenerate. command, which also reflects the power of Makefile.

After writing the Makefile, only one make can generate the target files corresponding to all source files and then generate the static library.

A single make output can organize header files and static libraries.

Use

Create the source file main.c and write the following simple program to try to use our packaged static library.

#include <stdio.h>
#include <add.h>

int main()
{<!-- -->
int x = 20;
int y = 10;
int z = my_add(x, y);
printf("%d + %d = %d\
", x, y, z);
return 0;
}

Now there are only main.c and the static library we just packaged in this directory.

Method 1: Use options

At this time, you need to carry three options when using gcc to compile main.c to generate an executable program:

-I: Specify the header file search path.
-L: Specify the library file search path.
-l: Indicate which library in the library file path needs to be linked.

 gcc main.c -I./mathlib/include -L./mathlib/lib -lcal

At this point, we can successfully use our own packaged library file and generate an executable program.

Explain:

Because the compiler does not know where the header file add.h you included is, you need to specify the search path for the header file.
Because the header file add.h only has the declaration of the my_add function and no definition of the function, you also need to specify the search path for the library file to be linked.
In practice, there may be a large number of library files in the lib directory of the library file, so we need to indicate which library in the library file path needs to be linked. Remove the prefix lib from the library file name, then remove the suffix .so or .a and the version number after it, and the rest is the name of the library.
The three options -I, -L, and -l can be followed by spaces or without spaces.

Method 2: Copy the header files and library files to the system path

Since the compiler cannot find our header files and library files, then we can just copy the header files and library files directly to the system path.

 sudo cp mathlib/include/* /usr/include/
 sudo cp mathlib/lib/libcal.a /lib64/

It should be noted that although the header files and library files have been copied to the system path, when we use gcc to compile main.c to generate an executable program, we still need to indicate which library in the library file path needs to be linked.

Only then can we successfully use our own packaged library files and generate executable programs.

Why was the library name not specified when compiling with gcc before?

Because we use gcc to compile C language, and gcc is used to compile C programs, gcc looks for the C library by default when compiling, but at this time the compiler does not know which library we want to link to. , so we still need to use the -l option to indicate which library in the library file path needs to be linked.

Extensions:
In fact, the process of copying the header files and library files to the system path is the process of installing the library. However, it is not recommended to copy the header files and library files written by yourself to the system path. Doing so will pollute the system files.

Packaging and use of dynamic libraries

Packaging

The packaging of dynamic libraries is a little different from that of static libraries, but they are roughly the same. We still use these four files for packaging demonstration:

Step 1: Let all source files generate corresponding target files

At this time, you need to carry the -fPIC option when generating the target file from the source file:

-fPIC (position independent code): Generate position independent code.

Explain:

-fPIC acts during the compilation phase, telling the compiler to generate position-independent code. There are no absolute addresses in the code generated at this time, and all relative addresses are used, so that the code can be loaded by the loader into any location in the memory and executed correctly. . This is exactly what a shared library requires. When a shared library is loaded, its location in memory is not fixed.

If the -fPIC option is not added, when loading the code segment of the .so file, the data object referenced by the code segment needs to be relocated. The relocation will modify the content of the code segment, which will cause problems for every process that uses the code segment of this .so file. A copy of the .so file code segment will be generated in the kernel, and each copy is different, depending on the memory mapping location of the .so file code segment and data segment.

The .so compiled without -fPIC needs to be relocated again according to the loaded location when loading, because the code in it is BBS position-independent code. If the .so file is used by multiple applications, then they must each maintain a copy of the .so code (because the location where .so is loaded by each program is different, obviously these relocated codes are also different) , of course cannot be shared).

We always use -fPIC to generate .so, but never -fPIC to generate .a. However, .so can also be compiled without the -fPIC option, but such .so must redirect all entries when loading into the address space of the user program.

Step 2: Use the -shared option to package all target files as dynamic libraries

Different from generating static libraries, we do not have to use the ar command when generating dynamic libraries, we only need to use the -shared option of gcc.

gcc -shared -o libcal.so add.o sub.o

Step 3: Organize the header files and the generated dynamic library

Just like when generating a static library, for the convenience of others, here we can put the two header files add.h and sub.h in a directory named include, and put the generated dynamic library file libcal.so in a Under the directory named lib, and then put these two directories under mlib. At this time, mlib can be used by others.

Use Makefile

Of course, to generate a dynamic library, you can also write all the above commands to be executed into the Makefile. Later, when we want to generate a dynamic library and organize header files and library files, we can do it in one step.

After writing the Makefile, only one make can generate the target files corresponding to all source files and then generate the dynamic library.

A make output can organize header files and dynamic libraries.

Use

We still use the main.c we just used to demonstrate the use of dynamic libraries.

#include <stdio.h>
#include <add.h>

int main()
{<!-- -->
int x = 20;
int y = 10;
int z = my_add(x, y);
printf("%d + %d = %d\
", x, y, z);
return 0;
}

Now there are only main.c and the dynamic library we just packaged in this directory.

To explain, the method of using the dynamic library is the same as the method of using the static library just now. We can either use the three options -I, -L, -l to generate an executable program, or we can first add the header file and library file. Copy it to the system directory, and then only use the -l option to specify the name of the library that needs to be linked to generate an executable program. Below we only take the first method as an example to demonstrate.

At this time, when using gcc to compile main.c to generate an executable program, you need to use the -I option to specify the header file search path, use the -L option to specify the library file search path, and finally use the -l option to specify which one of the library file paths needs to be linked. library.

 gcc main.c -I./mlib/include -L./mlib/lib -lcal

Different from the use of static libraries, the executable program we generate at this time cannot be run directly.

It should be noted that the three options we use -I, -L, and -l tell the compiler during compilation where and who the header files and library files we use are, but when the generated executable program is generated It has nothing to do with the compiler. After the executable program is run, the operating system cannot find the dynamic library that the executable program depends on. We can use the ldd command to check it.

It can be seen that the dynamic library on which the executable program depends is not found at this time.

There are three ways to solve this problem:

Method 1: Copy the .so file to the system shared library path

Since the system cannot find our library file, we directly copy the library file to the system shared library path, so that the system can find the corresponding library file.

sudo cp mlib/lib/libcal.so /lib64

The executable program will be able to run smoothly.

Method 2: Change LD_LIBRARY_PATH

LD_LIBRARY_PATH is the path that the program searches for when running a dynamic search library. We only need to add the directory path where the dynamic library is located to the LD_LIBRARY_PATH environment variable.

 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/cl/BasicIO/testlib/project/mlib/lib

At this time, if we use the ldd command to view the executable program, we will find that the system can now find the dynamic library that the executable program depends on.

Now we can run the executable program normally.

Method 3: Configure /etc/ld.so.conf.d/

We can solve this problem by configuring /etc/ld.so.conf.d/. All configuration files stored in the /etc/ld.so.conf.d/ path are configuration files with the suffix .conf, and these All paths are stored in the configuration file. The system will automatically find the paths in all configuration files under the /etc/ld.so.conf.d/ path, and then search for the libraries you need in each path. If we put the path of our own library file under this path, then when the executable program runs, the system will be able to find our library file.

echo /home/sherry/11.9/project1/mlib/lib > sherry.conf

First, save the path to the directory where the library file is located into a file with .conf as the suffix.

Then copy the .conf file to the /etc/ld.so.conf.d/ directory.

But at this time, when we used the ldd command to view the executable program, we found that the system still did not find the dynamic library that the executable program depends on.

At this time, we need to use the ldconfig command to update the configuration file. After the update, the system can find the dynamic library that the executable program depends on.

 sudo ldconfig

At this time we can run the executable program normally.

Summary:

Today we learned about Linux dynamic libraries and static libraries, understood the basic principles of dynamic and static libraries, understood dynamic and static libraries, the packaging and use of dynamic libraries, etc. Next, we will continue to learn other knowledge about Linux. I hope my articles and explanations can provide some help to everyone’s study.

Of course, there are still many shortcomings in this article, and you are welcome to communicate via private messages and make comments and corrections at any time! See you next time~