Subversion Repositories Kolibri OS

Rev

Rev 6767 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
6767 clevermous 1
; Processings of PE format.
2
; Works in conjunction with modules.inc for non-PE-specific code.
3
 
4
; PE-specific part of init_module_struct.
5
; Fills fields of MODULE struct from PE image.
6
macro init_module_struct_pe_specific
7
{
8
; We need a module timestamp for bound imports.
9
; In a full PE, there are two timestamps, one in the header
10
; and one in the export table; existing tools use the first one.
11
; A stripped PE header has no timestamp, so read the export table;
12
; the stripper should write the correct value there.
13
        cmp     byte [esi], 'M'
14
        jz      .parse_mz
15
        cmp     [esi+STRIPPED_PE_HEADER.NumberOfRvaAndSizes], SPE_DIRECTORY_EXPORT
16
        jbe     @f
17
        mov     edx, [esi+sizeof.STRIPPED_PE_HEADER+SPE_DIRECTORY_EXPORT*sizeof.IMAGE_DATA_DIRECTORY+IMAGE_DATA_DIRECTORY.VirtualAddress]
6810 clevermous 18
        mov     edx, [esi+edx+IMAGE_EXPORT_DIRECTORY.TimeDateStamp]
6767 clevermous 19
@@:
20
        mov     [eax+MODULE.timestamp], edx
21
        mov     edx, esi
22
        sub     edx, [esi+STRIPPED_PE_HEADER.ImageBase]
23
        mov     [eax+MODULE.basedelta], edx
24
        mov     edx, [esi+STRIPPED_PE_HEADER.SizeOfImage]
25
        mov     [eax+MODULE.size], edx
26
        ret
27
.parse_mz:
28
        mov     ecx, [esi+3Ch]
29
        add     ecx, esi
30
        mov     edx, [ecx+IMAGE_NT_HEADERS.FileHeader.TimeDateStamp]
31
        mov     [eax+MODULE.timestamp], edx
32
        mov     edx, esi
33
        sub     edx, [ecx+IMAGE_NT_HEADERS.OptionalHeader.ImageBase]
34
        mov     [eax+MODULE.basedelta], edx
35
        mov     edx, [ecx+IMAGE_NT_HEADERS.OptionalHeader.SizeOfImage]
36
        mov     [eax+MODULE.size], edx
37
        ret
38
}
39
 
6614 clevermous 40
; Check whether PE module has been loaded at preferred address.
41
; If not, relocate the module.
42
;
43
; in: esi = PE base address
44
; in: [esp+4] = module name for debug print
45
; out: CF=1 - fail
6767 clevermous 46
proc fixup_pe_relocations c uses ebp, modulename
6614 clevermous 47
; 1. Fetch some data from PE header or stripped PE header.
48
; We need:
49
; * ImageBase - preferred address, compare with esi = actual load address;
50
;   ebp will keep the delta
51
; * RVA and size of fixups directory
52
; * flag IMAGE_FILE_RELOCS_STRIPPED from Characteristics
53
; If the actual address equals the preferred address, do nothing.
54
; If fixups directory is present, proceed to 2.
55
; If there is no fixups directory, there are two options:
56
; * either the directory has not been created
57
; * or the module has no fixups (data-only module, for example).
58
; In the first case, IMAGE_FILE_RELOCS_STRIPPED is set, and this is an error.
59
; In the second case, IMAGE_FILE_RELOCS_STRIPPED is not set; do nothing.
60
        mov     ebp, esi
6767 clevermous 61
        cmp     byte [esi], 'M'
6614 clevermous 62
        jz      .parse_mz
63
        sub     ebp, [esi+STRIPPED_PE_HEADER.ImageBase]
64
        jnz     @f
65
.nothing:
66
        ret
67
@@:
68
        mov     dl, byte [esi+STRIPPED_PE_HEADER.Characteristics]
69
        lea     eax, [esi+sizeof.STRIPPED_PE_HEADER+SPE_DIRECTORY_BASERELOC*sizeof.IMAGE_DATA_DIRECTORY]
70
        cmp     [esi+STRIPPED_PE_HEADER.NumberOfRvaAndSizes], SPE_DIRECTORY_BASERELOC
71
        ja      .common
72
.norelocs:
73
        test    dl, IMAGE_FILE_RELOCS_STRIPPED
74
        jz      .nothing
6767 clevermous 75
        ccall   loader_say_error, msg_noreloc1, [modulename], msg_noreloc2, 0
6614 clevermous 76
        stc
77
        ret
78
.parse_mz:
79
        mov     eax, [esi+3Ch]
80
        add     eax, esi
81
        sub     ebp, [eax+IMAGE_NT_HEADERS.OptionalHeader.ImageBase]
82
        jz      .nothing
6767 clevermous 83
        mov     dl, byte [eax+IMAGE_NT_HEADERS.FileHeader.Characteristics]
6614 clevermous 84
        cmp     [eax+IMAGE_NT_HEADERS.OptionalHeader.NumberOfDirectories], IMAGE_DIRECTORY_ENTRY_BASERELOC
85
        jbe     .norelocs
86
        add     eax, IMAGE_NT_HEADERS.OptionalHeader.DataDirectory+IMAGE_DIRECTORY_ENTRY_BASERELOC*sizeof.IMAGE_DATA_DIRECTORY
87
.common:
6767 clevermous 88
        cmp     [eax+IMAGE_DATA_DIRECTORY.isize], 0
89
        jz      .norelocs
6614 clevermous 90
        mov     edi, [eax+IMAGE_DATA_DIRECTORY.VirtualAddress]
6767 clevermous 91
        push    -1
92
        push    -1
6614 clevermous 93
        push    [eax+IMAGE_DATA_DIRECTORY.isize]
94
virtual at esp
95
.sizeleft       dd      ?
6767 clevermous 96
.next_page_original_access      dd      ?
97
.next_page_addr dd      ?
6614 clevermous 98
end virtual
99
        add     edi, esi
100
; 2. We need to relocate and we have the relocation table.
101
; esi = PE base address
102
; edi = pointer to current data of relocation table
103
; 2a. Relocation table is organized into blocks describing every page.
104
; End of table is defined from table size fetched from the header.
6767 clevermous 105
; Loop 2b-2i over all blocks until no more data is left.
6614 clevermous 106
.pageloop:
107
; 2b. Load the header of the current block: address and size.
108
; Advance total size.
109
; Size in the block includes size of the header, subtract it.
110
; If there is no data in this block, go to 2g.
111
        mov     edx, [edi+IMAGE_BASE_RELOCATION.VirtualAddress]
112
        mov     ecx, [edi+IMAGE_BASE_RELOCATION.SizeOfBlock]
113
        sub     [.sizeleft], ecx
114
        add     edi, sizeof.IMAGE_BASE_RELOCATION
115
        sub     ecx, sizeof.IMAGE_BASE_RELOCATION
116
        jbe     .pagedone
6767 clevermous 117
        push    esi
118
        fpo_delta = fpo_delta + 4
119
; 2c. Check whether we have mprotect-ed the current page at the previous step.
120
; If so, go to 2e.
6810 clevermous 121
        add     edx, esi
6767 clevermous 122
        cmp     [.next_page_addr+fpo_delta], edx
123
        jz      .mprotected_earlier
