Subversion Repositories Kolibri OS

Rev

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