Rev 6767 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
6767 | clevermous | 1 | ; Module management, non-PE-specific code. |
2 | ; Works in conjuction with peloader.inc for PE-specific code. |
||
3 | |||
4 | ; void* dlopen(const char* filename, int mode) |
||
5 | ; Opens the module named filename and maps it in; returns a handle that can be |
||
6 | ; passed to dlsym to get symbol values from it. |
||
7 | ; |
||
8 | ; If filename starts with '/', it is treated as an absolute file name. |
||
9 | ; Otherwise, dlopen searches for filename in predefined locations: |
||
8088 | dunkaist | 10 | ; /sys/lib, /kolibrios/lib, directory of the executable file. |
6767 | clevermous | 11 | ; The current directory is *not* searched. |
12 | ; |
||
13 | ; If the same module is loaded again with dlopen(), the same |
||
14 | ; handle is returned. The loader maintains reference |
||
15 | ; counts for loaded modules, so a dynamically loaded module is |
||
16 | ; not deallocated until dlclose() has been called on it as many times |
||
17 | ; as dlopen() has succeeded on it. Any initialization functions |
||
18 | ; are called just once. |
||
19 | ; |
||
20 | ; If dlopen() fails for any reason, it returns NULL. |
||
21 | ; |
||
22 | ; mode is reserved and should be zero. |
||
23 | proc dlopen stdcall uses esi edi, file, mode |
||
24 | ; find_module_by_name and load_module do all the work. |
||
25 | ; We just need to acquire/release the mutex and adjust input/output. |
||
26 | cmp [mode], 0 |
||
27 | jnz .invalid_mode |
||
28 | mutex_lock modules_mutex |
||
29 | mov edi, [file] |
||
30 | call find_module_by_name |
||
31 | test esi, esi |
||
32 | jnz .inc_refcount |
||
33 | call load_module |
||
34 | xor edi, edi |
||
35 | test eax, eax |
||
36 | jz .unlock_return |
||
37 | ; The handle returned on success is module base address. |
||
38 | ; Unlike pointer to MODULE struct, it can be actually useful |
||
39 | ; for the caller as is. |
||
40 | mov edi, [eax+MODULE.base] |
||
41 | jmp .unlock_return |
||
42 | .inc_refcount: |
||
43 | inc [esi+MODULE.refcount] |
||
44 | mov edi, [esi+MODULE.base] |
||
45 | .unlock_return: |
||
46 | mutex_unlock modules_mutex |
||
47 | mov eax, edi |
||
48 | ret |
||
49 | .invalid_mode: |
||
50 | xor eax, eax |
||
51 | ret |
||
52 | endp |
||
53 | |||
54 | ; int dlclose(void* handle) |
||
55 | ; Decrements the reference count on the dynamically loaded module |
||
56 | ; referred to by handle. If the reference count drops to zero, |
||
57 | ; then the module is unloaded. All modules that were automatically loaded |
||
58 | ; when dlopen() was invoked on the module referred to by handle are |
||
59 | ; recursively closed in the same manner. |
||
60 | ; |
||
61 | ; A successful return from dlclose() does not guarantee that the |
||
62 | ; module has been actually removed from the caller's address space. |
||
63 | ; In addition to references resulting from explicit dlopen() calls, |
||
64 | ; a module may have been implicitly loaded (and reference counted) |
||
65 | ; because of dependencies in other shared objects. |
||
66 | ; Only when all references have been released can the module be removed |
||
67 | ; from the address space. |
||
68 | ; On success, dlclose() returns 0; on error, it returns a nonzero value. |
||
69 | proc dlclose stdcall uses esi, handle |
||
70 | ; This function uses two worker functions: |
||
71 | ; find_module_by_addr to map handle -> MODULE, |
||
72 | ; dereference_module for the main work. |
||
73 | ; Aside of calling these, we should only acquire/release the mutex. |
||
74 | mutex_lock modules_mutex |
||
75 | mov ecx, [handle] |
||
76 | call find_module_by_addr |
||
77 | test esi, esi |
||
78 | jz .invalid_handle |
||
79 | call dereference_module |
||
80 | mutex_unlock modules_mutex |
||
81 | xor eax, eax |
||
82 | ret |
||
83 | .invalid_handle: |
||
84 | mutex_unlock modules_mutex |
||
85 | xor eax, eax |
||
86 | inc eax |
||
87 | ret |
||
88 | endp |
||
89 | |||
90 | ; void* dlsym(void* handle, const char* symbol) |
||
91 | ; Obtains address of a symbol in a module. |
||
92 | ; On failure, returns NULL. |
||
93 | ; |
||
94 | ; symbol can also be a number between 0 and 0xFFFF; |
||
95 | ; it is interpreted as an ordinal of a symbol. |
||
96 | ; Low 64K of address space are blocked for the allocation, |
||
97 | ; so a valid pointer cannot be less than 0x10000. |
||
98 | ; |
||
99 | ; handle is not validated. Passing an invalid handle can result in a crash. |
||
100 | proc dlsym stdcall, handle, symbol |
||
101 | locals |
||
102 | export_base dd ? |
||
103 | export_ptr dd ? |
||
104 | export_size dd ? |
||
105 | import_module dd 0 |
||
106 | endl |
||
107 | ; Again, helper functions do all the work. |
||
108 | ; We don't need to browse list of MODULEs, |
||
109 | ; so we don't need to acquire/release the mutex. |
||
110 | ; Unless the function is forwarded or module name is required for error message, |
||
111 | ; but this should be processed by get_exported_function_*. |
||
112 | mov eax, [handle] |
||
113 | call prepare_import_from_module |
||
114 | mov ecx, [symbol] |
||
115 | cmp ecx, 0x10000 |
||
116 | jb .ordinal |
||
117 | mov edx, -1 ; no hint for lookup in name table |
||
118 | call get_exported_function_by_name |
||
119 | ret |
||
120 | .ordinal: |
||
121 | call get_exported_function_by_ordinal |
||
122 | ret |
||
123 | endp |
||
124 | |||
125 | ; Errors happen. |
||
126 | ; Some errors should be reported to the user. Some errors are normal. |
||
127 | ; After the process has been initialized, we don't know what an error |
||
128 | ; should mean - is the failed DLL absolutely required or unimportant enhancement? |
||
129 | ; So we report an error to the caller and let it decide how to handle it. |
||
130 | ; However, when the process is initializing, there is no one to report to, |
||
131 | ; so we must inform the user ourselves. |
||
132 | ; In any case, write to the debug board - it is *debug* board, after all. |
||
133 | ; |
||
134 | ; This function is called whenever an error occurs in the loader. |
||
135 | ; Except errors in malloc/realloc - they shouldn't happen anyway, |
||
136 | ; and if they happened after all, we are screwed and likely will fail anyway, |
||
137 | ; so don't bother. |
||
138 | ; Variable number of arguments: strings to be concatenated, end with NULL. |
||
139 | proc loader_say_error c uses ebx esi, first_msg, ... |
||
140 | ; 1. Concatenate all given strings to the final error message. |
||
141 | ; 1a. Calculate the total length. |
||
142 | xor ebx, ebx |
||
143 | lea edx, [first_msg] |
||
144 | .get_length: |
||
145 | mov ecx, [edx] |
||
146 | test ecx, ecx |
||
147 | jz .length_done |
||
148 | @@: |
||
149 | inc ebx |
||
150 | inc ecx |
||
151 | cmp byte [ecx-1], 0 |
||
152 | jnz @b |
||
153 | dec ebx |
||
154 | add edx, 4 |
||
155 | jmp .get_length |
||
156 | .length_done: |
||
157 | inc ebx ; terminating zero |
||
158 | ; 1b. Allocate memory. Exit if failed. |
||
159 | stdcall malloc, ebx |
||
160 | test eax, eax |
||
161 | jz .nothing |
||
162 | mov esi, eax |
||
163 | ; 1c. Copy data. |
||
164 | lea edx, [first_msg] |
||
165 | .copy_data: |
||
166 | mov ecx, [edx] |
||
167 | test ecx, ecx |
||
168 | jz .data_done |
||
169 | @@: |
||
170 | mov bl, [ecx] |
||
171 | test bl, bl |
||
172 | jz @f |
||
173 | mov [eax], bl |
||
174 | inc ecx |
||
175 | inc eax |
||
176 | jmp @b |
||
177 | @@: |
||
178 | add edx, 4 |
||
179 | jmp .copy_data |
||
180 | .data_done: |
||
181 | mov byte [eax], 0 ; terminating zero |
||
182 | ; 2. Print to the debug board. |
||
183 | mov ecx, loader_debugboard_prefix |
||
184 | call sys_msg_board_str |
||
185 | mov ecx, esi |
||
186 | call sys_msg_board_str |
||
187 | mov ecx, msg_newline |
||
188 | call sys_msg_board_str |
||
189 | ; 3. If the initialization is in process, report to the user. |
||
190 | xor eax, eax |
||
191 | cmp [process_initialized], al |
||
192 | jnz .no_report |
||
193 | ; Use @notify. Create structure for function 70.7 on the stack. |
||
194 | push eax ; to be rewritten with part of path |
||
195 | push eax ; to be rewritten with part of path |
||
196 | push eax ; reserved |
||
197 | push eax ; reserved |
||
198 | push esi ; command line |
||
199 | push eax ; flags: none |
||
200 | push 7 |
||
201 | mov eax, 70 |
||
202 | mov ebx, esp |
||
203 | mov dword [ebx+21], notify_program |
||
204 | call FS_SYSCALL_PTR |
||
205 | add esp, 28 |
||
206 | ; Ignore any errors. We can't do anything with them anyway. |
||
207 | .no_report: |
||
208 | stdcall free, esi |
||
209 | .nothing: |
||
210 | ret |
||
211 | endp |
||
212 | |||
213 | ; When the loader is initializing the process, errors can happen. |
||
214 | ; They should be reported to the user. |
||
215 | ; The main executable cannot do this, it is not initialized yet. |
||
216 | ; So we should do it ourselves. |
||
217 | ; However, after the process has been initialized, the main |
||
218 | ; |
||
219 | ; Helper function that is called whenever an error is occured. |
||
220 | |||
221 | ; For now, we don't expect many modules in one process. |
||
222 | ; So, all modules are linked into a single list, |
||
223 | ; and lookup functions simply walk the entire list. |
||
224 | ; This should be revisited if dozens of modules would be typical. |
||
225 | |||
226 | ; This structure describes one loaded PE module. |
||
227 | ; malloc'd from the default heap, |
||
228 | ; includes variable-sized module path in the end. |
||
229 | struct MODULE |
||
230 | ; All modules are linked in the global list with head at modules_list. |
||
231 | next dd ? |
||
232 | prev dd ? |
||
233 | base dd ? ; base address |
||
234 | size dd ? ; size in memory |
||
235 | refcount dd ? ; reference counter |
||
236 | timestamp dd ? ; for bound imports |
||
237 | basedelta dd ? ; base address - preferred address, for bound imports |
||
238 | num_imports dd ? ; size of imports array |
||
239 | imports dd ? |
||
240 | ; Pointer to array of pointers to MODULEs containing imported functions. |
||
241 | ; Used to unload all dependencies when the module is unloaded. |
||
242 | ; Contains all modules referenced by import table; |
||
243 | ; if the module forwards some export to another module, |
||
244 | ; then forward target is added to this array when forward source is requested. |
||
245 | filename dd ? ; pointer inside path array after dirname |
||
246 | filenamelen dd ? ; strlen(filename) + 1 |
||
247 | path rb 0 |
||
248 | ends |
||
249 | |||
250 | ; Fills some fields in a new MODULE struct based on given PE image. |
||
251 | ; Assumes that MODULE.path has been filled during the allocation, |
||
252 | ; does not insert the structure in the common list, fills everything else. |
||
253 | ; in: eax -> MODULE |
||
254 | ; in: esi = module base |
||
255 | proc init_module_struct |
||
256 | ; Straightforward initialization of all non-PE-specific fields. |
||
257 | lea edx, [eax+MODULE.path] |
||
258 | mov [eax+MODULE.filename], edx |
||
259 | @@: |
||
260 | inc edx |
||
261 | cmp byte [edx-1], 0 |
||
262 | jz @f |
||
263 | cmp byte [edx-1], '/' |
||
264 | jnz @b |
||
265 | mov [eax+MODULE.filename], edx |
||
266 | jmp @b |
||
267 | @@: |
||
268 | sub edx, [eax+MODULE.filename] |
||
269 | mov [eax+MODULE.filenamelen], edx |
||
270 | xor edx, edx |
||
271 | mov [eax+MODULE.base], esi |
||
272 | mov [eax+MODULE.refcount], 1 |
||
273 | mov [eax+MODULE.num_imports], edx |
||
274 | mov [eax+MODULE.imports], edx |
||
275 | ; Let the PE-specific part do its job. |
||
276 | init_module_struct_pe_specific |
||
277 | endp |
||
278 | |||
279 | ; Helper function for dlclose and resolving forwarded exports from dlsym. |
||
280 | ; in: ecx = module base address |
||
281 | ; out: esi -> MODULE or esi = NULL |
||
282 | ; modules_mutex should be locked |
||
283 | proc find_module_by_addr |
||
284 | ; Simple linear lookup in the list. |
||
285 | mov esi, [modules_list + MODULE.next] |
||
286 | .scan: |
||
287 | cmp esi, modules_list |
||
288 | jz .notfound |
||
289 | cmp ecx, [esi+MODULE.base] |
||
290 | jz .found |
||
291 | mov esi, [esi+MODULE.next] |
||
292 | jmp .scan |
||
293 | .notfound: |
||
294 | xor esi, esi |
||
295 | .found: |
||
296 | ret |
||
297 | endp |
||
298 | |||
299 | ; Helper function for whenever we have a module name |
||
300 | ; and want to check whether it is already loaded. |
||
301 | ; in: edi -> name with or without a path |
||
302 | ; out: esi -> MODULE or esi = NULL |
||
303 | ; modules_mutex should be locked |
||
304 | proc find_module_by_name uses edi |
||
305 | ; 1. Skip the path, if it is present. |
||
306 | ; eax = current pointer, |
||
307 | ; edi is updated whenever the previous character is '/' |
||
308 | mov eax, edi |
||
309 | .find_basename: |
||
310 | cmp byte [eax], 0 |
||
311 | jz .found_basename |
||
312 | inc eax |
||
313 | cmp byte [eax-1], '/' |
||
314 | jnz .find_basename |
||
315 | mov edi, eax |
||
316 | jmp .find_basename |
||
317 | .found_basename: |
||
318 | ; 2. Simple linear lookup in the list. |
||
319 | mov eax, [modules_list + MODULE.next] |
||
320 | .scan: |
||
321 | cmp eax, modules_list |
||
322 | jz .notfound |
||
323 | ; For every module, compare base names ignoring paths. |
||
324 | push edi |
||
325 | mov esi, [eax+MODULE.filename] |
||
326 | mov ecx, [eax+MODULE.filenamelen] |
||
327 | repz cmpsb |
||
328 | pop edi |
||
329 | jz .found |
||
330 | mov eax, [eax+MODULE.next] |
||
331 | jmp .scan |
||
332 | .found: |
||
333 | mov esi, eax |
||
334 | ret |
||
335 | .notfound: |
||
336 | xor esi, esi |
||
337 | ret |
||
338 | endp |
||
339 | |||
340 | ; Called when some module is implicitly loaded by another module, |
||
341 | ; either due to a record in import table, |
||
342 | ; or because some exported function forwards to another module. |
||
343 | ; Checks whether the target module has already been referenced |
||
344 | ; by the source module. The first reference is passed down |
||
345 | ; to load_module increasing refcount of the target and possibly |
||
346 | ; loading it if not yet, subsequent references just return |
||
347 | ; without modifying refcount. |
||
348 | ; We don't actually need to deduplicate DLLs from import table |
||
349 | ; as long as we decrement refcount on unload the same number of times |
||
350 | ; that we have incremented it on load. |
||
351 | ; However, we need to keep track of references to forward targets, |
||
352 | ; and we don't want to scan the entire export table and load all forward |
||
353 | ; targets just in case some of those would be useful, |
||
354 | ; so load them on-demand first time and ignore subsequential references. |
||
355 | ; To be consistent, do the same for import table too. |
||
356 | ; |
||
357 | ; in: esi -> source MODULE struct |
||
358 | ; in: edi -> target module name |
||
359 | ; out: eax -> imported MODULE, 0 on error |
||
360 | ; modules_mutex should be locked |
||
361 | proc load_imported_module uses edi |
||
362 | ; 1. Find the target module in the loaded modules list. |
||
363 | ; If not found, go to 5. |
||
364 | push esi |
||
365 | call find_module_by_name |
||
366 | test esi, esi |
||
367 | mov eax, esi |
||
368 | pop esi |
||
369 | jz .load |
||
370 | ; 2. The module has been already loaded. |
||
371 | ; Now check whether it is already stored in imports array. |
||
372 | ; If yes, just return without doing anything. |
||
373 | mov edi, [esi+MODULE.imports] |
||
374 | mov ecx, [esi+MODULE.num_imports] |
||
375 | test ecx, ecx |
||
376 | jz .newref |
||
377 | repnz scasd |
||
378 | jz .nothing |
||
379 | .newref: |
||
380 | ; The module is loaded, but not by us. |
||
381 | ; 3. Increment the reference counter of the target. |
||
382 | inc [eax+MODULE.refcount] |
||
383 | .add_to_imports: |
||
384 | ; 4. Add the new pointer to the imports array. |
||
385 | ; 4a. Check whether there is place in the array. |
||
386 | ; If so, go to 4c. |
||
387 | ; We don't want to reallocate too often, since reallocation |
||
388 | ; may involve copying our data to a new place. |
||
389 | ; We always reserve space that is a power of two; in this way, |
||
390 | ; the wasted space is never greater than the used space, |
||
391 | ; and total time of copying the data is O(number of modules). |
||
392 | ; The last fact is not really important right now, |
||
393 | ; since the current implementation of step 2 makes everything |
||
394 | ; quadratic and the number of modules is very small anyway, |
||
395 | ; but since this enhancement costs only a few instructions, why not? |
||
396 | mov edi, eax |
||
397 | ; X is a power of two or zero if and only if (X and (X - 1)) is zero |
||
398 | mov ecx, [esi+MODULE.num_imports] |
||
399 | lea edx, [ecx-1] |
||
400 | test ecx, edx |
||
401 | jnz .has_space |
||
402 | ; 4b. Reallocate the imports array: |
||
403 | ; if the current size is zero, allocate 1 item, |
||
404 | ; otherwise double number of items. |
||
405 | ; Item size is 4 bytes. |
||
406 | lea ecx, [ecx*8] |
||
407 | test ecx, ecx |
||
408 | jnz @f |
||
409 | mov ecx, 4 |
||
410 | @@: |
||
411 | stdcall realloc, [esi+MODULE.imports], ecx |
||
412 | test eax, eax |
||
413 | jz .realloc_failed |
||
414 | mov [esi+MODULE.imports], eax |
||
415 | mov ecx, [esi+MODULE.num_imports] |
||
416 | .has_space: |
||
417 | ; 4c. Append pointer to the target MODULE to imports array. |
||
418 | mov eax, [esi+MODULE.imports] |
||
419 | mov [eax+ecx*4], edi |
||
420 | inc [esi+MODULE.num_imports] |
||
421 | mov eax, edi |
||
422 | .nothing: |
||
423 | ret |
||
424 | .load: |
||
425 | ; 5. This is a totally new module. Load it. |
||
426 | call load_module |
||
427 | ; On error, return it to the caller. On success, go to 4. |
||
428 | test eax, eax |
||
429 | jz .nothing |
||
430 | jmp .add_to_imports |
||
431 | .realloc_failed: |
||
432 | ; Out of memory for a couple of dwords? Should not happen. |
||
433 | ; Dereference the target referenced by step 3 or 5 |
||
434 | ; and return error to the caller. |
||
435 | push esi |
||
436 | mov esi, edi |
||
437 | call dereference_module |
||
438 | pop esi |
||
439 | xor eax, eax |
||
440 | ret |
||
441 | endp |
||
442 | |||
443 | ; Helper procedure for load_module. |
||
444 | ; Allocates MODULE structure for (given path) + (module name), |
||
445 | ; calls the kernel to map it, |
||
446 | ; on success, fills the MODULE structure. |
||
447 | ; in: edi -> module name |
||
448 | ; in: ebx = strlen(filename) + 1 |
||
449 | proc try_map_module uses ebx esi, path_ptr, path_len |
||
450 | ; 1. Allocate MODULE structure. |
||
451 | mov eax, [path_len] |
||
452 | lea eax, [eax+ebx+MODULE.path] |
||
453 | stdcall malloc, eax |
||
454 | test eax, eax |
||
455 | jz .nothing |
||
456 | ; 2. Create the full name of module in MODULE structure: |
||
457 | ; concatenate module path, if given, and module name. |
||
458 | mov ecx, [path_len] |
||
459 | mov esi, [path_ptr] |
||
460 | push edi |
||
461 | lea edi, [eax+MODULE.path] |
||
462 | rep movsb |
||
463 | mov ecx, ebx |
||
464 | mov esi, [esp] |
||
465 | rep movsb |
||
466 | pop edi |
||
467 | mov esi, eax |
||
468 | ; 3. Call the kernel to map the module. |
||
469 | lea ecx, [eax+MODULE.path] |
||
470 | mov eax, 68 |
||
471 | mov ebx, 28 |
||
472 | call FS_SYSCALL_PTR |
||
473 | cmp eax, -0x1000 |
||
474 | ja .failed |
||
475 | ; 4. On success, fill the rest of MODULE structure and return it. |
||
476 | xchg eax, esi |
||
477 | call init_module_struct |
||
478 | ret |
||
479 | .failed: |
||
480 | ; On failure, undo allocation at step 1 and return zero. |
||
481 | stdcall free, esi |
||
482 | xor eax, eax |
||
483 | .nothing: |
||
484 | ret |
||
485 | endp |
||
486 | |||
487 | ; Worker procedure for loading a new module. |
||
488 | ; Does not check whether the module has been already loaded; |
||
489 | ; find_module_by_name should be called beforehand. |
||
490 | ; in: edi -> filename |
||
491 | ; out: eax -> MODULE or 0 |
||
492 | ; modules_mutex should be locked |
||
493 | proc load_module uses ebx esi ebp |
||
494 | ; 1. Map the module. |
||
495 | ; 1a. Prepare for try_map_module: calculate length of the name. |
||
496 | mov ebx, edi |
||
497 | @@: |
||
498 | inc ebx |
||
499 | cmp byte [ebx-1], 0 |
||
500 | jnz @b |
||
501 | sub ebx, edi |
||
502 | ; 1b. Check whether the given path is absolute. |
||
503 | ; If so, proceed to 1c. If not, go to 1d. |
||
504 | cmp byte [edi], '/' |
||
505 | jnz .relative |
||
506 | ; 1c. The given path is absolute. Use it as is. Don't try any other paths. |
||
507 | stdcall try_map_module, 0, 0 |
||
508 | test eax, eax |
||
509 | jnz .loaded_ok |
||
510 | ccall loader_say_error, msg_cannot_open, edi, 0 |
||
511 | jmp .load_failed |
||
512 | .relative: |
||
513 | ; 1d. The given path is relative. |
||
8088 | dunkaist | 514 | ; Try /sys/lib/, /kolibrios/lib/ and path to executable |
6767 | clevermous | 515 | ; in this order. |
516 | stdcall try_map_module, module_path1, module_path1.size |
||
517 | test eax, eax |
||
518 | jnz .loaded_ok |
||
519 | stdcall try_map_module, module_path2, module_path2.size |
||
520 | test eax, eax |
||
521 | jnz .loaded_ok |
||
522 | ; Note: we assume that the executable is always the first module in the list. |
||
523 | mov eax, [modules_list + MODULE.next] |
||
524 | mov ecx, [eax+MODULE.filename] |
||
525 | add eax, MODULE.path |
||
526 | mov esi, eax |
||
527 | sub ecx, eax |
||
528 | stdcall try_map_module, eax, ecx |
||
529 | test eax, eax |
||
530 | jnz .loaded_ok |
||
531 | mov ebx, dword [esi+MODULE.filename-MODULE.path] |
||
532 | movzx eax, byte [ebx] |
||
533 | mov byte [ebx], 0 |
||
534 | push eax |
||
535 | ccall loader_say_error, msg_cannot_open, edi, msg_paths_begin, esi, 0 |
||
536 | pop eax |
||
537 | mov byte [ebx], al |
||
538 | .load_failed: |
||
539 | xor eax, eax |
||
540 | ret |
||
541 | .loaded_ok: |
||
542 | ; Module has been mapped. |
||
543 | ; MODULE structure has been initialized, but not yet inserted in the common list. |
||
544 | ; 2. Insert the MODULE structure in the end of the common list. |
||
545 | mov esi, eax |
||
546 | mov eax, [modules_list+MODULE.prev] |
||
547 | mov [eax+MODULE.next], esi |
||
548 | mov [esi+MODULE.prev], eax |
||
549 | mov [modules_list+MODULE.prev], esi |
||
550 | mov [esi+MODULE.next], modules_list |
||
551 | ; 3. Call PE-specific code to initialize the mapped module. |
||
552 | push esi |
||
553 | push edi ; for messages in fixup_pe_relocations |
||
554 | mov esi, [esi+MODULE.base] |
||
555 | call fixup_pe_relocations |
||
556 | pop ecx |
||
557 | pop esi |
||
558 | jc .fail_unload |
||
559 | call resolve_pe_imports |
||
560 | test eax, eax |
||
561 | jnz .fail_unload |
||
562 | mov eax, esi |
||
563 | ret |
||
564 | .fail_unload: |
||
565 | call dereference_module |
||
566 | xor eax, eax |
||
567 | ret |
||
568 | endp |
||
569 | |||
570 | ; Worker procedure for unloading a module. |
||
571 | ; Drops one reference to the module; if it was the last one, |
||
572 | ; unloads the module and all referenced modules recursively. |
||
573 | ; in: esi -> MODULE struct |
||
574 | ; modules_mutex should be locked |
||
575 | proc dereference_module |
||
576 | ; 1. Decrement reference counter. |
||
577 | ; If the decremented value is nonzero, exit. |
||
578 | dec [esi+MODULE.refcount] |
||
579 | jnz .nothing |
||
580 | ; 2. Remove the module from the common list. |
||
581 | mov eax, [esi+MODULE.prev] |
||
582 | mov edx, [esi+MODULE.next] |
||
583 | mov [eax+MODULE.next], edx |
||
584 | mov [edx+MODULE.prev], eax |
||
585 | ; 3. Recursively unload dependencies. |
||
586 | cmp [esi+MODULE.num_imports], 0 |
||
587 | jz .import_deref_done |
||
588 | .import_deref_loop: |
||
589 | mov eax, [esi+MODULE.num_imports] |
||
590 | push esi |
||
591 | mov esi, [esi+MODULE.imports] |
||
592 | mov esi, [esi+(eax-1)*4] |
||
593 | call dereference_module |
||
594 | pop esi |
||
595 | dec [esi+MODULE.num_imports] |
||
596 | jnz .import_deref_loop |
||
597 | .import_deref_done: |
||
598 | stdcall free, [esi+MODULE.imports] ; free(NULL) is ok |
||
599 | ; 4. Unmap the module. |
||
600 | push ebx |
||
601 | mov eax, 68 |
||
602 | mov ebx, 29 |
||
603 | mov ecx, [esi+MODULE.base] |
||
604 | call FS_SYSCALL_PTR |
||
605 | pop ebx |
||
606 | ; 5. Free the MODULE struct. |
||
607 | stdcall free, esi |
||
608 | .nothing: |
||
609 | ret |
||
610 | endp |