124
; 2d. We are going to modify data, so mprotect the current page to be writable.
6614 clevermous 125
; Save the old protection, we will restore it after the block is processed.
126
; Ignore any error.
6767 clevermous 127
; Go to 2f after.
6614 clevermous 128
PROT_READ = 1
129
PROT_WRITE = 2
130
PROT_EXEC = 4
6767 clevermous 131
        push    ecx
6614 clevermous 132
        mov     eax, 68
133
        mov     ebx, 30
134
        mov     ecx, PROT_READ+PROT_WRITE
135
        mov     esi, 0x1000
136
        call    FS_SYSCALL_PTR
137
        pop     ecx
6767 clevermous 138
        jmp     .mprotected
139
; 2e. We have already mprotect-ed the current page,
140
; move corresponding variables.
141
.mprotected_earlier:
142
        mov     [.next_page_addr+fpo_delta], -1
143
        mov     eax, [.next_page_original_access+fpo_delta]
144
.mprotected:
6614 clevermous 145
        push    eax
6767 clevermous 146
        fpo_delta = fpo_delta + 4
147
; 2g. Block data is an array of word values. Repeat 2h for every of those.
6614 clevermous 148
.relocloop:
149
        sub     ecx, 2
150
        jb      .relocdone
6767 clevermous 151
; 2h. Every value consists of a 4-bit type and 12-bit offset in the page.
6614 clevermous 152
; x86 uses two types: 0 = no data (used for padding), 3 = 32-bit relative.
153
        movzx   eax, word [edi]
154
        add     edi, 2
155
        mov     ebx, eax
156
        and     ebx, 0xFFF
157
        shr     eax, 12
158
        jz      .relocloop
159
        cmp     al, IMAGE_REL_BASED_HIGHLOW
160
        jnz     .badreloc
6767 clevermous 161
; If the target dword intersects page boundary,
162
; we need to mprotect the next page too.
163
        cmp     ebx, 0xFFC
164
        jbe     .no_mprotect_next
165
        push    ebx ecx edx
166
        fpo_delta = fpo_delta + 12
167
        lea     eax, [.next_page_original_access+fpo_delta]
168
        call    .restore_old_access
169
        mov     eax, 68
170
        mov     ebx, 30
171
        mov     ecx, PROT_READ+PROT_WRITE
172
        mov     esi, 0x1000
173
        add     edx, esi
174
        call    FS_SYSCALL_PTR
175
        mov     [.next_page_original_access+fpo_delta], eax
176
        mov     [.next_page_addr+fpo_delta], edx
177
        pop     edx ecx ebx
178
        fpo_delta = fpo_delta - 12
179
.no_mprotect_next:
6614 clevermous 180
        add     [edx+ebx], ebp
181
        jmp     .relocloop
182
.relocdone:
6767 clevermous 183
; 2i. Restore memory protection changed in 2d.
6614 clevermous 184
        pop     ecx
6767 clevermous 185
        fpo_delta = fpo_delta - 4
6614 clevermous 186
        mov     eax, 68
187
        mov     ebx, 30
188
        mov     esi, 0x1000
189
        call    FS_SYSCALL_PTR
190
        pop     esi
6767 clevermous 191
        fpo_delta = fpo_delta - 4
6614 clevermous 192
.pagedone:
6767 clevermous 193
        cmp     [.sizeleft+fpo_delta], 0
6614 clevermous 194
        jnz     .pageloop
6767 clevermous 195
        lea     eax, [.next_page_original_access+fpo_delta]
196
        call    .restore_old_access
197
        add     esp, 12
6614 clevermous 198
; 3. For performance reasons, relocation should be avoided
199
; by choosing an appropriate preferred address.
200
; If we have actually relocated something, yell to the debug board,
201
; so the programmer can notice that.
6767 clevermous 202
; It's a warning, not an error, so don't call loader_say_error.
6614 clevermous 203
        mov     ecx, msg_relocated1
204
        call    sys_msg_board_str
6767 clevermous 205
        mov     ecx, [modulename]
6614 clevermous 206
        call    sys_msg_board_str
207
        mov     ecx, msg_relocated2
208
        call    sys_msg_board_str
209
        clc
210
        ret
211
.badreloc:
6767 clevermous 212
        pop     ecx
213
        pop     esi
214
        add     esp, 12
215
        ccall   loader_say_error, msg_bad_relocation, [modulename], 0
6614 clevermous 216
        stc
217
        ret
6767 clevermous 218
 
219
.restore_old_access:
220
        cmp     dword [eax+4], -1
221
        jz      @f
222
        mov     ecx, [eax]
223
        mov     edx, [eax+4]
224
        mov     eax, 68
225
        mov     ebx, 30
226
        mov     esi, 0x1000
227
        call    FS_SYSCALL_PTR
228
@@:
229
        retn
6614 clevermous 230
endp
6767 clevermous 231
 
232
; Resolves static dependencies in the given PE module.
233
; Recursively loads and initializes all dependencies.
234
; in: esi -> MODULE struct
235
; out: eax=0 - success, eax=-1 - error
236
; modules_mutex should be locked
237
proc resolve_pe_imports
238
locals
239
export_base             dd      ?
240
export_ptr              dd      ?
241
export_size             dd      ?
242
import_module           dd      ?
243
import_descriptor       dd      ?
6810 clevermous 244
next_forwarder          dd      ?
245
bound_import_descriptor dd      ?
6767 clevermous 246
bound_import_dir        dd      ?
6810 clevermous 247
bound_modules_count     dd      ?
248
bound_modules_ptr       dd      ?
249
bound_module            dd      ?
250
bound_modules_left      dd      ?
6767 clevermous 251
cur_page                dd      -0x1000 ; the page at 0xFFFFF000 is never allocated
252
cur_page_old_access     dd      ?
253
next_page               dd      -1
254
next_page_old_access    dd      ?
255
endl
256
 
257
; General case of resolving imports against one module that is already loaded:
258
; binding either does not exist or has mismatched timestamp,
259
; so we need to walk through all imported symbols and resolve each one.
260
; in: ebp -> IMAGE_IMPORT_DESCRIPTOR
261
macro resolve_import_from_module fail_action
262
{
263
local .label1, .loop, .done
264
; common preparation that doesn't need to be repeated per each symbol
265
        mov     eax, [import_module]
266
        mov     eax, [eax+MODULE.base]
267
        call    prepare_import_from_module
268
; There are two arrays of dwords pointed to by FirstThunk and OriginalFirstThunk.
269
; Target array is FirstThunk: addresses of imported symbols should be written
270
; there, that is where the program expects to find them.
271
; Source array can be either FirstThunk or OriginalFirstThunk.
272
; Normally, FirstThunk and OriginalFirstThunk in a just-compiled binary
273
; point to two identical copies of the same array.
274
; Binding of the binary rewrites FirstThunk array with actual addresses,
275
; but keeps OriginalFirstThunk as is.
276
; If OriginalFirstThunk and FirstThunk are both present, use OriginalFirstThunk
277
; as source array.
278
; However, a compiler is allowed to generate a binary without OriginalFirstThunk;
279
; it is impossible to bind such a binary, but it is still valid.
280
; If OriginalFirstThunk is absent, use FirstThunk as source array.
281
        mov     ebx, [ebp+IMAGE_IMPORT_DESCRIPTOR.OriginalFirstThunk]
282
        mov     ebp, [ebp+IMAGE_IMPORT_DESCRIPTOR.FirstThunk]
283
        test    ebx, ebx
6810 clevermous 284
        jnz     @f
6767 clevermous 285
        mov     ebx, ebp
6810 clevermous 286
@@:
6767 clevermous 287
; FirstThunk and OriginalFirstThunk are RVAs.
288
        add     ebx, [esi+MODULE.base]
289
        add     ebp, [esi+MODULE.base]
290
; Source array is terminated with zero dword.
291
.loop:
292
        cmp     dword [ebx], 0
293
        jz      .done
294
        mov     ecx, [ebx]
295
        get_address_for_thunk ; should preserve esi,edi,ebp
296
        test    eax, eax
297
        jz      fail_action
298
        mov     edi, eax
299
        mov     edx, ebp
300
        call    .ensure_writable ; should preserve edx,ebx,esi,ebp
301
        mov     [edx], edi
302
        add     ebx, 4
303
        add     ebp, 4
304
        jmp     .loop
305
.done:
306
}
307
 
