Subversion Repositories Kolibri OS

Rev

Rev 6614 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  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.  
  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
  45. proc fixup_pe_relocations c uses ebp, modulename
  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
  60.         cmp     byte [esi], 'M'
  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
  74.         ccall   loader_say_error, msg_noreloc1, [modulename], msg_noreloc2, 0
  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
  82.         mov     dl, byte [eax+IMAGE_NT_HEADERS.FileHeader.Characteristics]
  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:
  87.         cmp     [eax+IMAGE_DATA_DIRECTORY.isize], 0
  88.         jz      .norelocs
  89.         mov     edi, [eax+IMAGE_DATA_DIRECTORY.VirtualAddress]
  90.         push    -1
  91.         push    -1
  92.         push    [eax+IMAGE_DATA_DIRECTORY.isize]
  93. virtual at esp
  94. .sizeleft       dd      ?
  95. .next_page_original_access      dd      ?
  96. .next_page_addr dd      ?
  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.
  104. ; Loop 2b-2i over all blocks until no more data is left.
  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
  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.
  123. ; Save the old protection, we will restore it after the block is processed.
  124. ; Ignore any error.
  125. ; Go to 2f after.
  126. PROT_READ = 1
  127. PROT_WRITE = 2
  128. PROT_EXEC = 4
  129.         push    ecx
  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
  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:
  144.         push    eax
  145.         fpo_delta = fpo_delta + 4
  146. ; 2g. Block data is an array of word values. Repeat 2h for every of those.
  147. .relocloop:
  148.         sub     ecx, 2
  149.         jb      .relocdone
  150. ; 2h. Every value consists of a 4-bit type and 12-bit offset in the page.
  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
  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:
  179.         add     [edx+ebx], ebp
  180.         jmp     .relocloop
  181. .relocdone:
  182. ; 2i. Restore memory protection changed in 2d.
  183.         pop     ecx
  184.         fpo_delta = fpo_delta - 4
  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
  193.         fpo_delta = fpo_delta - 4
  194. .pagedone:
  195.         cmp     [.sizeleft+fpo_delta], 0
  196.         jnz     .pageloop
  197.         lea     eax, [.next_page_original_access+fpo_delta]
  198.         call    .restore_old_access
  199.         add     esp, 12
  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.
  204. ; It's a warning, not an error, so don't call loader_say_error.
  205.         mov     ecx, msg_relocated1
  206.         call    sys_msg_board_str
  207.         mov     ecx, [modulename]
  208.         call    sys_msg_board_str
  209.         mov     ecx, msg_relocated2
  210.         call    sys_msg_board_str
  211.         clc
  212.         ret
  213. .badreloc:
  214.         pop     ecx
  215.         pop     esi
  216.         add     esp, 12
  217.         ccall   loader_say_error, msg_bad_relocation, [modulename], 0
  218.         stc
  219.         ret
  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
  232. endp
  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 "<module>.<function>"
  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 <function>
  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 -> "#<ordinal>".
  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
  1132.