content:
First, _dl_map_object_deps Function Analysis Second, Relative Transfer, Absolute Transfer III, _DL_RELOCATE_Object Function Analysis Four
Analysis of Dynamic Link of ELF Document in Linux (2): Function Analysis and Uninstall Reprinted from: IBM DeveloperWorks China Website Wang Ruichuan (Jeppeterone@163.com) Engage in Linux Development Work in December 2003
I believe that the reader has seen the loading, parsing and instance analysis of the dynamic link of the ELF file in Linux under the Intel platform (1): The content of the loaded content is understood, and the general process experienced when the ELF file is loaded. Then let's solve the problems mentioned in the last article, as well as code that is not explained in DL_Open_Worker. First, _dl_map_object_deps Function Analysis Due to the length of the source code, the originally simple code has become a trampoline thing due to efficiency, so I have changed a big change in it, not only delete all unnecessary The code, but also use pseudo code to show its original design ideas.
13 _dl_map_object_deps (struct link_map * lmap) 14 15 {16 17 struct list_head * add_list; 18 char * load_dl_name; 19 struct link_map * curlmap; 20 Elf32_Dyn * needed_dyn; 21 struct link_map * new_lmap; 22 int lmap_count = 1; 23 24 add_lmap_to_list ( lmap, add_list); 25 26 for_each_in_list (add_list, curlmap) 27 {28 for_every_DT_NEEDED_section (curlmap, needed_dyn) 29 {30 load_dl_name = get_needed_name (curlmap, needed_dyn); 31 32 new_lmap = _dl_map_object (load_dl_name); 33 34 35 add_to_list_tail_uniq (add_list, NEW_LMAP); 36} 37} 38 39 lmap_count = count_the_list (lmap); 40 41 lmap-> l_initfini = (STRUCT LINK_MAP **) Malloc (2 * lmap_count 1) * (Struct Link_Map *)); 42 43 LMAP- > l_searchlist.r_list = & lmap-> l_initfini [lmap_count 1]; 44 lmap-> l_searchlist.r_nlist = lmap_count; 45 46 47 COPY_EACH_ADD_LIST_TO_SEARCHLIST (LMAP, Add_List, LM AP_COUNT); 48 49 Free_THE_ADD_LIST (add_list); 50 51} First, in fact, the dependent dynamic link library that loads a dynamic link library is not a simple thing, because all dynamic link libraries may have its own dynamic links Library, if you use recursive simple methods not only impossible ----- Because you can see the first article, there is a lock problem in the loading dynamic link library, and it is not necessary, you It does not guarantee that such dynamic link library reliance will not form a dependency loop, just like the picture below: The simplest idea is that we don't repeat all dynamic link libraries, here A single-strand implementation ----- In the original program, it is also use this method, but the method used to allocate is directly implemented in the stack, so that the program is running, but the program readability is greatly weakened.
23 lines first add LMAP to this Struct List, in the 26 line for_each_in_list (add_list, curlmap) is actually put curlmap = curlmap-> next, and judge its curlmap! = Null, 28 line for_every_dt_need_section ( CURLMAP, Needed_Dyn is mainly needed_dyn = curlmap-> l_info [dt_needed]; but here should pay attention to, there may be no only one in a dynamic link library, just like a readelf -a example tag type name / value 0x00000001 (Needed Shared library: [libstdc - libc6.2-2.so.3] 0x00000001 (NEEDED) Shared Library: [libm.so.6] 0x00000001 (Needed) Shared library: [libc.so.6] is more exact Searching in the Dynamic Section of LMAP-> L_LD, it is such that the method of using the 30 lines of the 30 lines of the DT_Needed in D_Neededed is like this.
LOAD_DL_NAME = CURLMAP-> L_ADDR NEED_DYN-> D_UN.D_PTR CURLMAP-> L_INFO [DT_STRTAB]; it is clear that this dynamic link library map will complete its loading, and 35 lines are to expand Add_List, here only Will load once for the same dynamic link library, so there will be no previous loop load, then go back to see the cycle between 26 lines to 37 lines, if there is no duplicate dynamic link library in 35 lines. The entire loop may continue to loop. From 39 lines to 51 lines, the dependent dynamic link library COPY in this function into L_SearchList and L_initfini are ingeniously assigned together. The last temporary single-link table in front. Second, relative transfer, absolute transfer When learning assembly language, we must have a deep impression on different addressing methods. But for the same important transfer instructions in assembly language, just brought (used in Call and JXX --------- JXX here refers to the conditional transfer instructions such as JMP JAE JBE and unconditional transfer instructions ). However, if the link implementation of the dynamic link library must be mentioned. The so-called relative transfer is that this binary code can be run without modification in the relocated environment. As in the case,
719: E9 E2 Fe FF FF JMP 600
MOVL% EIP,% EAX ADDL $ 0xffffee2,% EAX MOVL% EAX,% EIP next to the 719 is the offset compared to the start address, while the E9 E2 Fe ff ff is written Retrieved 0x11e because this is the number of -0x11e represented by FF FF Fe E2 (Intel is a Little Endian representation). If you add 719 to 5, you will lose 600. This is the relative transfer of the processor. There is another way of transfer, which is absolute transfer.
2b6: ff d0 call *% EAX This If you use the simplest code to indicate addl $ 2,% Eippushl% EIPMOVL (% EAX),% EIP is obvious, that is, turn the contents of EIP into Eax, if used JMP is also the same
LJMP * (% EDX) The above two transfer methods adapt to different environmental requirements, if it is in an ELF file, the benefits of using relative transfer can be brought about: 1, you can no longer have access to memory In the execution time of the instruction, it has been greatly improved (which is currently the highest frequency of the mainstream in the PCI bus structure 133MHz, and an Intel CPU's clocked frequency can exceed it). 2, can be adapted to the memory environment that is dynamically loaded and dynamically positioned, without having to modify the original code modification (code segment cannot be modified when running), because the entire dynamic link or executable is continuous Address mapping. But it also brought a few questions: 1. This relative transfer does not have a way to transfer the function address in another dynamic link library when running (because the loading address of most dynamic link libraries is expected. However, it is theoretically random). 2, such code brings a large problem between the platform, because there is no way to know that such numbers represent an address or represent a binary number. Therefore, in the system of transplantation, there is a high-demand function of C , the development of the relative address transfer. Such as COM, CORBA system is like this. The above two disadvantages are exactly the advantage of absolute metastasis. For a contrast, absolute transfer is equivalent to the immediate addressing of memory addressing, while relative transfer is equivalent to the relative addressing of memory addressing. The actual use in a general dynamic link library uses a clever way. Please see the assembly language fragment of the next paragraph:
2F7: E8 00 00 00 00 Call 2FC
Disassembly of section .got: 000013ac <.got>: 13ac: 34 13 x $ 0x13,% Al ... This is a GOT section, its full name is Global Object Table is a global object table. It stores the address to be transferred here. What is achieved if you want to call a function outside the dynamic link library? Let's look down:
306: 8D 83 74 EF FF LEA 0xffffef74 (% EBX),% EAX 30C: 50 PUSH% EAX 30D: E8 CE FF FF FF CALL 2E0
Relocation section '.rel.plt' at offset 0x2c8 contains 1 entries: Offset Info Type Symbol's Value Symbol's Name 000013b8 00000e07 R_386_JUMP_SLOT 00000000 printf 0x13b8 value if found in the file (this is the relative offset) is the content
13b8: E6 02 Since Intel is Little Endian, this number translated is 0x02E6, what is it here?
2E0: FF A3 0C 00 00 00 JMP * 0xc (% EBX) 2E6: 68 00 00 00 Push $ 0x0 2eb: E9 E0 FF FF JMP 2D0
2D0: FF B3 04 00 00 00 Pushl 0x4 (% EBX) 2D6: FF A3 08 00 00 00 jmp * 0x8 (% EBX) Before you have said% EBX get the start address of the GOT, so this is the pressure got [ 1] In the stack, then transfer to the address included in GOT [2], you can see 2162 rows and 2167 lines in Elf_Machine_Runtime_Setup, it is the pointer of this dynamic link library itself Struct Link_Map *, with _dl_runtime_resolve The address is located. One of the following pictures can be imaged. If it is the first function call, the route it walk is that I marked with the red line in the picture, and if it is called after the second time, it is indicated by the blue line. The reason has been given in the previous code.
82 int _dl_relocate_object (struct link_map * lmap, int lazy_mode) 83 {84 elf_machine_runtime_setup (lmap, lazy_mode); 85 elf_machine_lazy_rel (lmap, lazy_mode); 86 87} Here we accomplished in two steps, the first step is to dynamically elf_machine_runtime_setup The address of the data structure LMAP represented by the link library is written in a special place in the ELF file, and ELF_MACHINE_LAZY_REL is an implementation of the function relocation of all the functionality to the dynamic link library to be called. These two steps are very important, because if there is no two steps, it is impossible to dynamically resolve the function of dynamic link library. This can be detailed in detail in the relative transfer of the above. 54 void elf_machine_runtime_setup (struct link_map * lmap, int lazy_mode) 55 {56 Elf32_Addr * got; 57 58 got = (Elf32_Addr *) lmap-> l_info [DT_PLTGOT] .d_un.d_ptr; 59 60 got [2] = & _ dl_runtime_resolve 61 got [ 1] = LMAP; 62} Obviously, the address in the written ELF file is the second item in its DT_PLTGOT section --- No. 60. The content written to the first item is the address of the processing function to be mobilized, which is in the dynamic resolution in the dynamic resolution mentioned later.
64 void elf_machine_lazy_rel (struct link_map * lmap, int lazy_mode) 65 {66 Elf32_Addr rel_addr = lmap-> l_info [DT_REL] .d_un.d_ptr; 67 int rel_num = lmap-> l_info [DT_RELSZ] .d_un.d_ptr; 68 int i; 69 ELF32_ADDR L_ADDR = LMAP-> L_ADDR; 70 71 ELF32_REL * REL; 72 for (i = 0, rel = (ELF32_REL *) REL_ADDR; I
2087 # define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ( "// 2088 .text // n // 2089 .globl _dl_runtime_resolve // n // 2090 .type _dl_runtime_resolve, @ function // n // 2091 .align 16 // n // 2092 _dl_runtime_resolve: // n // 2093 pushl% EAX // N // 2094 PUSHL% ECX // N // 2095 Pushl% EDX // N / / 2096 MOVL 16 (% ESP),% EDX // N / / 2097 MOVL 12 (% ESP),% eax // n // 2098 Call fixup // n // 2099 POPL% EDX // N // 2100 POPL% ECX // N // 2101 XCHGL% EAX, (% ESP) // n // 2102 RET $ 8 // 2103 .size _dl_runtime_resolve,.-_R_Runtime_Resolve /// n // 2104 // n // 2105 "); From here, the name ELF_MACHINE_RUNTIME_TRAMPOLINE, we can see that this function is not simple (TRAMPOLINE in English It is the meaning of the trampoline, which is the weird thing to make your brain curving), and the following code has indeed illustrated this. In front .Text is the following code is executable, .globl _dl_runtime_resolve is to indicate that this function is global, if there is no one, then we look at the got [2] = & _dl_runtime_resolve can't compile --- - The compiler may not find its definition. .type _dl_runtime_resolve, @function is a function description. . RALIGN 16 is 16-byte alignment. We know that two parameters have been pressed during the previous call function (the first is the Struct Link_Map * pointer of the dynamic link library, the other is the index value of the REL) Here first saves the previous register value, and at this time 16 (% ESP) is the first parameter, 12 (% ESP) first parameter, the reason here is the following FixUP function to transfer parameters in register. I wonder what the FIXUP specific content is, and you will see the content of the code author. First POP two registers, and XCHG% EAX, (% ESP) and the top of the stack, this has two purposes, one is to restore the value of Eax, the other is the top of the stack is the address returned by the function. The EAX returned by Fixup is the address we want to find with the address in memory. This will naturally jump to that place. But if you think this is good, it is wrong, because you don't forget that we have pressed into two parameters in the stack. So use RET $ 8, which represents a combination of POPL% EIP Add $ 0x8,% ESP in Intel's instructions. (Very exciting !!!!!!!) You can also see "Links and Links of Programs and Links for Linux" URLs http://www-900.ibm.com/developerWorks/cn/linux/ There is a picture inside l-dynlink / index.shtml that exactly this ELF_MACHINE_RUNTIME_TRAMPOLINE. Then see the content of the fixup function directly
124 Elf32_Addr fixup (struct link_map * lmap, Elf32_Word reloc_offset) 125 {126 Elf32_Sym * symtab = lmap-> l_info [DT_SYMTAB] .d_un.d_ptr; 127 char * strtab = lmap-> l_info [DT_STRTAB] .d_un.d_ptr; 128 Elf32_Rel * reloc = (Elf32_Rel *) (lmap-> l_info [DT_JMPREL] .d_un.d_ptr reloc_offset); 129 Elf32_Sym * sym = & symtab [Elf32_R_SYM (reloc-> r_info)]; 130 char * symname = sym-> st_name strtab ; 131 Elf32_Addr reloc_addr = lmap-> l_addr reloc-> r_offset; 132 133 Elf32_Addr symaddr = 0; 134 135 136 137 symaddr = do_lookup (lmap, symname); 138 139 140 141 if (symaddr> 0) 142 {143 * reloc_addr = Symaddr; 144 Return Symaddr; 145} 146 147 exit (-2); 148 149 150 151 152 153} This is given to give the repulsive Reloc_offset to resolve the name of the function from a dynamic link library, if The way shown is as follows: You may think: It can also be used in another method, that is, putting this Reloc Sym ST_VALUE directly writes the previous call redirect function. This will be faster when analyzing. But reality is likely to bring great trouble throughout the ELF file structure system. I will explain every point:
If it is the address of this Reloc Sym, it is a dynamic link library that its loading address itself is dynamically determined. If you use the ELF32_SYM's ST_VALUE address, it is possible to get this SYM's Name with LMAP-> L_i NFO [DT_STRTAB], but if some functions are only valid, visible, if considering when compiling A function defined as static in a file, it is to be partially visible, it is impossible to resolve this function, and the C function is more complicated, which requires a field to represent its properties. This is to have ST_INFO's data member variable. This will also have SYM's participation. Light has ELF32_SYM or not, because it is a little information on itself, this is the resolution of this Relocation Symbol, or in another real dynamic link library, this is mainly In a module written in several files, some of their probes have been determined when the link is, while others are not, distinguished is R_INFO in the relocation. From the above analysis, there is a lot of considerations, if only a single consideration, it is not, especially the specifications of multiple operating systems and platforms, can not consider the efficiency Yes. At 143 lines are the true parsing functions to the front to be relocated. This is not to come once when this function is called again. At this time, the judgment of this R_info is now slightly. . Real parsing is implemented in do_lookup, I still have pseudo code here:
90 Elf32_Addr do_lookup (struct link_map * lmap, char * symname) 91 {92 struct link_map * search_lmap = NULL; 93 Elf32_Sym * symtab; 94 Elf32_Sym * sym; 95 char * strtab; 96 char * find_name; 97 int symindx; 98 99 Elf32_Word hash = elf_hash_name (symname); 100 for_each_search_lmap_in_search_list (lmap, search_lmap) 101 {102 symtab = search_lmap-> l_info [DT_SYMTAB] .d_un.d_ptr; 103 strtab = search_lmap-> l_info [DT_STRTAB] .d_un.d_ptr; 104 for (symindx = search_lmap-> l_buckets [hash% search_lmap-> l_nbuckets]; 105 symindx = 0;! symindx = search_lmap-> l_chain [symindx]) 106 {107 sym = & symtab [symindx]; 108 109 find_name = strtab sym-> st_name ; 110 if (strcmp (find_name, symname) == 0) 111 return sym-> st_value search_lmap-> l_addr; 112} 113 114 return 0; 115 116 117 118} 119} 100 from the front row is in _dl_map_object_de for_each_search_lmap_in_search_list Its itself depends on the dynamic link library in the L_SearchList obtained in the PS, the method of the intermediate lookup is displayed in the picture below. What is shown above is that the Symidx offset in the Hash table is the next offset. Finally, if strcmp == 0 can be obtained, otherwise it will return a 0 indicator. Now we have completed the analysis process of the function, it is necessary to make a summary work:
In the dynamic link library that calls the function, the method used is to perform absolute transfer from the code of the PLT section, and the transferred address is stored in the GOT section. In the dynamic link library called the function (the dynamic link library implemented by the function), its function is organized in DT_HASH and DT_SYMTAB, DT_STRTAB. The organization is as follows, and ST_VALUE in the ELF32_SYM in SYMTAB indicates the offset of this derived label in the dynamic link library, and the ST_NAME is the offset in the dynamic link base STRTAB. In calling dynamic link libraries and called dynamic link libraries, ELF32_REL (ELF32_RELA in the architecture of MIPS, etc.), and its R_INFO reflects this nature of this to import mark (which is the call square), while r_offset This is the offset of this marker in the dynamic link library. (This can be seen in ELF_MACHINE_LAZY_REL) 5. The uninstallation of the dynamic link library is actually uninstalled and loaded is just a reverse process, but the original code is allocated in the stack in order to improve the efficiency, but this will make it easy to understand. It is too complicated, so I made a big revision here, here is the implementation of pseudo code.
245 void dl_close (struct link_map * lmap) 246 {247 struct link_map ** dep_lmaplist = NULL; 248 int i; 249 Elf32_Addr * fini_call_array; 250 void * fini_call; 251 struct link_map * curlmap; 252 struct list * has_removed_list = malloc (sizeof ( Struct List); 253 254 HAS_REMOVED_LIST-> LMAP = LMAP; 255 HAS_Removed_List-> Next = NULL; 256 257 IF (LMAP-> L_OpenCount> 1) 258 {259 lmap-> l_opencount--; 260 return; 261} 262 263 lmap-> l_opencount--; 264 265 266 dep_lmaplist = lmap-> l_initfini; 267 268 269 270 for (i = 0; dep_lmaplist [i] = NULL;! i ) 271 {272 273 try_dl_close (dep_lmaplist [i], has_removed_list) ; 274} 275 276 277 IF (LMAP-> L_INFO [DT_FINI_ARRAY] .d_un.d_ptr! = Null && lmap-> l_opencount == 0) 278 {279 280 Fini_Call_Array = LMAP-> L_INFO [DT_FINI_ARRAY] .d_un.d_ptr 281 LMAP-> L_ADDR; 282 unsigned int SZ = LMAP-> L_INFO [DT_FINI_ARRAYSZ] .d_un.d_ptr 283 lmap-> l_addr; 284 285 While (SZ -> 0) 286 {287 / * Call the fini function * / 288 ((void *) Fini_call_Array [SZ]) (); 289 290} 291} 292 293 if (lmap-> l_info [dt_fini ] .d_un.d_ptr! = NULL && LMAP-> L_OpenCount == 0) 294 {295 FINI_CALL = LMAP-> L_INFO [DT_FINI] .d_un.d_ptr 296 LMAP-> L_ADDR; 297 298 (Void *) Fini_Call) ( ); 299} 300 301 302 MUNMAP (LMAP-> L_MAP_START, LMAP-> L_MAP_START); 303 304 305 Free (LMAP-> L_INITFINI); 306 307 Free (LMAP-> L_Scope);
308 309 IF (LMAP-> L_phdr_allocated) 310 Free ((void *) LMAP-> L_phdr); 311 312 Free_List (HAS_REMOVED_LIST); 313 314 Free (LMAP); 315 316 317 Return; 318} The HAS_Removed_List here is recorded The dynamic link library that has been unloaded in this time DL_CLOSE operation is mainly to prevent the uninstalled dynamic link library. In fact, start judging if this is no longer relying on its dynamic link library. If there is no (minus 1, equal to 0 is), then you can continue, let go of this to join this dynamic link library, try to uninstall the dynamic link library it rely on, these are finished Then it is its own point, one is the uninstall function in its dt_fini_array, and the function in dt_fini, which is finished, it is loaded to memory content, 213 lines. Then, the memory applied for Struct Link_MAP is. You can see the code after try_dl_close to understand this possible depth recursive process.
233 void try_dl_close (struct link_map * lmap, struct list * 234 has_removed_lmap_list) 234 {235 if (in_the_list (has_removed_lmap_list, lmap)) 236 return; 237 dl_close_with_list (lmap, has_removed_lmap_list); 238 return; 239 240} 156 void dl_close_with_list (struct link_map * lmap, struct list * has_removed_lmap_list) 157 {158 struct link_map ** dep_lmaplist = NULL; 159 int i; 160 Elf32_Addr * fini_call_array; 161 void * fini_call; 162 163 164 165 166 167 if (lmap-> l_opencount> 1) 168 { 169 lmap-> l_opencount--; 170 return; 171} 172 add_to_list_tail_uniq (has_removed_lmap_list, lmap); 173 174 lmap-> l_opencount--; 175 176 177 dep_lmaplist = lmap-> l_initfini; 178 179 180 181 for (i = 0; Dep_lmaplist [i]! = null; i ) 182 {183 184 try_dl_close (dep_lmaplist [i], has_removed_lmap_list); 185} 186 187 188 IF (LMAP-> L_INFO [DT_FINI_ARRAY] .d_un.d_ptr! = null && lmap- > L_opencount == 0) 189 {190 191 fini_call_array = lmap-> l_info [DT_FINI_ARRAY] .d_un.d_ptr 192 lmap-> l_addr; 193 unsigned int sz = lmap-> l_info [DT_FINI_ARRAYSZ] .d_un.d_ptr 194 lmap- > l_addr; 195 196 While (SZ -> 0) 197 {198 / * call the fini function * / 199 (void *) Fini_call_Array [SZ]) (); 200 201} 202} 203 204 if (lmap-> L_info [dt_fini] .d_un.d_ptr! = null && lmap-> l_opencount == 0) 205 {206 FINI_CALL = LMAP-> L_INFO [DT_FINI] .d_un.d_ptr 207 lmap-> l_addr; 208 209 ((void *) FINI_CALL) ();
210} 211 212 213 MUNMAP (LMAP-> L_MAP_START, LMAP-> L_MAP_START); 214 215 216 Free (LMAP-> L_INITFINI); 217 218 Free (LMAP-> L_Scope); 219 220 IF (LMAP- > l_phdr_allocated 221 Free ((void *) LMAP-> L_phdr); 222 223 Free (LMAP); 224 225 226 Return; 227 228} Integration, DL_CLOSE This function is ultimately uninstalling the entire executable work. Then, the executable of the highest level begins with the dynamic link library that may have an intricate dependency, using a Mark_removed and DL_Close, in the constant recursive call, put all dynamic link library L_OpenCount Reduce 0. Finally release all memory space. This situation can be seen more clearly if you call DELET_MODULE in the Linux kernel. Sixth, the development of prospects and prospects dynamic link library has been quite perfect today, and it provides a good example for our learning operating system and compile language in terms of theory and practice. However, the implementation of the dynamic link library is still only in one operating system, a single machine, a programming language (if it is a C programming language, this is not satisfied, because different compilers may be Function Name Mangling --- - Function name is different in diaphragm), which is not enough for now networked information industries. Therefore, there is a binary implementation specification with this target. This is the CORBA developed by Object Model Group, and COM developed by Microsoft, I may explore these latest developments in detail later. Reference [1] Glibc-2.3.2 Sourcecode This is the source of my main code here, can be downloaded in ftp://ftp.gnu.org [2] John R.Len Kers and loaders "introduces dynamic link library technology Classic http://linker.iecc.com/ [3] hongjiu lu "Elf: from the programmer's perspect" Good ELF programming. You can see about the author at http://linux4u.jinr.ru/usoft/www/www_debian.org/documentation/elf/Elf.html
Wang Ruichuan, engaged in Linux development work, willing to discuss with like-minded people, email address is jeppeterone@163.com.