Compilation process of study notes

Compilation Process Notes "Programmer's Self-cultivation"

static connection

Space and Address Allocation

For the linker, the linking process is to combine several input object files into one output file after processing. There are two ways to link:

  1. Overlay in order: Superimpose the input target files in sequence, and directly merge each target file in sequence. The problem with this is that in the case of many input files, the output file will have many fragmented segments. It is a waste of space, because each segment needs to have a certain address and space alignment.
  2. Merge similar segments: Merge segments of similar nature together.

The address and space that the linker assigns to the object file have two meanings:

  1. Spaces in the output executable.
  2. The virtual address space in the loaded virtual address.

For segments with actual data, such as data and text, space must be allocated in the file and in the virtual address space. For bss, the significance of allocating space is limited to the virtual address space, because there is no content in the file. Generally, the space allocation we care about only focuses on the allocation of virtual address space.

The current connectors all use the method of merging similar segments. There are two steps to use this method:

  1. Space and address allocation : Scan all input object files to obtain the length, attributes and positions of their segments, and collect all symbol definitions and symbol references in the symbol table in the input object file, and put them into a global symbol table .
  2. Symbol analysis and relocation : read the data of the segment in the input file, relocate information, and perform symbol analysis and relocation, and adjust the address in the code.

For example, link a.0 and bo using the ld linker: ld a.o b.o -e main -o ab.

  • -e main: Indicates that the main function is used as the program entry, and the default program entry of the ld linker is _start.
  • -o ab indicates that the link output file name is ab, and the default is a.out.

In linux, the elf executable file defaults from the address0x08048000Start allocating.

Symbol resolution and relocation

In the ELF file, there is a file calledrelocation tableThe structure is specially used to save these relocation-related information, and the relocation table is often one or more segments in the ELF file. For relocatable ELF files, a relocation table must be included to describe how to modify the contents of the corresponding segment. There is a corresponding relocation table for each ELF segment to be relocated, and a relocation table is often a segment in an ELF file, so the initial relocation table can also be called a relocation segment.

For example, if there is a place in the code segment .text that needs to be relocated, then there will be a segment called .rel.text that stores the relocation table of the code segment. You can use objdump to view the relocation table of an object file.objdump -r a.o

Each place to be relocated is called a relocation entry, and the offset of the relocation entry indicates the position of the entry in the segment to be relocated.

Loading and Process

process creation

A process has an independent virtual address space. Create a process, then load the corresponding executable file and execute it. In the case of virtual storage, three things need to be done.

  1. Create an independent virtual address space.
  2. Read the executable file header, and establish the virtual space and executable fileMapping relations
  3. will CPU'sinstruction registerSet it as the entry address of the executable file and start running.

Create a virtual address spaceIt is the corresponding data structure required to create the mapping function. This step is to establish the mapping relationship between the virtual address space and the physical memory.

Read the header of the executable file and establish the mapping relationship between the virtual space and the executable file, is to establish the mapping relationship between the virtual address space and the executable file. Because when a page fault occurs, the operating system will allocate a physical page from the physical memory, then read the missing page from the disk into the memory, and then set the mapping relationship between the virtual page and the physical page of the missing page . So when a page fault occurs, you need to know where the required page is in the executable file. This is the mapping relationship between the virtual space and the executable file.

Linux refers to a segment in the virtual address space of a process asVirtual Memory Area (VMA)For example, after the operating system creates a process, it will set a VMA of the .text segment in the corresponding data structure of the process.

Set the CPU instruction register as the entry of the executable file and start running, the operating system transfers control to the integration by setting the instruction register of the CPU, and the process begins to execute.

Segment re-divides each segment of ELF from the perspective of loading. When linking object files into executable files, the linker will try to allocate segments with the same permission attributes in the same space. For example, readable and executable segments are put together. In the ELF file, these segments with similar attributes and connected together are called a segment. Section refers to each segment of the elf file. After the elf file is linked, sections with the same attributes are combined to form a segment. Section is from the point of view of linking, and segment is from the point of view of loading. The structure that describes the section attribute is called a segment table, and the structure that describes the segment is called a program header, which mainly describes how the ELF file is mapped to the virtual space of the process by the operating system.

heap and stack

In addition to being used to map each segment in the executable file, the VMA can also have other functions, and the address space of the process can be managed through the VMA. The heap and stack of the process also exist in the form of VMA in the virtual space. You can view the virtual address space distribution of the process through /proc.