308
; Resolve one imported symbol.
309
; in: ecx = ordinal or RVA of thunk
310
; out: eax = address of exported function
311
macro get_address_for_thunk
312
{
313
local .ordinal, .common
314
; Ordinal imports have bit 31 set, name imports have bit 31 clear.
315
        btr     ecx, 31
316
        jc      .ordinal
317
; Thunk for name import is RVA of IMAGE_IMPORT_BY_NAME structure.
318
        add     ecx, [esi+MODULE.base]
319
        movzx   edx, [ecx+IMAGE_IMPORT_BY_NAME.Hint]
320
        add     ecx, IMAGE_IMPORT_BY_NAME.Name
321
        call    get_exported_function_by_name
322
        jmp     .common
323
.ordinal:
324
; Thunk for ordinal import is just an ordinal,
325
; bit 31 has been cleared by btr instruction.
326
        call    get_exported_function_by_ordinal
327
.common:
328
}
329
 
330
; We have four main variants:
331
; normal unbound import, old-style bound import, new-style bound import,
332
; no import.
333
; * Normal unbound import:
334
;   we have an array of import descriptors, one per imported module,
335
;   pointed to by import directory.
336
;   We should loop over all descriptors and apply resolve_import_from_module
337
;   for each one.
338
; * Old-style bound import:
339
;   we have the same array of import descriptors, but timestamp field is set up.
340
;   We should do the same loop, but we can do a lightweight processing
341
;   of modules with correct timestamp. In the best case, "lightweight processing"
342
;   means just skipping them, but corrections arise for relocated modules
343
;   and forwarded exports.
344
; * New-style bound import:
345
;   we have two parallel arrays of import descriptors and bound descriptors,
346
;   pointed to by two directories. Timestamp field has a special value -1
347
;   in import descriptors, real timestamps are in bound descriptors.
348
; * No import: not really different from normal import with no descriptors.
6810 clevermous 349
; Forwarded exports are the part where binding goes really interesting.
350
; The address of forwarded export can change with any library in the chain.
351
; In old-style bound import, a descriptor can list only the library itself,
352
; so it is impossible to bind forwarded exports at all; thunks that point to
353
; forwarded exports are linked in the list starting from ForwarderChain in import descriptor.
354
; New-style bound import exists exactly to address this problem; it allows to
355
; list several related libraries for one imported module.
356
; However, even with new-style bound import, some forwarded exports can
357
; still remain unbound: binding tool can fail to load dependent libraries
358
; during binding time; the tool from MS SDK for unknown reason just refuses to
359
; bind forwarded exports except for built-in white list if subsystem version is
360
; < 6.0. So even with new-style bound import, ForwarderChain still can be non-empty.
361
; Thus, we always need to look at old-style import descriptor.
6767 clevermous 362
; 1. Fetch addresses of two directories. We are not interested in their sizes.
363
; ebp = import RVA
364
; ebx = bound import RVA
365
        xor     ebx, ebx
366
        xor     ebp, ebp
367
; PE and stripped PE have different places for directories.
368
        mov     eax, [esi+MODULE.base]
369
        cmp     byte [eax], 'M'
370
        jz      .parse_mz
371
        cmp     [eax+STRIPPED_PE_HEADER.NumberOfRvaAndSizes], SPE_DIRECTORY_IMPORT
372
        jbe     .common
373
        mov     ebp, [eax+sizeof.STRIPPED_PE_HEADER+SPE_DIRECTORY_IMPORT*sizeof.IMAGE_DATA_DIRECTORY]
374
        cmp     [eax+STRIPPED_PE_HEADER.NumberOfRvaAndSizes], SPE_DIRECTORY_BOUND_IMPORT
375
        jbe     .common
376
        mov     ebx, [eax+sizeof.STRIPPED_PE_HEADER+SPE_DIRECTORY_BOUND_IMPORT*sizeof.IMAGE_DATA_DIRECTORY]
377
        jmp     .common
378
.parse_mz:
379
        add     eax, [eax+3Ch]
380
        cmp     [eax+IMAGE_NT_HEADERS.OptionalHeader.NumberOfDirectories], IMAGE_DIRECTORY_ENTRY_IMPORT
381
        jbe     .common
382
        mov     ebp, [eax+IMAGE_NT_HEADERS.OptionalHeader.DataDirectory+IMAGE_DIRECTORY_ENTRY_IMPORT*sizeof.IMAGE_DATA_DIRECTORY]
383
        cmp     [eax+IMAGE_NT_HEADERS.OptionalHeader.NumberOfDirectories], IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT
384
        jbe     .common
385
        mov     ebx, [eax+IMAGE_NT_HEADERS.OptionalHeader.DataDirectory+IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT*sizeof.IMAGE_DATA_DIRECTORY]
386
.common:
6810 clevermous 387
; If import directory is not present, no import - nothing to do.
388
; Ignore bound import directory in this case.
6767 clevermous 389
        test    ebp, ebp
390
        jz      .done
391
        add     ebp, [esi+MODULE.base] ; directories contain RVA
6810 clevermous 392
        add     ebx, [esi+MODULE.base] ; directories contain RVA
393
        mov     [bound_import_dir], ebx
394
        mov     [bound_import_descriptor], ebx
395
; Repeat remaining steps for all descriptors in the directory.
396
.descriptor_loop:
397
; 2. Check whether this descriptor is an end mark with zero fields.
6767 clevermous 398
; Look at Name field.
399
        mov     edi, [ebp+IMAGE_IMPORT_DESCRIPTOR.Name]
400
        test    edi, edi
401
        jz      .done
6810 clevermous 402
; 3. Load the target module.
6767 clevermous 403
        add     edi, [esi+MODULE.base] ; Name field is RVA
404
        call    load_imported_module ; should preserve esi,ebp
405
        test    eax, eax
406
        jz      .failed
407
        mov     [import_module], eax
6810 clevermous 408
; 4. Check whether the descriptor has a non-stale old-style binding.
6767 clevermous 409
; Zero timestamp means "not bound".
6810 clevermous 410
; Timestamp 0xFFFFFFFF means "new-style binding".
6767 clevermous 411
; Mismatched timestamp means "stale binding".
6810 clevermous 412
; In first and third cases, go to 9.
413
; In second case, go to 10 for further checks.
6767 clevermous 414
        mov     edx, [ebp+IMAGE_IMPORT_DESCRIPTOR.TimeDateStamp]
