Dynamic link library
What is dynamic link library
The following is my own understanding, which may not be correct, but it should be roughly the same
Long ago, without the concept of library, every time I wrote code, I was building wheels from scratch
Later, people found that some functions are common to most programs and have nothing to do with specific programs, such as printing a character to the console. Therefore, in order to reduce the cost of writing code, people gather these codes that realize common functions, and use methods such as include or import to refer to them every time they need to be used.
Later, the amount of such code is increasing. This leads to two problems: 1. The consumption of each compilation is increasing; 2. In fact, not all the code is used, and the extra part becomes a waste. This part of the code has nothing to do with the specific program, but every new program has to compile them. Even if you only use a simple printing function, you should include all the code. What about including only part of the code? There is still no fundamental solution to the problem. What people hope most is that this part of the code can be directly integrated into a new program without compiling.
Therefore, in order to solve this problem, people invented static link library.
First, compile all the code into object files, compress these object files into a file, and give these files a directory. Next, when compiling, you can get these compiled parts on demand and embed them into the program. For example, to use a function foo, when linking, find the location of the foo in the static link library directory, and then plug it into the program.
This not only solves the problem of repeated compilation, but also solves the problem of too large scale of executable program.
But there are still shortcomings in this way: some code is executed so frequently that almost all programs have such a code embedded in it. As a result, there are hundreds of copies of the same code in memory - which could have been used for more useful things, such as running more programs. The second and more difficult part is that if the original static link library has a bug, all programs containing this part of the code must be recompiled - as for which programs contain this code and others do not, programmers and gods know before writing, and only gods know after writing.
Therefore, in order to make up for this deficiency, dynamic link library appears.
The so-called dynamic link library means that it is dynamically linked - you can link one copy if you want to use it, and you can't link it if you don't use it.
All such reusable code is placed in a dynamic link library and occupies a part of memory. When a program needs to use some of its functions, go there to find the method you want. In this way, there is only one backup of this kind of code in memory, and when you need to update a bug, you only need to simply update the version of the dynamic link library.
Is dynamic link library better than static link library? not always.
The dynamic link library is linked only when it is running. When the program uses this part of things, it must stop and link it (that is, find out where this part of the code is placed) before it can go on.
In addition, the system must also run a dynamic linker to help our program find the code it needs.
However, with the rapid development of hardware, this loss has long been insignificant.
GOT,PLT
Reference blog: https://blog.csdn.net/u011987514/article/details/67716639?utm_medium=distribute.pc_relevant_t0.none -task-blog-BlogCommendFromBaidu-1. control&depth_ 1-utm_ source=distribute. pc_ relevant_ t0. none-task-blog-BlogCommendFromBaidu-1. control
Since the program needs to use the dynamic link library, you must know which parts of the dynamic link library you need to use, so as to accurately link the corresponding parts to its address space when loading. These address related parts are placed in the data segment, which is called GOT (Gloabl Offset Table). When the code needs to refer to the global variable, it can directly refer to the corresponding item in GOT for indirect reference.
This part of space is often dynamically allocated to avoid conflict with the space solidified by the original executable at compile time. In this way, the reference problem of absolute address is solved.
You can use objdump -h and objdump -R to view the position of GOT and the offset of data items in GOT
Because the data end is in the data segment, and this part can be modified during loading, each process can have an independent copy of this part of the code - both an independent data part and a common instruction part
For the jump and call between modules, the address of the objective function is saved in GOT. When this function needs to be executed, jump to the corresponding address in GOT.
As mentioned earlier, dynamic linking is a way to gain flexibility at the expense of some performance. Although the performance of this part is insignificant, there are still ways to improve it.
Under dynamic linking, there will be performance loss because there are a large number of function references in the program, so it will take a lot of time to link before execution and during loading.
Most functions have little chance to use. Therefore, ELF adopts a technology called delayed binding (PLT).
The so-called delayed binding means that you don't need to bind it in a hurry and bind it when you need it. Just like I usually catch up with my homework, the school committee doesn't urge me to do it until I urge it.
The implementation method of this delayed binding is:
- When using the function of external module, it does not jump directly through GOT, but through PLT
- Each external function has a specific item in the PLT
- At the beginning, the address item corresponding to GOT is not the corresponding address
- In order to find the location of the corresponding function, first, you need to know the corresponding module ID, and then you need to know the corresponding ID of this function
- When the information is sufficient, call a function for parsing. After parsing, fill in the address of the corresponding part of GOT
- The next time you use it, jump directly to the corresponding function position
For example, parse an executable file, which contains these things:
- GOT
23 .got 00000088 0000000000003f78 0000000000003f78 00002f78 2**3 CONTENTS, ALLOC, LOAD, DATA
DYNAMIC RELOCATION RECORDS OFFSET TYPE VALUE 0000000000003d78 R_X86_64_RELATIVE *ABS*+0x0000000000001240 0000000000003d80 R_X86_64_RELATIVE *ABS*+0x0000000000001200 0000000000004008 R_X86_64_RELATIVE *ABS*+0x0000000000004008 0000000000003fd8 R_X86_64_GLOB_DAT _ITM_deregisterTMCloneTable 0000000000003fe0 R_X86_64_GLOB_DAT __libc_start_main@GLIBC_2.2.5 0000000000003fe8 R_X86_64_GLOB_DAT __gmon_start__ 0000000000003ff0 R_X86_64_GLOB_DAT _ITM_registerTMCloneTable 0000000000003ff8 R_X86_64_GLOB_DAT __cxa_finalize@GLIBC_2.2.5 0000000000003f90 R_X86_64_JUMP_SLOT free@GLIBC_2.2.5 0000000000003f98 R_X86_64_JUMP_SLOT open_memstream@GLIBC_2.2.5 0000000000003fa0 R_X86_64_JUMP_SLOT fclose@GLIBC_2.2.5 0000000000003fa8 R_X86_64_JUMP_SLOT __stack_chk_fail@GLIBC_2.4 0000000000003fb0 R_X86_64_JUMP_SLOT printf@GLIBC_2.2.5 0000000000003fb8 R_X86_64_JUMP_SLOT fputs@GLIBC_2.2.5 0000000000003fc0 R_X86_64_JUMP_SLOT fflush@GLIBC_2.2.5 0000000000003fc8 R_X86_64_JUMP_SLOT perror@GLIBC_2.2.5 0000000000003fd0 R_X86_64_JUMP_SLOT exit@GLIBC_2.2.5
- .plt.got
...... 00000000000010e0 <open_memstream@plt>: 10e0: f3 0f 1e fa endbr64 10e4: f2 ff 25 ad 2e 00 00 bnd jmpq *0x2ead(%rip) # 3f98 <open_memstream@GLIBC_2.2.5> 10eb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) ......
- .plt
Disassembly of section .plt: 0000000000001020 <.plt>: 1020: ff 35 5a 2f 00 00 pushq 0x2f5a(%rip) # 3f80 <_GLOBAL_OFFSET_TABLE_+0x8> 1026: f2 ff 25 5b 2f 00 00 bnd jmpq *0x2f5b(%rip) # 3f88 <_GLOBAL_OFFSET_TABLE_+0x10> 102d: 0f 1f 00 nopl (%rax) 1030: f3 0f 1e fa endbr64 1034: 68 00 00 00 00 pushq $0x0 1039: f2 e9 e1 ff ff ff bnd jmpq 1020 <.plt> 103f: 90 nop 1040: f3 0f 1e fa endbr64 1044: 68 01 00 00 00 pushq $0x1 1049: f2 e9 d1 ff ff ff bnd jmpq 1020 <.plt> 104f: 90 nop 1050: f3 0f 1e fa endbr64 1054: 68 02 00 00 00 pushq $0x2 1059: f2 e9 c1 ff ff ff bnd jmpq 1020 <.plt> 105f: 90 nop 1060: f3 0f 1e fa endbr64 1064: 68 03 00 00 00 pushq $0x3 1069: f2 e9 b1 ff ff ff bnd jmpq 1020 <.plt> 106f: 90 nop 1070: f3 0f 1e fa endbr64 1074: 68 04 00 00 00 pushq $0x4 1079: f2 e9 a1 ff ff ff bnd jmpq 1020 <.plt> 107f: 90 nop 1080: f3 0f 1e fa endbr64 1084: 68 05 00 00 00 pushq $0x5 1089: f2 e9 91 ff ff ff bnd jmpq 1020 <.plt> 108f: 90 nop 1090: f3 0f 1e fa endbr64 1094: 68 06 00 00 00 pushq $0x6 1099: f2 e9 81 ff ff ff bnd jmpq 1020 <.plt> 109f: 90 nop 10a0: f3 0f 1e fa endbr64 10a4: 68 07 00 00 00 pushq $0x7 10a9: f2 e9 71 ff ff ff bnd jmpq 1020 <.plt> 10af: 90 nop 10b0: f3 0f 1e fa endbr64 10b4: 68 08 00 00 00 pushq $0x8 10b9: f2 e9 61 ff ff ff bnd jmpq 1020 <.plt> 10bf: 90 nop
To call the function open_ Take the memstream function as an example:
callq 10e0 <open_memstream@plt>
First call the function at 10e0:
00000000000010e0 <open_memstream@plt>: 10e0: f3 0f 1e fa endbr64 10e4: f2 ff 25 ad 2e 00 00 bnd jmpq *0x2ead(%rip) # 3f98 <open_memstream@GLIBC_2.2.5> 10eb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
It indirectly jumps to the address of 3f98 records:
0000000000003f98 R_X86_64_JUMP_SLOT open_memstream@GLIBC_2.2.5
At the beginning, the address filled in here is an address, such as 1050:
1050: f3 0f 1e fa endbr64 1054: 68 02 00 00 00 pushq $0x2 1059: f2 e9 c1 ff ff ff bnd jmpq 1020 <.plt> 105f: 90 nop
It just took 16 bytes
It pushes func ID=2 onto the stack and jumps to 1020
0000000000001020 <.plt>: 1020: ff 35 5a 2f 00 00 pushq 0x2f5a(%rip) # 3f80 <_GLOBAL_OFFSET_TABLE_+0x8> 1026: f2 ff 25 5b 2f 00 00 bnd jmpq *0x2f5b(%rip) # 3f88 <_GLOBAL_OFFSET_TABLE_+0x10> 102d: 0f 1f 00 nopl (%rax)
Another moudule ID is pressed in here=_ GLOBAL_ OFFSET_ TABLE_+ 0x8, that is, the ID of the libc module recorded at 3f80
Then, an address is called in. GLOBAL_ OFFSET_ TABLE_+ 0x10 > is the function at 3f88, which is used to dynamically link and find the function address of external modules. It can be called lookup function
Now, two parameters are pushed into our stack, one is the ID corresponding to the function and the other is the ID corresponding to the module. In the lookup function, these two parameters will be popped up - this is also the basic method of parameter transfer for 32-bit system functions.
After the lookup function performs the linking function, it will put the corresponding jump in GOT_ Modify the slot to the address of the corresponding function in memory, and jump to the position of the function to start execution
In this way, the next time when executing, after indirectly jumping to the GOT position, the above process will not be executed, but the function will be executed directly
That is, the general calling process is as follows:
When Unbound:
.plt.got => .plt => lookup => func
After binding:
.plt.got => func
How to use dynamic link library
Only the methods under Linux are discussed
When using GCC, use this parameter: - fPIC, - shared to generate dynamic link library
-The function of fPIC is to act on the compilation stage and tell the compiler to generate location independent code
-shared is used to compile dynamic libraries
Adding the link library to / usr/lib is not enough. You need to
Call the ldconfig command
reference resources: http://linux-wiki.cn/wiki/zh-hans/%E5%8A%A8%E6%80%81%E5%BA%93(.so)
Each so file has a file name, such as libabc so. x. Y.z, where ABC is the library name_ x.y.z_ Is the version number of the file
First_ x_ Indicates compatibility_ x_ Different so files are not compatible.
Second place_ y_ The change indicates that new features may be introduced, but they are generally compatible.
Third place_ z_ The change of generally means that only the Bug has been corrected.
Not all so files follow this rule, but its application is indeed very common.
In the system, there will be some problems Symbolic link , such as [3]:
libpam.so -> libpam.so.0.83.0 libpam.so.0 -> libpam.so.0.83.0
The first one is mainly used when developing other programs using this library. For example, gcc wants to connect to PAM library and libpam directly Just so
The second one is mainly used at runtime, because the same libraries as the first version are compatible with each other, so when the program runs, just try to connect libpam so. 0 is enough
ldconfig can automatically generate these links.
Here is a book for reference:
https://akkadia.org/drepper/dsohowto.pdf