The kernel loads the ELF process

At the user level, the bash process will first call ==fork()The system call creates a new process, and the new process callsexecve()==Execute the specified ELF file, the original bash process continues to return to wait for the end of the new process just started, and then continues to wait for the user to input commands.

int execve(const char *filename, char *const argv[], char *const envp[]), the three parameters represent the executed program file name, execution parameters and environment variables respectively. Glibc encapsulates the execvp() system call and provides five APIs, including execl(), execlp(), execle(), execv(), and execvp().

The corresponding entry of the execve() system call in the kernel is sys_execve(). After sys_execve() checks and copies some parameters, it calls do_execve(). do_execve() will first search for the executed file, and if it finds the file, it will read the first 128 bytes of the file to determine the format of the file.

After do_execve() reads the 128 direct file headers, it calls search_binary_handle() to search and match the appropriate executable file loading process. The loading process of ELF files is called load_elf_binary(), and the loading process of executable files is called load_aout_binary(). The main processing steps of the load_elf_binary() function are:

  1. Check the validity of the ELF file format, such as the magic number and the number of segments in the program header table.
  2. Look for the ".interp" section of the dynamic link, and set the dynamic linker path.
  3. According to the description of the program header table of the ELF executable file, the ELF file is mapped, such as code, data, and read-only data.
  4. Initialize the ELF process environment, for example, the address of the EDX register should be the address of DT_FINI when the process starts.
  5. Change the return address of the system call to the entry point of the ELF executable file. This entry point depends on the linking method of the program. For a statically linked ELF executable file, the program entry point is the address pointed to by e_entry in the file header of the ELF file .

dynamic link

Dynamic linking means not linking the target files that make up the program, and waiting until the program is about to run before linking, and delaying the linking process until runtime. This isdynamic link

The definition of dynamic linking is that the program can dynamically choose to load various program modules at runtime, which can be used to make program plug-ins. The link between the program and the dynamic library is done by the dynamic linker, not the static linker ld. Dynamic linking is to postpone the linking process from before the original program is loaded to when it is loaded.

During static linking, the entire program ends up with only one executable file, but under dynamic linking, a program is divided into several files, including executable files and shared objects on which the program depends. These parts can be called modules. That is, both executable files and shared files under dynamic linking can be regarded as a module of the program.

Load-time relocation is one of the solutions to absolute address references in dynamic modules, but there is a disadvantage that the instruction part cannot be shared among multiple processes. The part of the instruction that needs to be modified can be separated and put together with the data part, so that the instruction part can remain unchanged, and the data part can have a copy in each process. This solution is calledaddress independent code(PIC,Position-independent Code)。

The address references in the shared object module can be divided into two types according to whether they are cross-modules: internal references of modules and external references of modules . According to different reference methods, it can be divided into instruction reference and data access. So there are four cases:

  1. Function calls and jumps inside the module.
  2. Data access inside the module, such as global variables and static variables defined in the module.
  3. Function calls and jumps outside the module.
  4. Data access outside the module, such as global variables defined by other modules.

address independent code

module internal call

The caller and the called are in the same module, and the relative position is fixed. You can directly use the relative address call or the relative call based on the basic lacquer without relocation.

Module Internal Data Access

For data access inside the module, the absolute address of the data cannot be directly included in the instruction, and the only way is relative addressing. A module is usually preceded by several pages of code, followed by several pages of data. The relative position between these pages is fixed, and the relative position between any instruction and the internal data of the module it needs to access is fixed. , then you only need to add a fixed offset relative to the current instruction to access the internal data of the module.

Inter-module data access

Data access between modules is a little more troublesome than inside a module, because the target address of data access between modules is not determined until loading. Make the code address irrelevant. The basic idea is to put the address-related part into the data segment to create a pointer data pointing to these variables, also known as the global offset table (GOT, global offset table). When the code needs to refer to the global variable When , it can be indirectly referenced through the corresponding item in the GOT.

When an instruction wants to access a variable, the program will first find the GOT, and then find the target address of the variable according to the item corresponding to the variable in the GOT. Each variable corresponds to a four-byte address. The linker will look up the address of each variable when loading the module, and then fill in each item in the GOT to ensure that the address pointed to by each pointer is correct. Because the GOT is placed in the data segment, it can be modified during loading, and each process can have an independent copy without affecting each other.

Inter-module calls and conversions

For inter-module calls and jumps, GOT can also be used to solve the problem, and the corresponding item in GOT stores the address of the target function.