415
        test    edx, edx
6810 clevermous 416
        jz      .resolve_generic
417
        cmp     edx, -1
418
        jz      .new_binding
6767 clevermous 419
        cmp     edx, [eax+MODULE.timestamp]
6810 clevermous 420
        jnz     .resolve_generic
421
; 5. The descriptor has a non-stale old-style binding.
6767 clevermous 422
; There are two cases when we still need to do something:
423
; * if the target module has been relocated, we need to add
424
;   relocation delta to all addresses;
6810 clevermous 425
; * if some exports are forwarded, we need to resolve them.
6767 clevermous 426
;   Thunks for forwarded exports contain index of next forwarded export
427
;   instead of target address, making a single-linked list terminated by -1.
428
;   ForwarderChain is the head of the list.
6810 clevermous 429
; Check for the first problem first; the corresponding code can handle both.
430
; If the target module is relocated, go to 8.
431
; If the target module is not relocated, but has forwarded exports, go to 7.
432
; Otherwise, advance to 6.
433
.old_binding:
6767 clevermous 434
        cmp     [eax+MODULE.basedelta], 0
6810 clevermous 435
        jnz     .old_binding_relocate
436
.check_forwarder_chain:
6767 clevermous 437
        cmp     [ebp+IMAGE_IMPORT_DESCRIPTOR.ForwarderChain], -1
6810 clevermous 438
        jnz     .resolve_forward_chain
439
.next_descriptor:
440
; 6. Advance to next descriptor and continue the loop.
441
        add     ebp, sizeof.IMAGE_IMPORT_DESCRIPTOR
442
        jmp     .descriptor_loop
443
.resolve_forward_chain:
444
; 7. Resolve all thunks from ForwarderChain list.
6767 clevermous 445
        mov     eax, [import_module]
446
        mov     eax, [eax+MODULE.base]
447
        call    prepare_import_from_module
6810 clevermous 448
        mov     edi, [ebp+IMAGE_IMPORT_DESCRIPTOR.ForwarderChain]
449
.resolve_forward_chain_loop:
6767 clevermous 450
        mov     ebx, [ebp+IMAGE_IMPORT_DESCRIPTOR.OriginalFirstThunk]
451
        add     ebx, [esi+MODULE.base]
452
        mov     ecx, [ebx+edi*4]
453
        get_address_for_thunk ; should preserve esi,edi,ebp
454
        test    eax, eax
455
        jz      .failed
456
        mov     ebx, eax
457
        mov     edx, [ebp+IMAGE_IMPORT_DESCRIPTOR.FirstThunk]
458
        add     edx, [esi+MODULE.base]
459
        lea     edx, [edx+edi*4]
460
        call    .ensure_writable ; should preserve edx,ebx,esi,ebp
461
        mov     edi, [edx] ; next forwarded export
462
        mov     [edx], ebx ; store the address
463
        cmp     edi, -1
6810 clevermous 464
        jnz     .resolve_forward_chain_loop
465
; After resolving, we are done with this import descriptor, go to 6.
466
        jmp     .next_descriptor
467
.done:
468
        call    .restore_protection
469
        xor     eax, eax
470
        ret
471
.old_binding_relocate:
472
; 8. The descriptor has a non-stale old-style binding,
473
; but the target module is relocated, so we need to add MODULE.basedelta to
474
; all addresses. Also, there can be forwarded exports.
475
; For consistency with generic-case resolve_import_from_module,
476
; check for end of thunks by looking at OriginalFirstThunk array.
477
; Note: we assume here that ForwarderChain list is ordered.
478
; After that, go to 6.
479
        mov     edi, [eax+MODULE.basedelta]
480
        cmp     [ebp+IMAGE_IMPORT_DESCRIPTOR.ForwarderChain], -1
481
        jz      @f
482
        mov     eax, [import_module]
483
        mov     eax, [eax+MODULE.base]
484
        call    prepare_import_from_module
485
@@:
486
        mov     edx, [ebp+IMAGE_IMPORT_DESCRIPTOR.FirstThunk]
487
        add     edx, [esi+MODULE.base]
488
        mov     ebx, [ebp+IMAGE_IMPORT_DESCRIPTOR.OriginalFirstThunk]
489
        add     ebx, [esi+MODULE.base]
490
        mov     eax, [ebp+IMAGE_IMPORT_DESCRIPTOR.ForwarderChain]
491
        lea     eax, [edx+eax*4]
492
        mov     [next_forwarder], eax
493
.old_binding_relocate_loop:
494
        cmp     dword [ebx], 0
495
        jz      .next_descriptor
496
        call    .ensure_writable ; should preserve esi,edi,ebp,ebx,edx
497
        cmp     edx, [next_forwarder]
498
        jz      .old_binding_relocate_forwarder
499
        add     dword [edx], edi
500
.old_binding_relocate_next:
501
        add     edx, 4
502
        add     ebx, 4
503
        jmp     .old_binding_relocate_loop
504
.old_binding_relocate_forwarder:
505
        mov     ecx, [ebx]
506
        get_address_for_thunk
507
        test    eax, eax
508
        jz      .failed
509
        mov     edx, [next_forwarder]
510
        mov     ecx, [edx]
511
        mov     [edx], eax
512
        mov     eax, [ebp+IMAGE_IMPORT_DESCRIPTOR.FirstThunk]
513
        add     eax, [esi+MODULE.base]
514
        lea     eax, [eax+ecx*4]
515
        mov     [next_forwarder], eax
516
        jmp     .old_binding_relocate_next
517
.resolve_generic:
518
; 9. Run generic-case resolver.
6767 clevermous 519
        mov     [import_descriptor], ebp
520
        resolve_import_from_module .failed ; should preserve esi
521
        mov     ebp, [import_descriptor]
6810 clevermous 522
; After that, go to 6.
523
        jmp     .next_descriptor
524
.new_binding:
525
; New-style bound import.
526
; 10. Locate the new-style descriptor corresponding to the current
527
; import descriptor.
528
; 10a. Check the current new-style descriptor: the one that follows
529
; previous one, if there was the previous one, or the first one.
530
; In most cases, new-style descriptors are in the same order as
531
; import descriptors, so full loop in 10b can be avoided.
532
        mov     edx, [ebp+IMAGE_IMPORT_DESCRIPTOR.Name]
533
        add     edx, [esi+MODULE.base]
534
        mov     ebx, [bound_import_descriptor]
6767 clevermous 535
        movzx   edi, [ebx+IMAGE_BOUND_IMPORT_DESCRIPTOR.OffsetModuleName]
536
        test    edi, edi
6810 clevermous 537
        jz      .look_new_binding_hard
538
        add     edi, [bound_import_dir]
539
        xor     ecx, ecx
540
@@:
541
        mov     al, [edx+ecx]
542
        cmp     al, [edi+ecx]
543
        jnz     .look_new_binding_hard
544
        test    al, al
545
        jz      .new_binding_found
546
        inc     ecx
547
        jmp     @b
