For more blog posts, seeTotal catalog of romantic carriages for audio and video systems learning

If you are willing to peel away my heart layer by layer, you will find, you will be surprised, you are my most repressed, deepest secret

This is a very classic song by Yang Zongwei, and I quote it because today’s content is like the Onion, which needs to be peeled off one layer at a time, the tightly wrapped coat of the compiler…

Previous articles in this series have covered some of the key points and difficulties of C and C++, but starting with this post we will explore the C/C++ compilation system (not just the four main processes like many other articles, but the nature of linking in more depth) and the memory model. From the compilation system, we will explain the contents of MakeFile and cmake, and these contents, will do audio and video development to build a solid foundation for third-party libraries

GCC

The compiler

Any programmer will know what compilation is, but it’s worth mentioning a couple of sentences here to highlight the completeness of the article. We usually write computer programming language at reading, computer only know binary instructions, but if you want us to write simple binary instructions that terrible and very inefficient, so you need to have a broker as a translation, the clever people would come up with that let the computer translation to deal with this matter, so the compiler will arises at the historic moment. A compiler is a program we write (C, C++, Java, etc.) translated into binary instructions that a calculator can read.

What is the GCC

GCC is an outgrowth of the GNU Project. It was originally a compiler for C, but has since evolved. It can also be used as a compiler for programs written in C++, Go, Objective-C and many other compiled languages, and is now referred to as the “GNU compiler suite.”

There are many ides that already integrate GCC, but for a deeper understanding of the compilation process, this article will use GCC in its original form, the command-line approach.

GCC and g + +

GCC can compile C and C++, so for C and C++, the GCC compiler already provides an interface to call it. For C or C++ programs, you can call the GCC compiler by executing GCC or g++ directives. (actually GCC, g++ instruction is also a wrapper for CCP (preprocessor instruction), cc1 (compiler instruction), as (assembly instruction) instruction)

GCC can also be used to compile C++ programs, and g++ can also be used to compile C programs.

In fact, you can compile any program code that GCC supports using the GCC command.

GCC determines the file type based on the file name suffix. That is, if the xxx.c file is encountered, the file is compiled as a C program by default. If the xxx.cpp file is encountered, the file is compiled as a C++ program by default.

The g++ command compiles the object file the same way it would compile C++ code, regardless of its suffix. Since C++ is compatible with C, g++ can be compiled in C++ even if xxx. C is encountered

Compiling C++ code with GCC is different from g++ and a bit more tedious:

On a Linux terminal, add a c++ source file:

ubuntu@VM-20-7-ubuntu:~/study/projects/main$ vim main.cpp
Copy the code

The main. CPP:

#include <iostream>
#include <string>
using namespace std;
int main(a){
    string str ="I am programmer";
    cout << str << endl;
    return 0;
}

Copy the code

Execute g++ to compile it as an executable:

~/study/projects/CatDemo/src$ g++ Cat.cpp
Copy the code

No error was reported.

But if you compile with GCC:

ubuntu@VM-20-7-ubuntu:~/study/projects/main$ gcc main.cpp 
Copy the code

An error is reported immediately:


/usr/bin/ld: /tmp/ccZ7BeFi.o: in function `main':
main.cpp:(.text+0x24): undefined reference to `std::allocator<char>::allocator()'
/usr/bin/ld: main.cpp:(.text+0x3b): undefined reference to `std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&)'(There are many more)...Copy the code

The root cause of the error is that the program uses the standard library and provided class objects, which GCC cannot find by default and must be explicitly told by “-xc++” that it is a C++ file. Plus the compiler option “-lstdc++ -shared-libgcc” to find the C++ library:

ubuntu@VM-20-7-ubuntu:~/study/projects/main$ gcc -xc++ main.cpp -lstdc++ -shared-libgcc 
ubuntu@VM-20-7-ubuntu:~/study/projects/main$ 
Copy the code

This compiles with no problem.

Since compiling C++ with GCC is significantly more difficult, this series of C programs are compiled with GCC and C++ programs are compiled with g++.

C/C++ compilation process

The above example uses GCC directly to process the source code to the executable in one step, but there are actually four steps: Preprocessing, Compilation, Assembly, and Linking:

(Photo credit:GCC and Make Compiling, Linking and Building C/C++ Applications)

pretreatment

Preprocessing is the actual preparation for compiling. It is mainly used to process commands that start with #in source files and header files, such as #include, #define, #ifdef, etc. The rules of pretreatment are generally as follows:

  1. Remove all #define and expand all macro definitions.
  2. Handle all conditional compilation commands such as #if, #ifdef, #elif, #else, #endif, etc.
  3. Process the #include command and insert the contents of the included file into the command’s location, just like copy and paste. Note that this process is recursive, meaning that the included file may also contain other files.
  4. Delete all comments // and /*… * /.
  5. Add line numbers and file name identifiers to give specific code locations for debugging and error.
  6. Keep all #pragma commands, because the compiler needs to use them.

The preprocessing is performed by the following command, resulting in an. I file. I files are also source files containing C code, except that all macros have been expanded and all contained files have been inserted into the current file.

$gcc -E demo.c -o demo.i  
Copy the code

-e indicates that the preprocessing is performed, and -o indicates the name of the output file. If -o is not specified, the preprocessed result file content is displayed on the terminal.

Here’s an example:

Go back to the Linux terminal and create a cat.cpp:

ubuntu@VM-20-7-ubuntu:~/study/projects/CatTest$ vim Cat.cpp
Copy the code

Create a cat.h:

#ifndef UNTITLED_CAT_H
#define UNTITLED_CAT_H

/ * * * * / cat
class Cat {
public:
    /** * The cat eats */
    void eat(a);
};
Copy the code
ubuntu@VM-20-7-ubuntu:~/study/projects/CatTest$ vim Cat.h
Copy the code

Define a Cat class:

#endif //UNTITLED_CAT_H

#include "Cat.h"
#include <iostream>
#define EAT "Cat::eat"

void Cat::eat(a) {
    std::cout << EAT << std::endl;
}
Copy the code

Execute preprocessing commands:

ubuntu@VM-20-7-ubuntu:~/study/projects/CatTest$ g++ -E Cat.cpp -o Cat.i
ubuntu@VM-20-7-ubuntu:~/study/projects/CatTest$ ls
Cat.cpp  Cat.h  Cat.i  main.cpp
Copy the code

The Cat. I file has been generated.

It’s a very long document, most of which we can’t understand anymore. Here is a screenshot of the main part,H and iostream.h2 headers have been expanded, but only when cat. h is expanded can the Cat class be defined in cat. I. And the “#ifndef UNTITLED_CAT_H #define UNTITLED_CAT_H” statement defined by cat. h is missing

Because the file is too big, let’s go straight to the end of the file:

ubuntu@VM-20-7-ubuntu:~/study/projects/CatTest$ vim Cat.i +
Copy the code

Finally, we get to see our main character Cat in its own right.The macro #define EAT “Cat:: EAT “has been expanded to replace the used position with the corresponding value.

compile

Compilation is the pre-processing of the file for a series of lexical analysis, syntax analysis, semantic analysis and optimization to generate the corresponding assembly code file. Compilation is the core part of the entire program construction, and it is also one of the most complex parts.

GCC uses:

$gcc-s demo. I (or demo.c) -o demo.sCopy the code

-s Converts the source file (with or without preprocessing) to an assembler file. The file name suffix is S.

Now compile cat. I above:

ubuntu@VM-20-7-ubuntu:~/study/projects/CatTest$ g++ -S Cat.i -o Cat.s ubuntu@VM-20-7-ubuntu:~/study/projects/CatTest$ ls  Cat.cpp Cat.h Cat.i Cat.s main.cpp ubuntu@VM-20-7-ubuntu:~/study/projects/CatTest$Copy the code

See the Cat. S:This is the assembly code we used to be familiar with.

Because assembly language is not the main topic of this article, I will skip this section for now.

assembly

Is the assembly code into machine code process, this process is relatively simple, there is no complex syntax, there is no semantics, there is no need to do instruction optimization, mainly assembly statements and machine instructions of the translation table one by one.

Command as follows:

$gcc-c demo. I (or demo.c or demo.s) -o demo.oCopy the code

-c Indicates the object file that generates machine code from the source file or assembly file. The suffix is O.

Assembly of the Cat, s:

ubuntu@VM-20-7-ubuntu:~/study/projects/CatTest$ g++ -c Cat.s -o Cat.o ubuntu@VM-20-7-ubuntu:~/study/projects/CatTest$ ls  Cat.cpp Cat.h Cat.i Cat.o Cat.s main.cppCopy the code

Object files are organized in much the same way as executable files, except that some variables and functions are not addressed and the program cannot be executed. So an important function of the next link is to find the addresses of these variables and functions.

To understand the next step, linking, more fundamentally, we need to understand the object file structure.

Under Linux, object files and executables are collectively referred to as ELF files.

Object files come in three forms:

  1. Relocatable object file: Contains binary code and data in a form that can be merged with other relocatable object files at compile time to create an executable object file corresponding to Linux’s.o and static link libraries.
  2. Executable object file: Contains binary code and data in a form that can be copied directly into memory for execution, corresponding to an executable file.
  3. Shared object file: A special type of relocatable object file that can be reloaded or dynamically loaded into storage and linked at run time, corresponding to a dynamically linked library.

Here is the structure of the ELF file (image from:What’s in the object file and what’s in the executable?) :The sections andVirtual memory(Figure fromComputer System Foundation of Nanjing University (I) Speaker: Yuan ChunfengThe courseware) :

We only need to know the following paragraphs for now:

  1. ELF Header file Header: describes the attributes of the entire object file, including whether it is executable, dynamically linked or statically linked, what the entry address is, target hardware, target operating system, segment table offset and other information.
  2. .text code snippet: store the compiled machine instructions, that is, the binary code of each function. A C program consists of several functions, and the execution of C programs is the mutual call between functions.
  3. .data: Data segment that stores global and static variables. (Corresponding to the global data area in C language memory management)
  4. .rodata: read-only data segment, storing general constants, string constants, etc., corresponding to the constant area in the rampage C language memory management said.
  5. Rel. Text., rel. Data: relocation segments, which contain global symbols and relocation entries in the target file to be relocated.
  6. Symtab symbol table, store global variable names, local variable names, function names in the string table offset.

In addition to converting assembly instructions into binary instructions, the assembly file generates object files. Another important thing is to output symbol tables.

What is a sign? Variables (function names) are address mnemonics that exist to facilitate human processing. They are also called “symbols”. Their starting address is called “symbol definition”, and when they are called, they are also called symbolic references.

Now let’s talk about what a symbol table is, which is crucial to understanding the nature of the links that follow. A symbol table is essentially a database that stores information about variables, function calls, and so on in code. This table stores data in key-value mode. The names of variables and functions correspond to the key part of the table, and the value part contains information such as the type of the variable, the length of bytes it occupies, or the return value of the function.

Through the command

readelf -h Cat.o
Copy the code

Check out the ELF Header for Cat.o:

ubuntu@VM-20-7-ubuntu:~/study/projects/CatTest$ readelf -h Cat.o ELF Header: Magic: 7F 45 4C 46 02 01 01 00 00 00 00 00 00 00 00 00 00 Class: ELF64 // Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIx-system V ABI Version: 0 REL (Relocatable file) Machine: Advanced Micro Devices X86-64 Version: 0x0 Entry point address: 0x0 Start of Program headers: 0 (bytes into file) Start of section headers: 1912 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 0 (bytes) Number of program headers: 0 Size of section headers: 64 (bytes) Number of section headers: 16 Section header string table index: 15Copy the code

The current information that needs attention has been annotated. It can be seen that cat. o is a relocatable object file, that is, it needs to be linked with other relocatable object files into an executable file to run.

To disassemble the symbol table of cat. o, run objdump to disassemble the symbol table:

ubuntu@VM-20-7-ubuntu:~/study/projects/CatTest$ objdump -t Cat.o Cat.o: file format elf64-x86-64 SYMBOL TABLE: //l represents the local symbol, Only in this file 0000000000000000 L df *ABS* 0000000000000000 cat.cpp 0000000000000000 L D. text 0000000000000000.text 0000000000000000 l d .data 0000000000000000 .data 0000000000000000 l d .bss 0000000000000000 .bss 0000000000000000 l d .rodata 0000000000000000 .rodata 0000000000000000 l O .rodata 0000000000000001 _ZStL19piecewise_construct 0000000000000000 l O .bss 0000000000000001 _ZStL8__ioinit 000000000000003b l F .text 000000000000004d _Z41__static_initialization_and_destruction_0ii 0000000000000088 l F .text 0000000000000019 _GLOBAL__sub_I__ZN3Cat3eatEv  0000000000000000 l d .init_array 0000000000000000 .init_array 0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack 0000000000000000 l d .note.gnu.property 0000000000000000 .note.gnu.property 0000000000000000 l d .eh_frame 0000000000000000. Eh_frame 0000000000000000 L D. comment 0000000000000000. Comment //l Specifies the global symbol. F. Text 00000000000000003B _ZN3Cat3eatEv //UND means undefined, Need to be linked 0000000000000000 *UND* 0000000000000000_zST4cOUT 0000000000000000 *UND* 0000000000000_global_offset_table_ 0000000000000000 *UND* 0000000000000000 _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc 0000000000000000 *UND* 0000000000000000 _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ 0000000000000000 *UND* 0000000000000000 _ZNSolsEPFRSoS_E 0000000000000000 *UND* 0000000000000000 _ZNSt8ios_base4InitC1Ev 0000000000000000 *UND* 0000000000000000  .hidden __dso_handle 0000000000000000 *UND* 0000000000000000 _ZNSt8ios_base4InitD1Ev 0000000000000000 *UND* 0000000000000000 __cxa_atexitCopy the code

Here you can see the information of each segment clearly, and the points that you need to pay attention to have been marked with comments. The particular thing that you need to pay attention to here is _ZN3Cat3eatEv, that is, eat is placed in the.text segment, and pay attention to the difference when you check main.o later. UND stands for a symbol that is unknown and needs to be linked.

Assembly introduction to this end, is mainly introduced to the structure of the target file, paving the way for the link, the next is the most eye-catching link stage, because this is the cornerstone of multi-file module development, but also the whole compilation stage for us the most need to pay attention to point.

link

Linking simply means that in a multi-module program, a source file will reference variables or methods of other source files, but the compilation into the target file is for a single source file, so the target file does not know the specific address of variables or functions of other modules being referenced. Therefore, the address of these referenced variables or functions in the object file is in the state of shelving, and linking is the process of combining these object files together to determine the address of these referenced variables or functions, that is, filling the address defined by the symbol in the symbol reference.

For an example of not too accurate, built with lego bricks as a football field, points module is the venue, the audience, ceiling, wall several modules, and there is connection between modules, in the whole put together before, though the audience know the bottom will be connected to the ground, but the site also assemble at another place now, so for the time being the first to leave a hole at the bottom, When the modules are combined (linked), connect them at specific points of connection to the site.

In general, there are two steps to linking:

1. Merge the target file segments, and then perform symbol parsing after the symbol table is merged. 2. Symbol redirection.

A merge of target file segments is a merge of the same file segments, as shown in the figureComputer System Foundation of Nanjing University (I) Speaker: Yuan ChunfengThe courseware) : Symbol resolution is when the linker associates a reference to each symbol with a symbol definition. Relocation is to calculate the absolute address of each defined symbol in the virtual address space, and then modify the address at the symbol reference of the executable file to the repositioned address information.

To better display links, I made a small change to Cat and added a line:

#include "Cat.h"
#include <iostream>
#define EAT "Cat::eat"
// The newly declared global variable
int externData = 10;

void Cat::eat(a) {
    std::cout << EAT << std::endl;
}
Copy the code

Create a main.cpp:

#include <iostream>
#include "Cat.h"
static int s = 1;
static int t;

int a = 1;
int b;
// Declare variables referenced from cat.cpp
extern int externData;

void print(const char* c){
    std::cout << c << std::endl;
}

int main(a) {
    const char* c = "Here is a Cat";
    
    Cat *cat = new Cat(a);// The cat eats
    cat->eat(a); std::cout <<"externData:" << externData << std::endl;
    
    print(c);
    return 0;
}
Copy the code

Compile to object file and view symbol table information:

ubuntu@VM-20-7-ubuntu:~/study/projects/CatTest$ g++ -c Cat.cpp
ubuntu@VM-20-7-ubuntu:~/study/projects/CatTest$ objdump -t Cat.o
Copy the code

You can see the new global variable (symbol) externData:Compile main.cpp and view the generated object file:

ubuntu@VM-20-7-ubuntu:~/study/projects/CatTest$ g++ -c main.cpp
ubuntu@VM-20-7-ubuntu:~/study/projects/CatTest$ objdump -t main.o
Copy the code

Notice the Cat eat method (Cat ->eat(); in main.cpp). The externData variable that references Cat is unund, waiting for the link to assign the actual address.

Now link main, CPP, and cat. CPP:

ubuntu@VM-20-7-ubuntu:~/study/projects/CatTest$ g++ main.o Cat.o -o Main.exe
ubuntu@VM-20-7-ubuntu:~/study/projects/CatTest$ ls
Cat.cpp  Cat.h  Cat.i  Cat.o  Cat.s  main.cpp  Main.exe  main.o
Copy the code

Use the readelf command to view the generated Elf header of main.exe:C: the Executable file is Executable. C: the Executable file is Executable. C: the Executable file is Executable.

GCC enable-default-pie GCC enable-default-pie GCC enable-default-pie

Position-independent-executable is a feature of Binutils,glibc, and GCC that can be used to create code between shared libraries and regular Executable code – programs that can be reassigned like shared libraries and must be connected to SCRt1.o. Standard executable programs require a fixed address, and only when loaded to this address can the program execute correctly. PIE enables programs to be loaded anywhere in main memory like a shared library, by compiling programs to be position-independent and linked to ELF shared objects.

The reason PIE was introduced was to allow programs to be loaded at random addresses. Typically, kernels run at fixed addresses. If you could switch to position-independent, it would be difficult for an attacker to use executable code in the system to carry out attacks. Attacks such as buffer overflows cannot be carried out. And the cost of this security improvement is small

To disable position-independent-executable, add the -no-pie option:

This time the Type is normal:

Take a look at the symbol table in main.exe:

ubuntu@VM-20-7-ubuntu:~/study/projects/CatTest$ objdump -t Main.exe 
Copy the code

** We are happy to see that the Cat eat method and the externData variable that references Cat have addresses in the.data and.text sections, respectively. ** This is the key handling of links.

If everything is normal, the link is successful

For more details about links, you can read the chapter on in-depth Understanding of computer systems “Links” and the fundamentals of Computer Systems at Nanjing University (I).

Static link library, dynamic link library

C/C++ library file, similar to the Java Jar package, is essentially a compressed file, which is the implementation of a functional module of the various code package. Through the library file, the code can be easily reused, greatly improving the efficiency of development.

For example, the C language standard library provides a large number of functions, such as scanf(), printf(), strlen(), etc. As long as the corresponding header file is introduced into the source file, you can access the functions or variables of the library file. The advantage of this combination of header file and library file access mechanism is that we can expose some external interfaces and variables to be used through the header file, while not exposing the source code of internal implementation.

In C/C++, library files are processed in the linking stage. There are two linking methods: one is static linking, that is, before the executable is generated, and the other is dynamic linking, that is, after the executable is generated.

A statically linked library packages the entire library file into an executable during the linking phase before the executable is generated Advantages are:

  1. Executable files can run independently without the need for additional library files
  2. Saves link time at run time compared to dynamic link libraries (but is almost negligible)

Disadvantages are:

  1. Whenever a library file needs to be changed, the entire executable is relinked to generate a new executable.
  2. Executables generated by static linked libraries are larger and therefore take up more disk space than those generated by dynamic linked libraries.
  3. If the library file is used by more than one program in the system, the memory space will be wasted because of repeated use.

Dynamically linked libraries link to executables at runtime.

  1. Once the library file needs to be changed, only the library file needs to be replaced, and the executable program does not need to be modified.
  2. Compared with the executable files generated using static linked libraries, the executable files generated using dynamic linked libraries are smaller and therefore less disk space.
  3. If multiple programs use the library file in the system, it can be reused, that is, it will not cause the waste of memory space.

Disadvantages are:

  1. Executable files can not run independently, need to bring additional library files
  2. Increased dynamic link time at runtime compared to static link libraries (but almost negligible)

It can be seen that the advantages and disadvantages of static link library and dynamic link library are opposite. In general, dynamic link library is more flexible and saves space, so it is generally preferred to use dynamic link library.

Generate a static link library

To get a better feel for statically linked library packaging, I added the Dog class to the original folder:T h:

#ifndef UNTITLED_DOG_H
#define UNTITLED_DOG_H


class Dog {
public:
	// Dogs have only one way to bark
    void shout(a);
};


#endif //UNTITLED_DOG_H
Copy the code

Dog.cpp:

#include "Dog.h"
#include <iostream>

void Dog::shout(a) {
    std::cout << "I am dog" << std::endl;
}
Copy the code

Modify the main. CPP:The main. CPP is changed to:

#include <iostream>
#include "Cat.h"
#include "Dog.h"

static int s = 1;
static int t;

int a = 1;
int b;

extern int externData;

void print(const char* c){
    std::cout << c << std::endl;
}

int main(a) {
    const char* c = "Here is a Cat";
    
    Cat *cat = new Cat(a);// The cat eats
    cat->eat(a); std::cout <<"externData:" << externData << std::endl;
    / / dog
    Dog *dog = new Dog(a); dog->shout(a);print(c);
    return 0;
}
Copy the code

Generate dog. CPP and main. CPP corresponding object files: To make a dynamic library, we generally use AR packaging and compression instructions. First man: Simply put, ar commands can create, modify, extract an archive package file.

The static link library parameter is RCS, which can be seen from man:

r: V: s: Specific will not translate, you understand it haha ~~

Here we package cat. o and dog. o into a static link library animal. a(Linux static link library suffix a) :Animal. A static link library has been generated!

Let’s look at Animal. A’s symbol table:

ubuntu@VM-20-7-ubuntu:~/study/projects/CatTest$ objdump -t Animal.a
Copy the code

You can see that the symbol tables for cat. o and dog. o are successively explicit, and you can see that cat. o and dog. o have indeed been packed

Use g++ -static, the -static option forces the GCC compiler to use static linked libraries:

Since no name is explicitly specified, a.out is the linked executable. Perform under:Needless to say, the static link library has been linked successfully.

Generate a dynamic link library

Dynamic libraries differ from static libraries because they need to load links while the program is running.

For dynamically shared libraries, the main purpose is to allow multiple running libraries to inherit the same library code in shared memory. The problem is how can multiple processes share the same library code?

Method 1: Allocate a block dedicated address space to each shared library in advance, requiring the loader to always load the shared library at this address.

Disadvantages:

  1. It is inefficient to use the address space because even if the shared library is not loaded, the space still has to be allocated.
  2. Once the library is modified, this allocated space has to be adjusted, which makes it difficult to manage
  3. If there are many shared libraries, it is easy to have a lot of unusable memory fragmentation space between address Spaces

Method 2: Based on the shortcomings of method 1, “position-independent code” has emerged, which is designed to allow library code to be loaded and executed at any address. (that is, relative address)

The first thing to know is that in a target module (including shared target modules), the data segment always comes after the code segment, and the relative distance between the code segment and the data segment is fixed, regardless of the absolute address of the code segment and the data segment.

So the call or reference offsets within a target module are known and are themselves PIC code. Only external function calls and variable references need to be handled as PIC, as they all require relocation at link time.

In the generated PIC code, the compiler will be in the data segment to create a “global offset scale” GOT “, GOT contains each the target block reference of global data table, and give each table mesh generated a relocation records, when loading the dynamic library, the dynamic linker will relocation GOT each table, it contains the correct position.

(Here is just a brief explanation, details can be seen in The Computer System of Nanjing University (I) Speaker: Yuan Chunfeng teacher)

Start by generating an object file with location-independent code:

ubuntu@VM- 207 --ubuntu:~/study/projects/CatTest$ g++ Cat.cpp Dog.cpp -c -fpic
Copy the code

Then generate a dynamic link library from the target file:

g++ -shared Cat.o Dog.o -o Animal.so
Copy the code

Dynamic link libraries have been generated:

-rwxrwxr-x 1 ubuntu ubuntu  17072 Dec 18 12:37 Animal.so
Copy the code

Use the dynamic library and main.cpp to generate the executable:

g++ main.cpp Animal.so -o Animal.exe
Copy the code

You can see that the executable has been generated:

-rwxrwxr-x 1 ubuntu ubuntu  17680 Dec 18 20:44 Animal.exe
Copy the code

Run smoothly:

ubuntu@VM- 207 --ubuntu:~/study/projects/CatTest$ ./Animal.exe 
./Animal.exe: error while loading shared libraries: Animal.so: cannot open shared object file: No such file or directory
Copy the code

The forehead.. Could not find the dynamic link library.

Do not forget that the dynamic library is loaded at runtime, and the error is that the dynamic library file cannot be found. Using the LDD command, you can see the dynamic library information in the executable file:

You can see that we did not find the dynamic library. Why is that?

First of all, the dynamic library is loaded through the dynamic linker. The dynamic linker is an independent process of the operating system. When loading the dynamic library, there is a default search order inside it, in order of priority from high to low:

  1. DT_RPATH segment inside the executable
  2. The system environment variable LD_LIBRARY_PATH
  3. The system dynamic library cache file /etc/ld.so.cache
  4. System directory for storing dynamic/static libraries /lib/, /usr/lib, etc

1 is inside the executable, we can’t change it. 2 Run the export LD_LIBRARY_PATH=$LD_LIBRARY_PATH: XXX command, where XXX is the absolute storage path of the dynamic link library file. 3 Run the ~/. Bashrc or ~/. Add export LD_LIBRARY_PATH=$LD_LIBRARY_PATH: XXX to the last line of the file (XXX indicates the absolute storage path of the dynamic library file). Once saved, the source.bashrc command is executed (this method is only valid for the currently logged in user). /usr/lib: /usr/lib: /usr/lib: /usr/lib: /usr/lib

/usr/lib/animal. so/animal. so /usr/ animal. so

ubuntu@VM- 207 --ubuntu:~/study/projects/CatTest$ sudo ln Animal.so /usr/lib/libAnimal.so
Copy the code

Then run the LDD command to view the executable file:The dynamic library has been found

Perform under:

ubuntu@VM- 207 --ubuntu:~/study/projects/CatTest$ ./Animal.exe 
Cat::eat
externData:10
I am dog
Here is a Cat
Copy the code

Perfect ~ ~

conclusion

Is a very long very long blog, wrote three weeks, although similar content online a lot, but still hope to try to write more than most of the article deep some, and easy to understand some bar, because the level is limited, there are mistakes please correct ha ~~ next will begin to talk about the automatic construction of the content.