summary

The instruction jump and call inside the module use relative jump and call, and the data access inside the module uses relative address access.

The instruction jump and call outside the module use indirect jump and call (GOT), and the data access outside the module uses indirect access (GOT).

-fpic and -fPIC

Using GCC to generate address-independent code only needs to use the "-fPIC" parameter, and GCC also provides another parameter "-fpic". These two parameters are exactly the same in terms of function, and they both let GCC generate address-independent code. The only difference is that "-fpic" produces relatively small code, and "-fPIC" produces relatively large code. But "-fpic" has some restrictions on some hardware platforms, such as the number of global symbols or the length of code, etc. "-fPIC" has no such restrictions.

To distinguish whether a so is pic, you can use the readelf command to check whether there is a TEXTREL segment in the file. The so of PIC does not know the code segment relocation table, and TEXTREL indicates the address of the code segment relocation table.

Global variable problem in shared module

When a module refers to a global variable defined in a shared object, the compiler cannot determine whether the global variable is defined in another object file in the same module or in another shared object when compiling the module, that is, it cannot judge Is it a cross-module call.

When the module is a part of the executable file, the code of the main module of the program is not an address-independent code and does not use the PIC mechanism, so this global variable uses the normal data access method. Executable files do not perform code relocation at runtime, so the addresses of variables must be determined during linking. In order for the linking process to be normal, the linker will create a copy of the global variable in the .bss section when creating the executable file. So it will cause variables to exist in multiple locations at the same time.

When the ELF shared library is compiled, the global variables defined in the module are regarded as the global variables defined in other modules by default, and the variables are accessed through the GOT. When the shared module is loaded, if a global variable has a copy in the executable file, then the dynamic linker will point the corresponding address in the GOT to the copy, so that the variable will have only one instance at runtime. If the global variable does not have a copy in the main module of the program, the corresponding address in the GOT will point to the copy of the variable inside the module.

If a global variable G is defined in a shared object lib.so, and both process A and process B use lib.so, when process A changes the value of this global variable G, will G in process B be affected? ?

No, it should be because when lib.so is loaded by two processes, its data segment part has an independent copy in each process, and the global variables in the shared object actually have nothing to do with the global variables defined inside the program the difference. Any process accesses only its own copy without affecting other processes.

If it is changed to thread A and thread B in the same process, different threads in the same process access the same process address space, which is the same copy of lib.so.

So is there an opposite requirement to the above?

Yes, multi-process shared global variables are also called shared data segments, and multiple threads accessing different copies of global variables is also called thread private storage.

Lazy Binding PLT

Static linking is about 1%-5% faster than dynamic linking, mainly because dynamic linking requires complex GOT positioning for global and static data access, and then indirect addressing. For calls between modules, the GOT must be located first, and then an indirect jump is performed, so the running speed of the program is slowed down.

Under dynamic linking, a large number of function references are included between program modules. Before the program starts to execute, dynamic linking will consume a lot of time to resolve symbol search and relocation of function references between modules. But during the running of a program, many functions may not be used after the program is executed. So ELF adopts a method called delayed binding. The basic idea is to bind (symbol search, relocation) when the function is used for the first time , and not to bind if it is not used.

Dynamic link related structures

To load an executable file, the operating system first reads the header of the executable file, checks the legality of the file, and then reads the virtual address, file address, and attributes of each segment from the Program Header in the header, and converts them to Mapped to the corresponding location in the process's virtual address space. In the case of static linking, the operating system can then transfer control to the entry address of the executable file, and the program begins execution. In the case of dynamic linking, the operating system cannot hand over control to the executable file after loading the executable file, because the executable file still has many shared objects. Many references to external symbols in executable files are still at invalid addresses, so the operating system will first start a dynamic linker.

Under Linux, the dynamic linker ld.so is actually a shared object, and the operating system also loads it into the address space of the process through mapping. After the operating system loads the dynamic linker, it hands over control to the entry address of the dynamic linker. When the dynamic linker takes control, it starts to perform a series of its own initialization operations, and then starts to dynamically link the executable file according to the current environment parameters. When all dynamic link work is completed, the dynamic linker will transfer control to the entry address of the executable file, and the program will start to execute.

.interp section

The interp section is the abbreviation of the interpreter. The content of .interp is very simple, and what is stored in it is a string, which is the path of the dynamic linker required by the executable file. Under the Linux system, the path of the dynamic linker required by all executable files is almost "/lib/ld-linux.so.2". When the file is loaded, go back and find the corresponding dynamic linker required to load the executable file.