548
.look_new_binding_hard:
549
; 10b. We are out of luck with the current new-style descriptor,
550
; so loop over all of them looking for the matching one.
551
        mov     ebx, [bound_import_dir]
552
.look_new_binding_loop:
553
        movzx   edi, [ebx+IMAGE_BOUND_IMPORT_DESCRIPTOR.OffsetModuleName]
554
        add     edi, [bound_import_dir]
555
        xor     ecx, ecx
556
@@:
557
        mov     al, [edx+ecx]
558
        cmp     al, [edi+ecx]
559
        jnz     .look_new_binding_next
560
        test    al, al
561
        jz      .new_binding_found
562
        inc     ecx
563
        jmp     @b
564
.look_new_binding_next:
565
        movzx   ecx, [ebx+IMAGE_BOUND_IMPORT_DESCRIPTOR.NumberOfModuleForwarderRefs]
566
        lea     ebx, [ebx+(ecx+1)*sizeof.IMAGE_BOUND_IMPORT_DESCRIPTOR]
567
        jmp     .look_new_binding_loop
568
.new_binding_found:
569
; 10c. Store the next descriptor for subsequent scans.
570
        movzx   ecx, [ebx+IMAGE_BOUND_IMPORT_DESCRIPTOR.NumberOfModuleForwarderRefs]
571
        lea     eax, [ebx+(ecx+1)*sizeof.IMAGE_BOUND_IMPORT_DESCRIPTOR]
572
        mov     [bound_import_descriptor], eax
6767 clevermous 573
; Bound import descriptors come in groups.
574
; The first descriptor in each group corresponds to the main imported module.
575
; If some exports from the module are forwarded, additional descriptors
576
; are created for modules where those exports are forwarded to.
577
; Number of additional descriptors is given by one field in the first descriptor.
6810 clevermous 578
; 11. We have already loaded the main module, validate its timestamp.
579
; If timestamp does not match, go to 9 to run generic-case resolver.
580
        mov     eax, [import_module]
581
        mov     edx, [eax+MODULE.timestamp]
582
        cmp     edx, [ebx+IMAGE_BOUND_IMPORT_DESCRIPTOR.TimeDateStamp]
583
        jnz     .resolve_generic
584
; 12. If there are no additional libraries,
585
; the situation is exactly same as old-style binding, so go to 5.
586
        test    ecx, ecx
587
        jz      .old_binding
588
; 13. Load additional libraries and validate their timestamps.
589
; If at least one timestamp is invalid, resort to generic-case resolving.
590
; 13a. Allocate memory for all bound modules, including the main module.
591
        lea     ecx, [(ecx+1)*4]
592
        stdcall malloc, ecx
593
        test    eax, eax
594
        jz      .failed
595
        mov     [bound_modules_ptr], eax
596
; 13b. Store the main module.
597
        mov     edx, [import_module]
598
        mov     [eax], edx
599
        xor     ecx, ecx
600
; 13c. Loop over all additional descriptors.
601
.newstyle_load_loop:
602
        inc     ecx
603
        mov     [bound_modules_count], ecx
604
        movzx   edi, [ebx+ecx*sizeof.IMAGE_BOUND_IMPORT_DESCRIPTOR+IMAGE_BOUND_IMPORT_DESCRIPTOR.OffsetModuleName]
6767 clevermous 605
        add     edi, [bound_import_dir]
606
        call    load_imported_module ; should preserve ebx,esi,ebp
607
        test    eax, eax
6810 clevermous 608
        jz      .newstyle_failed
609
        mov     ecx, [bound_modules_count]
610
        mov     edx, [ebx+ecx*sizeof.IMAGE_BOUND_IMPORT_DESCRIPTOR+IMAGE_BOUND_IMPORT_DESCRIPTOR.TimeDateStamp]
611
        cmp     [eax+MODULE.timestamp], edx
612
        jnz     .newstyle_stale
613
        mov     edx, [bound_modules_ptr]
614
        mov     [edx+ecx*4], eax
615
        cmp     cx, [ebx+IMAGE_BOUND_IMPORT_DESCRIPTOR.NumberOfModuleForwarderRefs]
616
        jb      .newstyle_load_loop
617
        inc     ecx
618
        mov     [bound_modules_count], ecx
619
; New-style binding has correct timestamp.
620
; There still can be same two problems as in step 5 with old-style binding.
621
; 14. Check whether at least one module is relocated. If so, go to 16.
622
.newstyle_check_reloc:
623
        mov     eax, [edx]
6767 clevermous 624
        cmp     [eax+MODULE.basedelta], 0
6810 clevermous 625
        jnz     .newstyle_need_reloc
626
        add     edx, 4
627
        dec     ecx
628
        jnz     .newstyle_check_reloc
629
; 15. Bound modules are not relocated.
630
; The only remaining problem could be unbound forwarders.
631
; Free memory allocated at 13a and let steps 6 and 7 do their work.
632
        stdcall free, [bound_modules_ptr]
633
        jmp     .check_forwarder_chain
634
.newstyle_stale:
635
        stdcall free, [bound_modules_ptr]
636
        jmp     .resolve_generic
637
; 16. The descriptor has a non-stale new-style binding,
638
; but at least one of target modules is relocated, so we need to add
639
; MODULE.basedelta to addresses from relocated modules.
640
; Also, there can be forwarded exports.
6767 clevermous 641
; For consistency with generic-case resolve_import_from_module,
6810 clevermous 642
; check for end of thunks by looking at OriginalFirstThunk array.
643
; Note: we assume here that ForwarderChain list is ordered.
644
; After that, go to 6.
645
.newstyle_need_reloc:
646
        mov     eax, [import_module]
647
        mov     eax, [eax+MODULE.base]
648
        call    prepare_import_from_module
6767 clevermous 649
        mov     edx, [ebp+IMAGE_IMPORT_DESCRIPTOR.FirstThunk]
650
        add     edx, [esi+MODULE.base]
651
        mov     ebx, [ebp+IMAGE_IMPORT_DESCRIPTOR.OriginalFirstThunk]
652
        add     ebx, [esi+MODULE.base]
6810 clevermous 653
        mov     eax, [ebp+IMAGE_IMPORT_DESCRIPTOR.ForwarderChain]
654
        lea     eax, [edx+eax*4]
655
        mov     [next_forwarder], eax
656
.new_binding_relocate_loop:
6767 clevermous 657
        cmp     dword [ebx], 0
6810 clevermous 658
        jz      .new_binding_relocate_done
659
        cmp     edx, [next_forwarder]
660
        jz      .new_binding_resolve_thunk
661
        mov     [bound_module], 0
662
        mov     eax, [bound_modules_count]
663
        mov     [bound_modules_left], eax
664
        mov     edi, [bound_modules_ptr]
665
; There should be at least one module containing address [edx].
666
; There can be more than one if preferred address ranges for two modules intersect;
667
; in this case, we are forced to resolve address from scratch.
668
.new_binding_lookup_module:
669
        mov     ecx, [edx]
670
        mov     eax, [edi]
671
        sub     ecx, [eax+MODULE.base]
672
        add     ecx, [eax+MODULE.basedelta]
673
        cmp     ecx, [eax+MODULE.size]
674
        jae     @f
675
        cmp     [bound_module], 0
676
        jnz     .new_binding_resolve_thunk
677
        mov     [bound_module], eax
