forked from KolibriOS/kolibrios
142 lines
5.0 KiB
PHP
142 lines
5.0 KiB
PHP
|
; Check whether PE module has been loaded at preferred address.
|
||
|
; If not, relocate the module.
|
||
|
;
|
||
|
; in: esi = PE base address
|
||
|
; in: [esp+4] = module name for debug print
|
||
|
; out: CF=1 - fail
|
||
|
proc fixup_pe_relocations uses edi ebp
|
||
|
; 1. Fetch some data from PE header or stripped PE header.
|
||
|
; We need:
|
||
|
; * ImageBase - preferred address, compare with esi = actual load address;
|
||
|
; ebp will keep the delta
|
||
|
; * RVA and size of fixups directory
|
||
|
; * flag IMAGE_FILE_RELOCS_STRIPPED from Characteristics
|
||
|
; If the actual address equals the preferred address, do nothing.
|
||
|
; If fixups directory is present, proceed to 2.
|
||
|
; If there is no fixups directory, there are two options:
|
||
|
; * either the directory has not been created
|
||
|
; * or the module has no fixups (data-only module, for example).
|
||
|
; In the first case, IMAGE_FILE_RELOCS_STRIPPED is set, and this is an error.
|
||
|
; In the second case, IMAGE_FILE_RELOCS_STRIPPED is not set; do nothing.
|
||
|
mov ebp, esi
|
||
|
cmp word [esi], 'MZ'
|
||
|
jz .parse_mz
|
||
|
sub ebp, [esi+STRIPPED_PE_HEADER.ImageBase]
|
||
|
jnz @f
|
||
|
.nothing:
|
||
|
ret
|
||
|
@@:
|
||
|
mov dl, byte [esi+STRIPPED_PE_HEADER.Characteristics]
|
||
|
lea eax, [esi+sizeof.STRIPPED_PE_HEADER+SPE_DIRECTORY_BASERELOC*sizeof.IMAGE_DATA_DIRECTORY]
|
||
|
cmp [esi+STRIPPED_PE_HEADER.NumberOfRvaAndSizes], SPE_DIRECTORY_BASERELOC
|
||
|
ja .common
|
||
|
.norelocs:
|
||
|
test dl, IMAGE_FILE_RELOCS_STRIPPED
|
||
|
jz .nothing
|
||
|
stc
|
||
|
ret
|
||
|
.parse_mz:
|
||
|
mov eax, [esi+3Ch]
|
||
|
add eax, esi
|
||
|
sub ebp, [eax+IMAGE_NT_HEADERS.OptionalHeader.ImageBase]
|
||
|
jz .nothing
|
||
|
mov dl, byte [esi+IMAGE_NT_HEADERS.FileHeader.Characteristics]
|
||
|
cmp [eax+IMAGE_NT_HEADERS.OptionalHeader.NumberOfDirectories], IMAGE_DIRECTORY_ENTRY_BASERELOC
|
||
|
jbe .norelocs
|
||
|
add eax, IMAGE_NT_HEADERS.OptionalHeader.DataDirectory+IMAGE_DIRECTORY_ENTRY_BASERELOC*sizeof.IMAGE_DATA_DIRECTORY
|
||
|
.common:
|
||
|
mov edi, [eax+IMAGE_DATA_DIRECTORY.VirtualAddress]
|
||
|
push [eax+IMAGE_DATA_DIRECTORY.isize]
|
||
|
virtual at esp
|
||
|
.sizeleft dd ?
|
||
|
end virtual
|
||
|
add edi, esi
|
||
|
cmp [.sizeleft], 0
|
||
|
jz .norelocs
|
||
|
; 2. We need to relocate and we have the relocation table.
|
||
|
; esi = PE base address
|
||
|
; edi = pointer to current data of relocation table
|
||
|
; 2a. Relocation table is organized into blocks describing every page.
|
||
|
; End of table is defined from table size fetched from the header.
|
||
|
; Loop 2b-2g over all blocks until no more data is left.
|
||
|
.pageloop:
|
||
|
; 2b. Load the header of the current block: address and size.
|
||
|
; Advance total size.
|
||
|
; Size in the block includes size of the header, subtract it.
|
||
|
; If there is no data in this block, go to 2g.
|
||
|
mov edx, [edi+IMAGE_BASE_RELOCATION.VirtualAddress]
|
||
|
mov ecx, [edi+IMAGE_BASE_RELOCATION.SizeOfBlock]
|
||
|
sub [.sizeleft], ecx
|
||
|
add edi, sizeof.IMAGE_BASE_RELOCATION
|
||
|
sub ecx, sizeof.IMAGE_BASE_RELOCATION
|
||
|
jbe .pagedone
|
||
|
; 2c. We are going to modify data, so mprotect the current page to be writable.
|
||
|
; Save the old protection, we will restore it after the block is processed.
|
||
|
; Ignore any error.
|
||
|
PROT_READ = 1
|
||
|
PROT_WRITE = 2
|
||
|
PROT_EXEC = 4
|
||
|
push esi ecx
|
||
|
mov eax, 68
|
||
|
mov ebx, 30
|
||
|
mov ecx, PROT_READ+PROT_WRITE
|
||
|
add edx, esi
|
||
|
mov esi, 0x1000
|
||
|
call FS_SYSCALL_PTR
|
||
|
pop ecx
|
||
|
push eax
|
||
|
; 2d. Block data is an array of word values. Repeat 2e for every of those.
|
||
|
.relocloop:
|
||
|
sub ecx, 2
|
||
|
jb .relocdone
|
||
|
; 2e. Every value consists of a 4-bit type and 12-bit offset in the page.
|
||
|
; x86 uses two types: 0 = no data (used for padding), 3 = 32-bit relative.
|
||
|
movzx eax, word [edi]
|
||
|
add edi, 2
|
||
|
mov ebx, eax
|
||
|
and ebx, 0xFFF
|
||
|
shr eax, 12
|
||
|
jz .relocloop
|
||
|
cmp al, IMAGE_REL_BASED_HIGHLOW
|
||
|
jnz .badreloc
|
||
|
add [edx+ebx], ebp
|
||
|
jmp .relocloop
|
||
|
.relocdone:
|
||
|
; 2f. Restore memory protection changed in 2c.
|
||
|
pop ecx
|
||
|
cmp ecx, -1
|
||
|
jz @f
|
||
|
mov eax, 68
|
||
|
mov ebx, 30
|
||
|
mov esi, 0x1000
|
||
|
call FS_SYSCALL_PTR
|
||
|
@@:
|
||
|
pop esi
|
||
|
.pagedone:
|
||
|
cmp [.sizeleft], 0
|
||
|
jnz .pageloop
|
||
|
pop eax ; pop .sizeleft
|
||
|
; 3. For performance reasons, relocation should be avoided
|
||
|
; by choosing an appropriate preferred address.
|
||
|
; If we have actually relocated something, yell to the debug board,
|
||
|
; so the programmer can notice that.
|
||
|
mov ecx, msg_relocated1
|
||
|
call sys_msg_board_str
|
||
|
mov ecx, [esp+4]
|
||
|
call sys_msg_board_str
|
||
|
mov ecx, msg_relocated2
|
||
|
call sys_msg_board_str
|
||
|
clc
|
||
|
ret
|
||
|
.badreloc:
|
||
|
pop eax
|
||
|
mov ecx, msg_bad_relocation1
|
||
|
call sys_msg_board_str
|
||
|
mov ecx, [esp+4]
|
||
|
call sys_msg_board_str
|
||
|
mov ecx, msg_newline
|
||
|
call sys_msg_board_str
|
||
|
stc
|
||
|
ret
|
||
|
endp
|