.dynamic section

The .dynamic section saves the basic information needed by the dynamic linker, such as which shared exclusives depend on, the location of the dynamic link symbol table, the location of the dynamic link symbol table, the location of the dynamic link relocation table, and the address of the shared object initialization code .

The information saved in the .dynamic section is a bit like the ELF file header, except that the ELF file header stores the relevant content during static linking, that is, the symbol table and relocation table during static linking. .dynamic is the corresponding information used by the saved dynamic link, which can be regarded as the file header of the ELF file under the dynamic link.

dynamic symbol table

In the static link, there is a special section called the symbol table . symtab, which stores all the definitions and references of the symbols of the object file. In dynamic linking, in order to represent the symbol import and export relationship between these dynamic link modules, ELF has a special section called Dynamic Symbol Table (Dynamic Symbol Table) to store this information. The name of this section is usually called .dynsym. The difference from ".symtab" is that ".dynsym" only saves the symbols related to the dynamic link, and does not save the symbols inside the module. Most of the time, dynamically linked modules have both ".dynsym" and ".symtab" tables. ".symtab" saves all symbols, including ".dynsym" symbols.

The dynamic symbol table also needs some symbol tables, such as the string table to save the symbol name . It is called the symbol string table ".strtab" during static linking, and the dynamic symbol string table ".dynstr" in the dynamic symbol table . Since symbols need to be searched when the program is running, in order to speed up the symbol search process, there is often an auxiliary symbol hash table ".hash".

Dynamic Link Relocation Table

Under dynamic linking, whether it is an executable file or a shared object, once there is a dependent object, there will be imported symbols, and there will be references to the imported symbols in the code or data. The address of these imported symbols is unknown at compile time, and in static linking, these unknown address references are fixed at final link time. In dynamic linking, the addresses of imported symbols are determined at runtime, so the references to these imported symbols need to be corrected at runtime, that is, relocation is required.

Regardless of whether the shared object is compiled using PIC, it needs to be relocated when loading. For shared objects compiled using PIC, the code segment does not need to be relocated, but the data segment also contains references to absolute addresses, because the absolute address-related part of the code segment is separated and becomes GOT, and GOT is actually data part of the segment. In addition to the GOT, data segments may also contain absolute address references.

The relocation of the object file in the static link is done at the time of the static link, and the relocation of the shared object is done at the time of loading. In static linking, the object file contains a relocation table specially used to represent relocation information, such as ".rel.text" means the relocation table of the code segment, and ".rel.data" means the relocation table of the data segment Relocation table.

In the dynamically linked files, there are also similar relocation tables ".rel.dyn" and ".rel,plt", which are equivalent to ".rel.text" and ".rel.data" respectively. ".rel.dyn" is the correction to the data reference, and the correction location is located in ".got" and the data segment, while ".rel.plt" is the correction to the function, and the correction location is located in ".got.plt".

Steps for dynamic linking

Dynamic linking is divided into three steps: first start the dynamic linker, then load all required shared objects, and finally relocate and initialize.

Dynamic linker bootstrapping: For ordinary shared objects, the relocation work is done by the dynamic linker, and other dependent shared objects are also linked and loaded by the dynamic linker. But for the dynamic linker itself, it cannot rely on any shared objects, and the relocation of the required global variables and static variables is done by itself. When the dynamic linker starts, there is a piece of code that can complete the relocation without using any global variables and static variables. This limited startup code is called bootstrapping .

When the operating system passes control to the dynamic linker, the dynamic linker's bootstrap code begins execution. The bootstrap code first finds its own GOT. The first entry of the GOT saves the offset address of the .dynamic segment, so that the relocation table and symbol table of the dynamic linker itself are obtained.

mount shared object

After the dynamic linker completes the bootstrapping, the executable file and the symbol table of the linker itself are combined into one symbol table, which is called the Global Symbol Table (Global Symbol Table). The linker then starts looking for shared objects that the executable depends on, in ".dynamic". The linker can list the shared objects that an executable depends on and place these shared objects in a load collection. Then the linker takes out the name of a shared object from the loading collection in turn, and finds the corresponding file, opens the file, reads the corresponding ELF file header and ".dynamic" segment, and then maps the corresponding code segment and data segment to in the process space.