678
@@:
679
        add     edi, 4
680
        dec     [bound_modules_left]
681
        jnz     .new_binding_lookup_module
682
        mov     edi, [bound_module]
683
        mov     edi, [edi+MODULE.basedelta]
684
        test    edi, edi
685
        jz      .new_binding_relocate_next
686
        call    .ensure_writable ; should preserve esi,edi,ebp,ebx,edx
687
        add     dword [edx], edi
688
.new_binding_relocate_next:
689
        add     edx, 4
6767 clevermous 690
        add     ebx, 4
6810 clevermous 691
        jmp     .new_binding_relocate_loop
692
.new_binding_resolve_thunk:
693
        mov     [bound_modules_left], edx
694
        call    .ensure_writable
695
        mov     ecx, [ebx]
696
        get_address_for_thunk
697
        test    eax, eax
698
        jz      .newstyle_failed
699
        mov     edx, [bound_modules_left]
700
        mov     ecx, [edx]
701
        mov     [edx], eax
702
        cmp     edx, [next_forwarder]
703
        jnz     .new_binding_relocate_next
704
        mov     eax, [ebp+IMAGE_IMPORT_DESCRIPTOR.FirstThunk]
705
        add     eax, [esi+MODULE.base]
706
        lea     eax, [eax+ecx*4]
707
        mov     [next_forwarder], eax
708
        jmp     .new_binding_relocate_next
709
.new_binding_relocate_done:
710
        stdcall free, [bound_modules_ptr]
711
        jmp     .next_descriptor
712
.newstyle_failed:
713
        stdcall free, [bound_modules_ptr]
6767 clevermous 714
.failed:
715
        call    .restore_protection
716
        xor     eax, eax
717
        dec     eax
718
        ret
719
 
720
; Local helper functions.
721
        fpo_delta = fpo_delta + 4
722
; Import table may reside in read-only pages.
723
; We should mprotect any page where we are going to write to.
724
; Things get interesting when one thunk spans two pages.
725
; in: edx = address of dword to make writable
726
.ensure_writable:
727
; 1. Fast path: if we have already mprotect-ed one page and
728
; the requested dword is in the same page, do nothing.
729
        mov     eax, edx
730
        sub     eax, [cur_page]
731
        cmp     eax, 0x1000 - 4
732
        ja      .cur_page_not_sufficient
733
.ensure_writable.nothing:
734
        retn
735
.cur_page_not_sufficient:
736
; 2. If the requested dword begins in the current page
737
; and ends in the next page, mprotect the next page and return.
738
        push    ebx esi edx
739
        fpo_delta = fpo_delta + 12
740
        cmp     eax, 0x1000
741
        jae     .wrong_cur_page
742
        cmp     [next_page], -1
743
        jnz     @f
744
        mov     eax, 68
745
        mov     ebx, 30
746
        mov     ecx, PROT_READ+PROT_WRITE
747
        mov     edx, [cur_page]
748
        mov     esi, 0x1000
749
        add     edx, esi
750
        mov     [next_page], edx
751
        call    FS_SYSCALL_PTR
752
        mov     [next_page_old_access], eax
753
@@:
754
        pop     edx esi ebx
755
        retn
756
.wrong_cur_page:
757
; The requested dword does not intersect with the current page.
758
; 3. Restore the protection of the current page,
759
; it is unlikely to be used again.
760
        cmp     [cur_page], -0x1000
761
        jz      @f
762
        mov     eax, 68
763
        mov     ebx, 30
764
        mov     ecx, [cur_page_old_access]
765
        mov     edx, [cur_page]
766
        mov     esi, 0x1000
767
        call    FS_SYSCALL_PTR
768
@@:
769
; 4. If the next page has been mprotect-ed too,
770
; switch to it as the current page and restart the function.
771
        cmp     [next_page], -1
772
        jz      @f
773
        mov     eax, [next_page]
774
        mov     [cur_page], eax
775
        mov     eax, [next_page_old_access]
776
        mov     [cur_page_old_access], eax
777
        mov     [next_page], -1
778
        pop     edx esi ebx
779
        jmp     .ensure_writable
780
@@:
781
; 5. This is the entirely new page to mprotect.
782
        mov     edx, [esp]
783
        and     edx, not 0xFFF
784
        mov     eax, 68
785
        mov     ebx, 30
786
        mov     ecx, PROT_READ+PROT_WRITE
787
        mov     [cur_page], edx
788
        mov     esi, 0x1000
789
        call    FS_SYSCALL_PTR
790
        mov     [cur_page_old_access], eax
791
        pop     edx esi ebx
792
        fpo_delta = fpo_delta - 12
793
        retn
794
 
795
; Called at end of processing,
796
; restores protection of pages that we have mprotect-ed for write.
797
.restore_protection:
798
        push    esi
799
        fpo_delta = fpo_delta + 4
800
        cmp     [next_page], -1
801
        jz      @f
802
        mov     eax, 68
803
        mov     ebx, 30
804
        mov     ecx, [next_page_old_access]
805
        mov     edx, [next_page]
806
        mov     esi, 0x1000
807
        call    FS_SYSCALL_PTR
808
@@:
809
        cmp     [cur_page], -0x1000
810
        jz      @f
811
        mov     eax, 68
812
        mov     ebx, 30
813
        mov     ecx, [cur_page_old_access]
814
        mov     edx, [cur_page]
815
        mov     esi, 0x1000
816
        call    FS_SYSCALL_PTR
817
@@:
818
        pop     esi
819
        fpo_delta = fpo_delta - 4
820
        retn
821
endp
822
 
823
; Part of resolving symbol from a module that is the same for all symbols.
824
; resolve_pe_imports calls it only once per module.
825
; Fetches export directory from the module.
826
; Non-standard calling convention: saves results to first 2 dwords on the stack.
827
; in: eax = module base
828
proc prepare_import_from_module c, export_base, export_ptr, export_size
829
; The implementation is straightforward.
830
        mov     [export_base], eax
831
        cmp     byte [eax], 'M'
832
        jz      .parse_mz
833
        cmp     [eax+STRIPPED_PE_HEADER.NumberOfRvaAndSizes], SPE_DIRECTORY_EXPORT
834
        jbe     .noexport
835
        mov     edx, [eax+sizeof.STRIPPED_PE_HEADER+SPE_DIRECTORY_EXPORT*sizeof.IMAGE_DATA_DIRECTORY+IMAGE_DATA_DIRECTORY.VirtualAddress]
6810 clevermous 836
        test    edx, edx
837
        jz      .noexport
6767 clevermous 838
        add     edx, eax
839
        mov     [export_ptr], edx
840
        mov     edx, [eax+sizeof.STRIPPED_PE_HEADER+SPE_DIRECTORY_EXPORT*sizeof.IMAGE_DATA_DIRECTORY+IMAGE_DATA_DIRECTORY.isize]
841
        mov     [export_size], edx
842
        ret
843
.parse_mz:
844
        mov     ecx, [eax+3Ch]
845
        add     ecx, eax
846
        cmp     [ecx+IMAGE_NT_HEADERS.OptionalHeader.NumberOfDirectories], IMAGE_DIRECTORY_ENTRY_EXPORT
847
        jbe     .noexport
