Subversion Repositories Kolibri OS

Rev

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