The phenomenon that a global symbol in a shared object is overwritten by a global symbol of the same name in another shared object is called Global Symbol Interpose. Linux stipulates that when a symbol needs to be added to the global symbol table, if the same symbol name already exists, the added symbol will be ignored.

relocation and initialization

When the loading is complete, the linker starts to traverse the executable file and the relocation table of each shared object, and corrects each position that needs to be relocated in all GOT/PLT. This correction process is relatively easy since the global symbol table is already in place.

After the relocation is completed, if a shared object has a ".init" section, the dynamic linker will execute the code in the ".init" section to implement the initialization process unique to the shared object.

If the executable file of the process also has a ".init" section, then the dynamic linker will not execute it, because the ".init" section and ".finit" section in the executable file are executed by the program initialization part code.

When the relocation and initialization are complete, the dynamic linker will pass control to the entry of the program and start execution.

Implementation of the dynamic linker

The path of the dynamic linker is /lib/ld-linux.so.2, but this is actually a soft link, and the real dynamic linker is /lib/ld-xyzso. The dynamic linker is a very special shared object. It is not only a shared object but also an executable program, which can be executed directly on the command line.

explicit runtime connection

Systems that support dynamic linking often support a more flexible module loading method, called explicit runtime linking, also known as runtime loading. The loading of the dynamic library is provided by a series of APIs provided by the dynamic linker: open the dynamic library (dlopen), find the symbol (dlsym), error handling (dlerror), close the dynamic library (dlclose), the program can use these APIs for the dynamic library operate.

dlopen

It is used to open a dynamic library and load it into the address space of the process to complete the initialization process. The function prototype is: void * dlopen(const char *filename, int flag);.

The first parameter is the path of the dynamic library. If the path is an absolute path, it will be opened directly. If it is a relative path, then dlopen() will try to find the dynamic library file in a certain order.

  1. Look in a list of directories specified by the environment variable LD_LIBRARY_PATH.
  2. Find the shared library path pointed to by /etc/ld.so.cache.
  3. /lib、/usr/lib

The second parameter flag indicates the parsing method of the function symbol, and RTLD_LAZY indicates delayed binding, which is bound when the function is used for the first time. RTLD_NOW means that all function binding work is completed when the module is loaded. If there is any undefined symbol reference binding work that is not completed, dlopen() will return an error.

dlsym

Find the symbols needed in the dynamic library, the function prototype is: void * dlsym(void *handle, char * symbol).

The first parameter is the handle of the dynamic library returned by dlopen(), and the second parameter is the name of the symbol to be found, a string ending with "\0". dlsym returns the symbol's value if it finds a corresponding symbol, or NULL if it does not. If the searched symbol is a function, then the address of the function is returned; if the searched symbol is a variable, then the address of the variable is returned; if the searched symbol is a constant, then the value of the constant is returned.

If the value of the constant is exactly 0, how to judge whether the symbol is found?

It is necessary to use dlerror() to judge.

dlerror

Returns NULL if found, NULL if not found. The return value type of dlerror is char*, indicating whether the last call was successful.

dlclose

To unload a loaded module, the system will maintain a loading reference counter, and each time a module is loaded using dlopen(), the corresponding counter will be incremented by one. Each time dlclose is used to unload a module, the corresponding counter is decremented by one. Only when the counter value is reduced to 0, the module will be actually unloaded. The process of unloading is to execute the ".finit" segment code, then remove the corresponding symbol from the symbol table, cancel the mapping relationship between the process space and the module, and then close the module file.

shared library

The naming rule of the linux library for shared libraries is: libname.so.xyz. The prefix lib is used at the beginning, the name of the library is in the middle, the suffix is ​​.so, and the version number is followed at the end, where x is the major version number, y is the minor version number, and z is the release version number.

System path for shared libraries

  1. /lib: This location mainly stores the most critical and basic shared libraries of the system, such as dynamic linker, C language runtime library, math library, etc. Mainly the libraries to be used in the /bin and /sbin paths.
  2. /usr/lib: It mainly saves some key shared libraries required for non-system runtime, mainly some shared libraries to be used during development, this shared library will not be directly used by user programs or shell scripts .
  3. /usr/local/lib: Used to place some libraries that are not very related to the operating system itself, mainly the libraries of some third-party applications.

The search process for shared libraries

There is an ldconfig program in linux. The function of this program is to create, delete or update the corresponding symbolic links for each shared library under the shared library directory.

environment variable

LD_LIBRARY_PATH