848
        mov     edx, [ecx+IMAGE_NT_HEADERS.OptionalHeader.DataDirectory.VirtualAddress+IMAGE_DIRECTORY_ENTRY_EXPORT*sizeof.IMAGE_DATA_DIRECTORY]
6810 clevermous 849
        test    edx, edx
850
        jz      .noexport
6767 clevermous 851
        add     edx, eax
852
        mov     [export_ptr], edx
853
        mov     edx, [ecx+IMAGE_NT_HEADERS.OptionalHeader.DataDirectory.isize+IMAGE_DIRECTORY_ENTRY_EXPORT*sizeof.IMAGE_DATA_DIRECTORY]
854
        mov     [export_size], edx
855
        ret
856
.noexport:
857
        mov     [export_ptr], 0
858
        mov     [export_size], 0
859
        ret
860
endp
861
 
862
; PE format supports export by name and by ordinal.
863
; Any exported symbol always have an ordinal.
864
; It may have a name, it may have no name.
865
; A symbol can even have multiple names, usually this happens
866
; when several functions with the same body like 'ret' are merged.
867
;
868
; Addresses of all exported symbols are contained in one array AddressOfFunctions.
869
; Ordinal of a symbol is an index in this array + Base.
870
; Base is defined in export directory, usually it equals 1.
871
;
872
; Export by name is more complicated. There are two parallel arrays
873
; AddressOfNames and AddressOfNameOrdinals with the same length.
874
; This length can be less or greater than length of AddressOfFunctions.
875
; AddressOfNames is a sorted array with all exported names.
876
; AddressOfNameOrdinals, contrary to the title, gives index in AddressOfFunctions.
877
; Looking up a name means
878
; * scanning AddressOfNames array to find the index of the corresponding name
879
; * looking in AddressOfNameOrdinals at the index found above to get another index;
880
;   index in AddressOfNames/AddressOfNameOrdinals has no other meaning
881
; * finally, looking in AddressOfFunctions with that second index.
882
 
883
; Resolve symbol from a module by name.
884
; prepare_import_from_module should be called beforehand.
885
; in: ecx -> name, edx = hint for lookup in name table
886
; out: eax = exported address or NULL
887
; if [module] is zero, modules_mutex should be unlocked
888
; if [module] is nonzero, modules_mutex should be locked
889
proc get_exported_function_by_name c uses ebx esi edi, export_base, export_ptr, export_size, module
890
locals
891
forward_export_base     dd      ?
892
forward_export_ptr      dd      ?
893
forward_export_size     dd      ?
894
forward_export_module   dd      ?
895
endl
896
; 1. Find length of the name, including terminating zero.
897
        mov     esi, ecx
898
@@:
899
        inc     ecx
900
        cmp     byte [ecx-1], 0
901
        jnz     @b
902
        sub     ecx, esi
903
; 2. Validate that export directory is present at all.
904
        mov     eax, [export_ptr]
905
        test    eax, eax
906
        jz      .export_name_not_found
907
; 3. Check whether the hint is correct.
908
; The hint is a zero-based index in name table.
909
; Theoretically, zero is a valid hint.
910
; Unfortunately, in practice everyone uses zero if the hint is unknown,
911
; which is a quite typical situation, so treating zero as a valid hint
912
; would waste processor cycles much more often than save.
913
; So only check the hint if it is between 1 and NumberOfNames-1 inclusive.
914
; 3a. Validate the hint.
915
        mov     ebx, [eax+IMAGE_EXPORT_DIRECTORY.AddressOfNames]
916
        add     ebx, [export_base]
917
        cmp     edx, [eax+IMAGE_EXPORT_DIRECTORY.NumberOfNames]
918
        jae     .ignore_hint
919
        test    edx, edx
920
        jz      .ignore_hint
921
; 3b. Check the hinted name.
922
; If it matches, go to 5. If not, we're out of luck, use normal lookup.
923
        mov     edi, [ebx+edx*4]
924
        add     edi, [export_base]
925
        push    ecx esi
926
        repz cmpsb
927
        pop     esi ecx
6810 clevermous 928
        jz      .hint_ok
6767 clevermous 929
.ignore_hint:
930
; 4. Binary search over name table.
931
; Export names are sorted with respect to repz cmpsb.
932
; edi <= (the target index) < edx
933
        xor     edi, edi
934
        mov     edx, [eax+IMAGE_EXPORT_DIRECTORY.NumberOfNames]
935
.export_name_search.loop:
936
; if there are no indexes between edi and edx, name is invalid
937
        cmp     edi, edx
938
        jae     .export_name_not_found
939
; try the index in the middle of current range
940
        lea     eax, [edi+edx]
941
        shr     eax, 1
942
; compare
943
        push    ecx esi edi
944
        fpo_delta = fpo_delta + 12
945
        mov     edi, [ebx+eax*4]
946
        add     edi, [export_base]
947
        repz cmpsb
948
        pop     edi esi ecx
949
        fpo_delta = fpo_delta - 12
950
; exact match -> found, go to 5
951
; string at esi = target, string at edi = current attempt
952
; (string at esi) < (string at edi) -> current index is too high, update upper range
953
; (string at esi) > (string at edi) -> current index is too low, update lower range
954
        jz      .found
955
        jb      @f
956
        lea     edi, [eax+1]
957
        jmp     .export_name_search.loop
958
@@:
959
        mov     edx, eax
960
        jmp     .export_name_search.loop
961
; Generic error handler.
962
.export_name_not_found:
963
        mov     ebx, esi
6810 clevermous 964
        call    .get_module_name
6767 clevermous 965
        ccall   loader_say_error, msg_export_name_not_found, ebx, msg_export_not_found, eax, 0
966
.return0:
967
        xor     eax, eax
968
        ret
6810 clevermous 969
.hint_ok:
970
        mov     eax, edx
6767 clevermous 971
.found:
972
; 5. We have found an index in AddressOfNames/AddressOfNameOrdinals arrays,
973
; convert it to index in AddressOfFunctions array.
974
        mov     edx, [export_ptr]
975
        mov     ebx, [edx+IMAGE_EXPORT_DIRECTORY.AddressOfNameOrdinals]
976
        add     ebx, [export_base]
977
        movzx   eax, word [ebx+eax*2]
978
; 6. Fetch the exported address from AddressOfFunctions array.
979
        cmp     eax, [edx+IMAGE_EXPORT_DIRECTORY.NumberOfFunctions]
980
        jae     .export_name_not_found
981
        mov     ebx, [edx+IMAGE_EXPORT_DIRECTORY.AddressOfFunctions]
982
        add     ebx, [export_base]
983
        mov     eax, [ebx+eax*4]
984
        test    eax, eax
985
        jz      .export_name_not_found
986
.check_forwarded:
987
; This part of code is also used by get_exported_function_by_ordinal.
988
; 7. Check whether the address is inside the export directory.
989
; If not, we are done.
990
        add     eax, [export_base]
6810 clevermous 991
        mov     ebx, eax
992
        sub     ebx, edx
993
        cmp     ebx, [export_size]
6767 clevermous 994
        jb      .export_is_forwarded
995
        ret
996
.export_is_forwarded:
997
; The export is forwarded to another module.
998
; The address we have got points to the string "."
999
; 8. Get the target module name. It is everything before the first dot,
1000
; minus DLL extension.
1001
; 8a. Find the dot.
1002
        mov     ebx, eax