In the Linux system, LD_LIBRARY_PATH is an environment variable consisting of several paths, each path is separated by a colon. By default, LD_LIBRARY_PATH is empty. If we set LD_LIBRARY_PATH for a process, when the process starts, the dynamic linker will first look for the directory specified by LD_LIBRARY_PATH when looking for the shared library

In addition to using LD_LIBRARY_PATH=/xxx to specify in linux, you can also directly run the dynamic link library to start the program, /lib/lib-linux.so.2 -library-path /home/user /bin/ls.

The dynamic linker will load or search for shared objects in the following order.

  1. The path specified by the environment variable LD_LIBRARY_PATH.
  2. The path specified by the path cache file /etc/ld.so.cache.
  3. The default shared library directory, first /usr/lib, then /lib.

LD_LIBRARY_PATH will also affect the path where GCC searches for libraries when compiling. The directories contained in it are equivalent to the "-L" parameter of GCC when linking.

LD_PRELOAD

LD_PRELOAD can specify some shared libraries or even object files that are preloaded. The files specified in LD_PRELOAD will be loaded before the dynamic linker searches for shared libraries according to fixed rules, and they will take precedence over the shared libraries in the directory specified in LD_LIBRARY_PATH. Shared libraries or object files specified in LD_PRELOAD are loaded regardless of whether the program depends on them.

The shared library specified in LE_PRELOAD or the global symbol in the object file will overwrite the global symbol with the same name loaded later.

LD_DEBUG

LD_DEBUG can turn on the debugging function of dynamic linking. When we set this variable, the dynamic linker will print out various useful information at runtime. LD_DEBUG can set files (loading process), bindings (displays the symbol binding process of dynamic link), libs (displays the search process of shared libraries), versions (displays the version dependencies of symbols), reloc (displays the relocation process), symbol (display symbol table lookup process), statistics (display various statistical information during dynamic linking process), all (display all information), help (display help information for various optional values ​​above).

Creation of shared libraries

Creating a shared library requires adding two parameters when compiling with GCC, -shared and -fPIC. -shared indicates that the output result is a shared library type. -fPIC means use the address independent code technique to output the file. Another parameter is "-WI", which can pass the specified parameters to the linker.

By default, when the linker generates an executable file, only those symbols that are referenced by other shared modules during linking will be put into the symbol table, which can reduce the size of the dynamic symbol table. Therefore, when using the dlopen function to dynamically load a module, the dynamic module needs to back-reference the symbols of the main module. Some symbols of the main module may not be placed in the dynamic symbols because they are not referenced by other shared modules during the connection. causing the backreference to fail. In order to prevent this from happening, ld provides a "-export-dynamic" parameter, which can export global symbols to the dynamic symbol table. This parameter can also be passed to the linker in GCC using "-Wl,-eport-dynamic".

clear symbol information

For the release version, the debugging information is not very useful. You can use the strip tool to clear all symbols and debugging information for calling shared libraries or executable files. The version with symbolic information removed is only half or less than half of the original.

You can also use the "-s" or "-S" parameter in ld, so that the linker does not generate symbol information when generating the output file. The difference between "-s" and "-S" is: "-S" eliminates debug symbol information, while "-s" eliminates all symbol information. You can also pass these two parameters to ld through "-Wl, -s" and "-Wl, -S" in GCC.

Installation of shared libraries

After creating a shared library, it needs to be installed on the system so that various programs can share it. The easiest way is to copy the shared library to a standard shared library directory, such as /lib, /usr/lib, etc., and then run ldconfig.

Copying files to the system library path requires root privileges, and you can use it ldconfig -n shared-libary-directoryto specify a directory without root privileges. When compiling, you also need to specify the location of the shared library. GCC provides two parameters "-L" and "-l", which are used to specify the shared library search directory and the path of the shared library respectively.

Shared library constructor and destructor

If you want the shared library to perform some initialization work during loading, GCC provides a shared library constructor. As long as the attribute is added when the function is declared, the function is designated as a __attribute__((constructor)) shared library constructor. A function with this attribute will It is executed when the shared library is loaded, that is, it is executed before the main function of the program. If dlopen is used to open a shared library, the shared library constructor is executed before the dlopen function returns.

Similarly, the destructor can be __attribute((destructor))an attribute added to the function declaration. This function will be executed after the main() function is executed or when exit() is called. If loaded at runtime, when dlclose() is used to unload the shared library, the destructor will be executed before dlclose() returns.

Guess you like

Origin blog.csdn.net/qq_41323475/article/details/127856519