1003
@@:
1004
        inc     eax
1005
        cmp     byte [eax-1], '.'
1006
        jz      .dot_found
1007
        cmp     byte [eax-1], 0
1008
        jnz     @b
6810 clevermous 1009
        call    .get_module_name
1010
        ccall   loader_say_error, msg_invalid_forwarder, eax, 0
1011
        xor     eax, eax
1012
        ret
6767 clevermous 1013
.dot_found:
1014
; 8b. Allocate the memory.
1015
        sub     eax, ebx
1016
        mov     edi, eax
1017
        add     eax, 4 ; dll + terminating zero
1018
        stdcall malloc, eax
1019
        test    eax, eax
1020
        jz      .return0
1021
; 8c. Copy module name.
1022
        mov     esi, ebx
1023
        mov     ecx, edi
1024
        mov     edi, eax
1025
        rep movsb
1026
        mov     dword [edi], 'dll'
1027
        mov     ebx, esi ; save pointer to 
1028
        mov     edi, eax ; module name
1029
; 9. Load the target module.
1030
; 9a. Get the pointer to MODULE struct for ourselves.
1031
        mov     esi, [module]
1032
        test    esi, esi
1033
        jnz     @f
1034
        mutex_lock modules_mutex
1035
        mov     ecx, [export_base]
1036
        call    find_module_by_addr
1037
        test    esi, esi
1038
        jz      .load_forwarded_failed
1039
@@:
1040
; 9b. Call the worker.
1041
        call    load_imported_module
1042
        test    eax, eax
1043
        jz      .load_forwarded_failed
1044
        mov     esi, eax
1045
; 9c. We don't need module name anymore, free the memory allocated at 8b.
1046
        stdcall free, edi
1047
; 10. Resolve the forwarded export recursively.
1048
; 10a. Prepare for importing.
1049
        mov     [forward_export_module], esi
1050
        mov     eax, [esi+MODULE.base]
1051
        call    prepare_import_from_module
1052
; 10b. Check whether we are importing by ordinal or by name.
1053
; Forwarded export by ordinal has ebx -> "#".
1054
        cmp     byte [ebx], '#'
1055
        jnz     .no_ordinal
1056
        lea     edx, [ebx+1]
1057
        xor     ecx, ecx ; ordinal
1058
@@:
1059
        movzx   eax, byte [edx]
1060
        sub     eax, '0'
1061
        cmp     eax, 10
1062
        jae     .no_ordinal
1063
        lea     ecx, [ecx*5]
1064
        lea     ecx, [ecx*2+eax]
1065
        inc     edx
1066
        cmp     byte [edx], 0
1067
        jnz     @b
1068
; 10c. We are importing by ordinal. Call the worker.
1069
        call    get_exported_function_by_ordinal
1070
        jmp     @f
1071
        ret
1072
.no_ordinal:
1073
; 10d. We are importing by name. Call the worker.
1074
        mov     ecx, ebx
1075
        or      edx, -1
1076
        call    get_exported_function_by_name
1077
@@:
1078
        cmp     [module], 0
1079
        jnz     @f
1080
        push    eax
1081
        mutex_unlock modules_mutex
1082
        pop     eax
1083
@@:
1084
        ret
1085
.load_forwarded_failed:
1086
        cmp     [module], 0
1087
        jnz     @f
1088
        mutex_unlock modules_mutex
1089
@@:
1090
        stdcall free, edi
1091
        xor     eax, eax
1092
        ret
6810 clevermous 1093
 
1094
        fpo_delta = fpo_delta + 4
1095
.get_module_name:
1096
        mov     esi, [module]
1097
        test    esi, esi
1098
        jnz     @f
1099
        mutex_lock modules_mutex
1100
        mov     ecx, [export_base]
1101
        call    find_module_by_addr
1102
        mutex_unlock modules_mutex
1103
@@:
1104
        mov     eax, msg_unknown
1105
        test    esi, esi
1106
        jz      @f
1107
        mov     eax, [esi+MODULE.filename]
1108
@@:
1109
        retn
6767 clevermous 1110
endp
1111
 
1112
; Resolve symbol from a module by name.
1113
; prepare_import_from_module should be called beforehand.
1114
; in: ecx = ordinal
1115
; out: eax = exported address or NULL
1116
; if [module] is zero, modules_mutex should be unlocked
1117
; if [module] is nonzero, modules_mutex should be locked
1118
proc get_exported_function_by_ordinal c uses ebx esi edi, export_base, export_ptr, export_size, module
1119
locals
1120
forward_export_base     dd      ?
1121
forward_export_ptr      dd      ?
1122
forward_export_size     dd      ?
1123
forward_export_module   dd      ?
1124
endl
1125
; 1. Validate that export directory is present at all.
1126
        mov     edx, [export_ptr]
1127
        test    edx, edx
1128
        jz      .export_ordinal_not_found
1129
; 2. Convert ordinal to index in AddressOfFunctions array.
1130
        mov     eax, ecx ; keep ecx for error message
1131
        sub     eax, [edx+IMAGE_EXPORT_DIRECTORY.Base]
1132
; 3. Validate the index.
1133
        cmp     eax, [edx+IMAGE_EXPORT_DIRECTORY.NumberOfFunctions]
1134
        jae     .export_ordinal_not_found
1135
; 4. Fetch the exported address from AddressOfFunctions array.
1136
; On success, continue to check for forwarded exports in get_exported_function_by_name.
1137
        mov     ebx, [edx+IMAGE_EXPORT_DIRECTORY.AddressOfFunctions]
1138
        add     ebx, [export_base]
1139
        mov     eax, [ebx+eax*4]
1140
        test    eax, eax
1141
        jnz     get_exported_function_by_name.check_forwarded
1142
; Generic error handler.
1143
.export_ordinal_not_found:
1144
        sub     esp, 16
1145
        fpo_delta = fpo_delta + 16
1146
; Convert ordinal to string.
1147
        lea     edi, [esp+15]
1148
        mov     byte [edi], 0
1149
@@:
1150
        mov     eax, 0xCCCCCCCD
1151
        mul     ecx
1152
        shr     edx, 3 ; edx = quotient of ecx / 10
1153
        lea     eax, [edx*5]
1154
        add     eax, eax
1155
        sub     ecx, eax ; ecx = remainder of ecx % 10
1156
        add     cl, '0'
1157
        dec     edi
1158
        mov     byte [edi], cl
1159
        mov     ecx, edx
1160
        test    edx, edx
1161
        jnz     @b
1162
; Get module name.
1163
        mov     esi, [module]
1164
        test    esi, esi
1165
        jnz     @f
1166
        mutex_lock modules_mutex
1167
        mov     ecx, [export_base]
1168
        call    find_module_by_addr
1169
        mutex_unlock modules_mutex
1170
@@:
1171
        mov     eax, msg_unknown
1172
        test    esi, esi
1173
        jz      @f
1174
        mov     eax, [esi+MODULE.filename]
1175
@@:
1176
        ccall   loader_say_error, msg_export_ordinal_not_found, edi, msg_export_not_found, eax, 0
1177
        add     esp, 16
1178
        fpo_delta = fpo_delta - 16
1179
        xor     eax, eax
1180
        ret
1181
endp