forked from KolibriOS/kolibrios
import processing for the system library
git-svn-id: svn://kolibrios.org@6767 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
parent
99341c5016
commit
a5d8ff9f45
@ -1,2 +1,3 @@
|
|||||||
if tup.getconfig("NO_FASM") ~= "" then return end
|
if tup.getconfig("NO_FASM") ~= "" then return end
|
||||||
tup.rule("kolibri.asm", "fasm %f %o " .. tup.getconfig("KPACK_CMD"), "kolibri.dll")
|
ROOT="../../../.."
|
||||||
|
tup.rule("kolibri.asm", "fasm %f %o " .. tup.getconfig("PESTRIP_CMD") .. tup.getconfig("KPACK_CMD"), "kolibri.dll")
|
||||||
|
@ -16,9 +16,13 @@ local loc,regcount
|
|||||||
parmbase@proc equ esp+4+loc+regcount*4
|
parmbase@proc equ esp+4+loc+regcount*4
|
||||||
localbase@proc equ esp
|
localbase@proc equ esp
|
||||||
fpo_localsize = loc
|
fpo_localsize = loc
|
||||||
|
fpo_delta = 0
|
||||||
}
|
}
|
||||||
macro fpo_epilogue procname,flag,parmbytes,localbytes,reglist
|
macro fpo_epilogue procname,flag,parmbytes,localbytes,reglist
|
||||||
{
|
{
|
||||||
|
if fpo_localsize
|
||||||
|
add esp, fpo_localsize
|
||||||
|
end if
|
||||||
irps reg, reglist \{ reverse pop reg \}
|
irps reg, reglist \{ reverse pop reg \}
|
||||||
if flag and 10000b
|
if flag and 10000b
|
||||||
retn
|
retn
|
||||||
@ -26,3 +30,57 @@ macro fpo_epilogue procname,flag,parmbytes,localbytes,reglist
|
|||||||
retn parmbytes
|
retn parmbytes
|
||||||
end if
|
end if
|
||||||
}
|
}
|
||||||
|
macro deflocal@proc name,def,[val]
|
||||||
|
{
|
||||||
|
common
|
||||||
|
deflocal@proc name#_unique_suffix,def,val
|
||||||
|
all@vars equ all@vars,name
|
||||||
|
name equ name#_unique_suffix+fpo_delta
|
||||||
|
}
|
||||||
|
macro defargs@proc [arg]
|
||||||
|
{
|
||||||
|
common
|
||||||
|
rawargs equ
|
||||||
|
srcargs equ arg
|
||||||
|
forward
|
||||||
|
rawargs equ rawargs,arg#_unique_suffix
|
||||||
|
common
|
||||||
|
match =,tmp,rawargs \{
|
||||||
|
defargs@proc tmp
|
||||||
|
uniqargs equ args@proc
|
||||||
|
restore args@proc
|
||||||
|
args@proc equ uniqargs,srcargs
|
||||||
|
\}
|
||||||
|
forward
|
||||||
|
arg equ arg#_unique_suffix+fpo_delta
|
||||||
|
}
|
||||||
|
macro stdcall proc,[arg] ; directly call STDCALL procedure
|
||||||
|
{ common
|
||||||
|
fpo_delta_base = fpo_delta
|
||||||
|
if ~ arg eq
|
||||||
|
reverse
|
||||||
|
pushd arg
|
||||||
|
fpo_delta = fpo_delta + 4
|
||||||
|
common
|
||||||
|
end if
|
||||||
|
call proc
|
||||||
|
fpo_delta = fpo_delta_base
|
||||||
|
}
|
||||||
|
macro ccall proc,[arg] ; directly call CDECL procedure
|
||||||
|
{ common
|
||||||
|
fpo_delta_base = fpo_delta
|
||||||
|
size@ccall = 0
|
||||||
|
if ~ arg eq
|
||||||
|
reverse
|
||||||
|
pushd arg
|
||||||
|
fpo_delta = fpo_delta + 4
|
||||||
|
size@ccall = size@ccall+4
|
||||||
|
common
|
||||||
|
end if
|
||||||
|
call proc
|
||||||
|
if size@ccall
|
||||||
|
add esp, size@ccall
|
||||||
|
end if
|
||||||
|
fpo_delta = fpo_delta_base
|
||||||
|
}
|
||||||
|
fpo_delta = 0
|
||||||
|
@ -10,7 +10,6 @@ section '.text' code readable executable
|
|||||||
FS_STACK_MAX equ dword [fs:4]
|
FS_STACK_MAX equ dword [fs:4]
|
||||||
FS_STACK_MIN equ dword [fs:8]
|
FS_STACK_MIN equ dword [fs:8]
|
||||||
FS_SELF_PTR equ dword [fs:0x18]
|
FS_SELF_PTR equ dword [fs:0x18]
|
||||||
FS_PROCESS_DATA equ dword [fs:0x30]
|
|
||||||
FS_ERRNO equ dword [fs:0x34]
|
FS_ERRNO equ dword [fs:0x34]
|
||||||
FS_SYSCALL_PTR equ dword [fs:0xC0]
|
FS_SYSCALL_PTR equ dword [fs:0xC0]
|
||||||
|
|
||||||
@ -40,8 +39,10 @@ command_line dd ?
|
|||||||
environment dd ?
|
environment dd ?
|
||||||
ends
|
ends
|
||||||
|
|
||||||
|
include 'sync.inc'
|
||||||
include 'malloc.inc'
|
include 'malloc.inc'
|
||||||
include 'peloader.inc'
|
include 'peloader.inc'
|
||||||
|
include 'modules.inc'
|
||||||
include 'cmdline.inc'
|
include 'cmdline.inc'
|
||||||
|
|
||||||
proc syscall_int40
|
proc syscall_int40
|
||||||
@ -75,10 +76,6 @@ prologue@proc equ fpo_prologue
|
|||||||
epilogue@proc equ fpo_epilogue
|
epilogue@proc equ fpo_epilogue
|
||||||
|
|
||||||
proc start stdcall, dll_base, reason, reserved
|
proc start stdcall, dll_base, reason, reserved
|
||||||
locals
|
|
||||||
exe_base dd ?
|
|
||||||
exe_path_size dd ?
|
|
||||||
endl
|
|
||||||
; 1. Do nothing unless called by the kernel for DLL_PROCESS_ATTACH.
|
; 1. Do nothing unless called by the kernel for DLL_PROCESS_ATTACH.
|
||||||
cmp [reason], DLL_PROCESS_ATTACH
|
cmp [reason], DLL_PROCESS_ATTACH
|
||||||
jnz .nothing
|
jnz .nothing
|
||||||
@ -112,15 +109,8 @@ endl
|
|||||||
call fixup_pe_relocations
|
call fixup_pe_relocations
|
||||||
pop ecx
|
pop ecx
|
||||||
jc .die
|
jc .die
|
||||||
; 2d. Allocate process data.
|
; 2d. Initialize process heap.
|
||||||
mov eax, 68
|
|
||||||
mov ebx, 12
|
|
||||||
mov ecx, 0x1000
|
|
||||||
call FS_SYSCALL_PTR
|
|
||||||
mov FS_PROCESS_DATA, eax
|
|
||||||
; 2e. Initialize process heap.
|
|
||||||
mov eax, [ebp+kernel_init_data.exe_base]
|
mov eax, [ebp+kernel_init_data.exe_base]
|
||||||
mov [exe_base], eax
|
|
||||||
mov edx, [eax+STRIPPED_PE_HEADER.SizeOfHeapReserve]
|
mov edx, [eax+STRIPPED_PE_HEADER.SizeOfHeapReserve]
|
||||||
cmp word [eax], 'MZ'
|
cmp word [eax], 'MZ'
|
||||||
jnz @f
|
jnz @f
|
||||||
@ -128,6 +118,43 @@ endl
|
|||||||
mov edx, [eax+IMAGE_NT_HEADERS.OptionalHeader.SizeOfHeapReserve]
|
mov edx, [eax+IMAGE_NT_HEADERS.OptionalHeader.SizeOfHeapReserve]
|
||||||
@@:
|
@@:
|
||||||
malloc_init
|
malloc_init
|
||||||
|
; 2e. Allocate and fill MODULE structs for main exe and kolibri.dll.
|
||||||
|
mov eax, [ebp+kernel_init_data.exe_path]
|
||||||
|
@@:
|
||||||
|
inc eax
|
||||||
|
cmp byte [eax-1], 0
|
||||||
|
jnz @b
|
||||||
|
sub eax, [ebp+kernel_init_data.exe_path]
|
||||||
|
push eax
|
||||||
|
add eax, sizeof.MODULE
|
||||||
|
stdcall malloc, eax
|
||||||
|
test eax, eax
|
||||||
|
jz .die
|
||||||
|
mov ebx, eax
|
||||||
|
stdcall malloc, sizeof.MODULE + kolibri_dll.size
|
||||||
|
test eax, eax
|
||||||
|
jz .die
|
||||||
|
mov edx, modules_list
|
||||||
|
mov [edx+MODULE.next], ebx
|
||||||
|
mov [ebx+MODULE.next], eax
|
||||||
|
mov [eax+MODULE.next], edx
|
||||||
|
mov [edx+MODULE.prev], eax
|
||||||
|
mov [eax+MODULE.prev], ebx
|
||||||
|
mov [ebx+MODULE.prev], edx
|
||||||
|
push esi
|
||||||
|
mov esi, kolibri_dll
|
||||||
|
mov ecx, kolibri_dll.size
|
||||||
|
lea edi, [eax+MODULE.path]
|
||||||
|
rep movsb
|
||||||
|
pop esi
|
||||||
|
call init_module_struct
|
||||||
|
mov eax, ebx
|
||||||
|
mov esi, [ebp+kernel_init_data.exe_path]
|
||||||
|
pop ecx
|
||||||
|
lea edi, [ebx+MODULE.path]
|
||||||
|
rep movsb
|
||||||
|
mov esi, [ebp+kernel_init_data.exe_base]
|
||||||
|
call init_module_struct
|
||||||
; 2f. Copy rest of init struct and free memory.
|
; 2f. Copy rest of init struct and free memory.
|
||||||
; Parse command line to argc/argv here and move arguments to the heap
|
; Parse command line to argc/argv here and move arguments to the heap
|
||||||
; in order to save memory: init struct and heap use different pages,
|
; in order to save memory: init struct and heap use different pages,
|
||||||
@ -138,13 +165,6 @@ endl
|
|||||||
mov FS_STACK_MIN, eax
|
mov FS_STACK_MIN, eax
|
||||||
add eax, [ebp+kernel_init_data.stack_size]
|
add eax, [ebp+kernel_init_data.stack_size]
|
||||||
mov FS_STACK_MAX, eax
|
mov FS_STACK_MAX, eax
|
||||||
mov eax, [ebp+kernel_init_data.exe_path]
|
|
||||||
@@:
|
|
||||||
inc eax
|
|
||||||
cmp byte [eax-1], 0
|
|
||||||
jnz @b
|
|
||||||
sub eax, [ebp+kernel_init_data.exe_path]
|
|
||||||
mov [exe_path_size], eax
|
|
||||||
mov esi, [ebp+kernel_init_data.command_line]
|
mov esi, [ebp+kernel_init_data.command_line]
|
||||||
xor edx, edx
|
xor edx, edx
|
||||||
xor edi, edi
|
xor edi, edi
|
||||||
@ -156,16 +176,16 @@ endl
|
|||||||
mov [.argc], ebx
|
mov [.argc], ebx
|
||||||
sub esi, [ebp+kernel_init_data.command_line]
|
sub esi, [ebp+kernel_init_data.command_line]
|
||||||
lea esi, [esi+(ebx+1)*4]
|
lea esi, [esi+(ebx+1)*4]
|
||||||
add esi, [exe_path_size]
|
|
||||||
stdcall malloc, esi
|
stdcall malloc, esi
|
||||||
|
test eax, eax
|
||||||
|
jz .die
|
||||||
mov [.argv], eax
|
mov [.argv], eax
|
||||||
mov edx, eax
|
mov edx, eax
|
||||||
lea edi, [eax+ebx*4]
|
lea edi, [eax+(ebx+1)*4]
|
||||||
mov esi, [ebp+kernel_init_data.exe_path]
|
mov eax, [modules_list + MODULE.next]
|
||||||
mov [edx], edi
|
add eax, MODULE.path
|
||||||
|
mov [edx], eax
|
||||||
add edx, 4
|
add edx, 4
|
||||||
mov ecx, [exe_path_size]
|
|
||||||
rep movsb
|
|
||||||
mov esi, [ebp+kernel_init_data.command_line]
|
mov esi, [ebp+kernel_init_data.command_line]
|
||||||
call parse_cmdline
|
call parse_cmdline
|
||||||
and dword [edx], 0 ; argv[argc] = NULL
|
and dword [edx], 0 ; argv[argc] = NULL
|
||||||
@ -174,23 +194,56 @@ endl
|
|||||||
mov ebx, 13
|
mov ebx, 13
|
||||||
mov ecx, ebp
|
mov ecx, ebp
|
||||||
call FS_SYSCALL_PTR
|
call FS_SYSCALL_PTR
|
||||||
|
; 2g. Initialize mutex for list of MODULEs.
|
||||||
|
mov ecx, modules_mutex
|
||||||
|
call mutex_init
|
||||||
|
; 2h. For console applications, call console.dll!con_init with default parameters.
|
||||||
|
mov eax, [modules_list + MODULE.next]
|
||||||
|
mov esi, [eax+MODULE.base]
|
||||||
|
mov al, [esi+STRIPPED_PE_HEADER.Subsystem]
|
||||||
|
cmp byte [esi], 'M'
|
||||||
|
jnz @f
|
||||||
|
mov eax, [esi+3Ch]
|
||||||
|
mov al, byte [esi+eax+IMAGE_NT_HEADERS.OptionalHeader.Subsystem]
|
||||||
|
@@:
|
||||||
|
cmp al, IMAGE_SUBSYSTEM_WINDOWS_CUI
|
||||||
|
jnz .noconsole
|
||||||
|
stdcall dlopen, console_dll, 0
|
||||||
|
test eax, eax
|
||||||
|
jz .noconsole
|
||||||
|
stdcall dlsym, eax, con_init_str
|
||||||
|
test eax, eax
|
||||||
|
jz .noconsole
|
||||||
|
mov edx, [modules_list + MODULE.next]
|
||||||
|
stdcall eax, -1, -1, -1, -1, [edx+MODULE.filename]
|
||||||
|
.noconsole:
|
||||||
; 3. Configure modules: main EXE and possible statically linked DLLs.
|
; 3. Configure modules: main EXE and possible statically linked DLLs.
|
||||||
mov esi, [exe_base]
|
mov eax, [modules_list + MODULE.next]
|
||||||
mov eax, [.argv]
|
mov esi, [eax+MODULE.base]
|
||||||
pushd [eax]
|
add eax, MODULE.path
|
||||||
|
push eax
|
||||||
call fixup_pe_relocations
|
call fixup_pe_relocations
|
||||||
pop ecx
|
pop ecx
|
||||||
jc .die
|
jc .die
|
||||||
|
mutex_lock modules_mutex
|
||||||
|
mov esi, [modules_list + MODULE.next]
|
||||||
|
call resolve_pe_imports
|
||||||
|
mov ebx, eax
|
||||||
|
mutex_unlock modules_mutex
|
||||||
|
test ebx, ebx
|
||||||
|
jnz .die
|
||||||
; 4. Call exe entry point.
|
; 4. Call exe entry point.
|
||||||
|
mov esi, [esi+MODULE.base]
|
||||||
mov edx, [esi+STRIPPED_PE_HEADER.AddressOfEntryPoint]
|
mov edx, [esi+STRIPPED_PE_HEADER.AddressOfEntryPoint]
|
||||||
cmp word [esi], 'MZ'
|
cmp byte [esi], 'M'
|
||||||
jnz @f
|
jnz @f
|
||||||
mov ecx, [esi+IMAGE_DOS_HEADER.e_lfanew]
|
mov ecx, [esi+IMAGE_DOS_HEADER.e_lfanew]
|
||||||
add ecx, esi
|
add ecx, esi
|
||||||
mov edx, [ecx+IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint]
|
mov edx, [ecx+IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint]
|
||||||
@@:
|
@@:
|
||||||
add edx, esi
|
add edx, esi
|
||||||
add esp, fpo_localsize+4
|
pop ecx
|
||||||
|
mov [process_initialized], 1
|
||||||
call edx
|
call edx
|
||||||
; If exe entry point has returned control, die.
|
; If exe entry point has returned control, die.
|
||||||
jmp .die
|
jmp .die
|
||||||
@ -246,18 +299,46 @@ export 'kolibri.dll' \
|
|||||||
, mspace_realloc, 'mspace_realloc' \
|
, mspace_realloc, 'mspace_realloc' \
|
||||||
, mspace_realloc_in_place, 'mspace_realloc_in_place' \
|
, mspace_realloc_in_place, 'mspace_realloc_in_place' \
|
||||||
, mspace_memalign, 'mspace_memalign' \
|
, mspace_memalign, 'mspace_memalign' \
|
||||||
|
, dlopen, 'dlopen' \
|
||||||
|
, dlclose, 'dlclose' \
|
||||||
|
, dlsym, 'dlsym' \
|
||||||
|
|
||||||
end data
|
end data
|
||||||
|
|
||||||
kolibri_dll db 'kolibri.dll',0
|
kolibri_dll db '/rd/1/lib/kolibri.dll',0
|
||||||
|
.size = $ - kolibri_dll
|
||||||
|
|
||||||
|
console_dll db 'console.dll',0
|
||||||
|
con_init_str db 'con_init',0
|
||||||
|
|
||||||
msg_version_mismatch db 'S : Version mismatch between kernel and kolibri.dll',13,10,0
|
msg_version_mismatch db 'S : Version mismatch between kernel and kolibri.dll',13,10,0
|
||||||
msg_bad_relocation1 db 'S : Bad relocation type in ',0
|
msg_bad_relocation db 'Bad relocation type in ',0
|
||||||
msg_newline db 13,10,0
|
msg_newline db 13,10,0
|
||||||
msg_relocated1 db 'S : fixups for ',0
|
msg_relocated1 db 'S : fixups for ',0
|
||||||
msg_relocated2 db ' applied',13,10,0
|
msg_relocated2 db ' applied',13,10,0
|
||||||
|
msg_noreloc1 db 'Module ',0
|
||||||
|
msg_noreloc2 db ' is not at preferred base and has no fixups',0
|
||||||
|
loader_debugboard_prefix db 'S : ',0
|
||||||
|
notify_program db '/rd/1/@notify',0
|
||||||
|
msg_cannot_open db 'Cannot open ',0
|
||||||
|
msg_paths_begin db ' in any of '
|
||||||
|
|
||||||
|
module_path1 db '/rd/1/lib/'
|
||||||
|
.size = $ - module_path1
|
||||||
|
db ', '
|
||||||
|
module_path2 db '/kolibrios/lib/'
|
||||||
|
.size = $ - module_path2
|
||||||
|
db ', ',0
|
||||||
|
msg_export_name_not_found db 'Exported function ',0
|
||||||
|
msg_export_ordinal_not_found db 'Exported ordinal #',0
|
||||||
|
msg_export_not_found db ' not found in module ',0
|
||||||
|
msg_unknown db '<unknown>',0
|
||||||
|
|
||||||
if FOOTERS
|
|
||||||
section '.data' data readable writable
|
section '.data' data readable writable
|
||||||
|
if FOOTERS
|
||||||
malloc_magic dd ?
|
malloc_magic dd ?
|
||||||
end if
|
end if
|
||||||
|
default_heap dd ?
|
||||||
|
modules_list rd 2
|
||||||
|
modules_mutex MUTEX
|
||||||
|
process_initialized db ?
|
||||||
|
@ -98,8 +98,7 @@ endp
|
|||||||
|
|
||||||
macro set_default_heap
|
macro set_default_heap
|
||||||
{
|
{
|
||||||
mov ebp, FS_PROCESS_DATA
|
mov ebp, [default_heap]
|
||||||
mov ebp, [ebp+0x18]
|
|
||||||
.got_mspace:
|
.got_mspace:
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,8 +294,7 @@ if FOOTERS
|
|||||||
mov [malloc_magic], eax
|
mov [malloc_magic], eax
|
||||||
end if
|
end if
|
||||||
stdcall create_mspace, edx, 1
|
stdcall create_mspace, edx, 1
|
||||||
mov ecx, FS_PROCESS_DATA
|
mov [default_heap], eax
|
||||||
mov [ecx+0x18], eax
|
|
||||||
}
|
}
|
||||||
|
|
||||||
proc heap_corrupted
|
proc heap_corrupted
|
||||||
@ -317,7 +315,7 @@ proc heap_corrupted
|
|||||||
jz @f
|
jz @f
|
||||||
call FS_SYSCALL_PTR
|
call FS_SYSCALL_PTR
|
||||||
inc esi
|
inc esi
|
||||||
cmp esi, ebx
|
cmp esi, edx
|
||||||
jb @b
|
jb @b
|
||||||
@@:
|
@@:
|
||||||
mov esi, heap_corrupted_msg
|
mov esi, heap_corrupted_msg
|
||||||
|
@ -444,5 +444,5 @@ logfile_mode db 'w',0
|
|||||||
align 4
|
align 4
|
||||||
logfile dd ?
|
logfile dd ?
|
||||||
errno dd ?
|
errno dd ?
|
||||||
FS_PROCESS_DATA = process_data
|
default_heap dd ?
|
||||||
process_data rd 1024
|
process_data rd 1024
|
||||||
|
610
programs/system/os/modules.inc
Normal file
610
programs/system/os/modules.inc
Normal file
@ -0,0 +1,610 @@
|
|||||||
|
; Module management, non-PE-specific code.
|
||||||
|
; Works in conjuction with peloader.inc for PE-specific code.
|
||||||
|
|
||||||
|
; void* dlopen(const char* filename, int mode)
|
||||||
|
; Opens the module named filename and maps it in; returns a handle that can be
|
||||||
|
; passed to dlsym to get symbol values from it.
|
||||||
|
;
|
||||||
|
; If filename starts with '/', it is treated as an absolute file name.
|
||||||
|
; Otherwise, dlopen searches for filename in predefined locations:
|
||||||
|
; /rd/1/lib, /kolibrios/lib, directory of the executable file.
|
||||||
|
; The current directory is *not* searched.
|
||||||
|
;
|
||||||
|
; If the same module is loaded again with dlopen(), the same
|
||||||
|
; handle is returned. The loader maintains reference
|
||||||
|
; counts for loaded modules, so a dynamically loaded module is
|
||||||
|
; not deallocated until dlclose() has been called on it as many times
|
||||||
|
; as dlopen() has succeeded on it. Any initialization functions
|
||||||
|
; are called just once.
|
||||||
|
;
|
||||||
|
; If dlopen() fails for any reason, it returns NULL.
|
||||||
|
;
|
||||||
|
; mode is reserved and should be zero.
|
||||||
|
proc dlopen stdcall uses esi edi, file, mode
|
||||||
|
; find_module_by_name and load_module do all the work.
|
||||||
|
; We just need to acquire/release the mutex and adjust input/output.
|
||||||
|
cmp [mode], 0
|
||||||
|
jnz .invalid_mode
|
||||||
|
mutex_lock modules_mutex
|
||||||
|
mov edi, [file]
|
||||||
|
call find_module_by_name
|
||||||
|
test esi, esi
|
||||||
|
jnz .inc_refcount
|
||||||
|
call load_module
|
||||||
|
xor edi, edi
|
||||||
|
test eax, eax
|
||||||
|
jz .unlock_return
|
||||||
|
; The handle returned on success is module base address.
|
||||||
|
; Unlike pointer to MODULE struct, it can be actually useful
|
||||||
|
; for the caller as is.
|
||||||
|
mov edi, [eax+MODULE.base]
|
||||||
|
jmp .unlock_return
|
||||||
|
.inc_refcount:
|
||||||
|
inc [esi+MODULE.refcount]
|
||||||
|
mov edi, [esi+MODULE.base]
|
||||||
|
.unlock_return:
|
||||||
|
mutex_unlock modules_mutex
|
||||||
|
mov eax, edi
|
||||||
|
ret
|
||||||
|
.invalid_mode:
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; int dlclose(void* handle)
|
||||||
|
; Decrements the reference count on the dynamically loaded module
|
||||||
|
; referred to by handle. If the reference count drops to zero,
|
||||||
|
; then the module is unloaded. All modules that were automatically loaded
|
||||||
|
; when dlopen() was invoked on the module referred to by handle are
|
||||||
|
; recursively closed in the same manner.
|
||||||
|
;
|
||||||
|
; A successful return from dlclose() does not guarantee that the
|
||||||
|
; module has been actually removed from the caller's address space.
|
||||||
|
; In addition to references resulting from explicit dlopen() calls,
|
||||||
|
; a module may have been implicitly loaded (and reference counted)
|
||||||
|
; because of dependencies in other shared objects.
|
||||||
|
; Only when all references have been released can the module be removed
|
||||||
|
; from the address space.
|
||||||
|
; On success, dlclose() returns 0; on error, it returns a nonzero value.
|
||||||
|
proc dlclose stdcall uses esi, handle
|
||||||
|
; This function uses two worker functions:
|
||||||
|
; find_module_by_addr to map handle -> MODULE,
|
||||||
|
; dereference_module for the main work.
|
||||||
|
; Aside of calling these, we should only acquire/release the mutex.
|
||||||
|
mutex_lock modules_mutex
|
||||||
|
mov ecx, [handle]
|
||||||
|
call find_module_by_addr
|
||||||
|
test esi, esi
|
||||||
|
jz .invalid_handle
|
||||||
|
call dereference_module
|
||||||
|
mutex_unlock modules_mutex
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
.invalid_handle:
|
||||||
|
mutex_unlock modules_mutex
|
||||||
|
xor eax, eax
|
||||||
|
inc eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; void* dlsym(void* handle, const char* symbol)
|
||||||
|
; Obtains address of a symbol in a module.
|
||||||
|
; On failure, returns NULL.
|
||||||
|
;
|
||||||
|
; symbol can also be a number between 0 and 0xFFFF;
|
||||||
|
; it is interpreted as an ordinal of a symbol.
|
||||||
|
; Low 64K of address space are blocked for the allocation,
|
||||||
|
; so a valid pointer cannot be less than 0x10000.
|
||||||
|
;
|
||||||
|
; handle is not validated. Passing an invalid handle can result in a crash.
|
||||||
|
proc dlsym stdcall, handle, symbol
|
||||||
|
locals
|
||||||
|
export_base dd ?
|
||||||
|
export_ptr dd ?
|
||||||
|
export_size dd ?
|
||||||
|
import_module dd 0
|
||||||
|
endl
|
||||||
|
; Again, helper functions do all the work.
|
||||||
|
; We don't need to browse list of MODULEs,
|
||||||
|
; so we don't need to acquire/release the mutex.
|
||||||
|
; Unless the function is forwarded or module name is required for error message,
|
||||||
|
; but this should be processed by get_exported_function_*.
|
||||||
|
mov eax, [handle]
|
||||||
|
call prepare_import_from_module
|
||||||
|
mov ecx, [symbol]
|
||||||
|
cmp ecx, 0x10000
|
||||||
|
jb .ordinal
|
||||||
|
mov edx, -1 ; no hint for lookup in name table
|
||||||
|
call get_exported_function_by_name
|
||||||
|
ret
|
||||||
|
.ordinal:
|
||||||
|
call get_exported_function_by_ordinal
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; Errors happen.
|
||||||
|
; Some errors should be reported to the user. Some errors are normal.
|
||||||
|
; After the process has been initialized, we don't know what an error
|
||||||
|
; should mean - is the failed DLL absolutely required or unimportant enhancement?
|
||||||
|
; So we report an error to the caller and let it decide how to handle it.
|
||||||
|
; However, when the process is initializing, there is no one to report to,
|
||||||
|
; so we must inform the user ourselves.
|
||||||
|
; In any case, write to the debug board - it is *debug* board, after all.
|
||||||
|
;
|
||||||
|
; This function is called whenever an error occurs in the loader.
|
||||||
|
; Except errors in malloc/realloc - they shouldn't happen anyway,
|
||||||
|
; and if they happened after all, we are screwed and likely will fail anyway,
|
||||||
|
; so don't bother.
|
||||||
|
; Variable number of arguments: strings to be concatenated, end with NULL.
|
||||||
|
proc loader_say_error c uses ebx esi, first_msg, ...
|
||||||
|
; 1. Concatenate all given strings to the final error message.
|
||||||
|
; 1a. Calculate the total length.
|
||||||
|
xor ebx, ebx
|
||||||
|
lea edx, [first_msg]
|
||||||
|
.get_length:
|
||||||
|
mov ecx, [edx]
|
||||||
|
test ecx, ecx
|
||||||
|
jz .length_done
|
||||||
|
@@:
|
||||||
|
inc ebx
|
||||||
|
inc ecx
|
||||||
|
cmp byte [ecx-1], 0
|
||||||
|
jnz @b
|
||||||
|
dec ebx
|
||||||
|
add edx, 4
|
||||||
|
jmp .get_length
|
||||||
|
.length_done:
|
||||||
|
inc ebx ; terminating zero
|
||||||
|
; 1b. Allocate memory. Exit if failed.
|
||||||
|
stdcall malloc, ebx
|
||||||
|
test eax, eax
|
||||||
|
jz .nothing
|
||||||
|
mov esi, eax
|
||||||
|
; 1c. Copy data.
|
||||||
|
lea edx, [first_msg]
|
||||||
|
.copy_data:
|
||||||
|
mov ecx, [edx]
|
||||||
|
test ecx, ecx
|
||||||
|
jz .data_done
|
||||||
|
@@:
|
||||||
|
mov bl, [ecx]
|
||||||
|
test bl, bl
|
||||||
|
jz @f
|
||||||
|
mov [eax], bl
|
||||||
|
inc ecx
|
||||||
|
inc eax
|
||||||
|
jmp @b
|
||||||
|
@@:
|
||||||
|
add edx, 4
|
||||||
|
jmp .copy_data
|
||||||
|
.data_done:
|
||||||
|
mov byte [eax], 0 ; terminating zero
|
||||||
|
; 2. Print to the debug board.
|
||||||
|
mov ecx, loader_debugboard_prefix
|
||||||
|
call sys_msg_board_str
|
||||||
|
mov ecx, esi
|
||||||
|
call sys_msg_board_str
|
||||||
|
mov ecx, msg_newline
|
||||||
|
call sys_msg_board_str
|
||||||
|
; 3. If the initialization is in process, report to the user.
|
||||||
|
xor eax, eax
|
||||||
|
cmp [process_initialized], al
|
||||||
|
jnz .no_report
|
||||||
|
; Use @notify. Create structure for function 70.7 on the stack.
|
||||||
|
push eax ; to be rewritten with part of path
|
||||||
|
push eax ; to be rewritten with part of path
|
||||||
|
push eax ; reserved
|
||||||
|
push eax ; reserved
|
||||||
|
push esi ; command line
|
||||||
|
push eax ; flags: none
|
||||||
|
push 7
|
||||||
|
mov eax, 70
|
||||||
|
mov ebx, esp
|
||||||
|
mov dword [ebx+21], notify_program
|
||||||
|
call FS_SYSCALL_PTR
|
||||||
|
add esp, 28
|
||||||
|
; Ignore any errors. We can't do anything with them anyway.
|
||||||
|
.no_report:
|
||||||
|
stdcall free, esi
|
||||||
|
.nothing:
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; When the loader is initializing the process, errors can happen.
|
||||||
|
; They should be reported to the user.
|
||||||
|
; The main executable cannot do this, it is not initialized yet.
|
||||||
|
; So we should do it ourselves.
|
||||||
|
; However, after the process has been initialized, the main
|
||||||
|
;
|
||||||
|
; Helper function that is called whenever an error is occured.
|
||||||
|
|
||||||
|
; For now, we don't expect many modules in one process.
|
||||||
|
; So, all modules are linked into a single list,
|
||||||
|
; and lookup functions simply walk the entire list.
|
||||||
|
; This should be revisited if dozens of modules would be typical.
|
||||||
|
|
||||||
|
; This structure describes one loaded PE module.
|
||||||
|
; malloc'd from the default heap,
|
||||||
|
; includes variable-sized module path in the end.
|
||||||
|
struct MODULE
|
||||||
|
; All modules are linked in the global list with head at modules_list.
|
||||||
|
next dd ?
|
||||||
|
prev dd ?
|
||||||
|
base dd ? ; base address
|
||||||
|
size dd ? ; size in memory
|
||||||
|
refcount dd ? ; reference counter
|
||||||
|
timestamp dd ? ; for bound imports
|
||||||
|
basedelta dd ? ; base address - preferred address, for bound imports
|
||||||
|
num_imports dd ? ; size of imports array
|
||||||
|
imports dd ?
|
||||||
|
; Pointer to array of pointers to MODULEs containing imported functions.
|
||||||
|
; Used to unload all dependencies when the module is unloaded.
|
||||||
|
; Contains all modules referenced by import table;
|
||||||
|
; if the module forwards some export to another module,
|
||||||
|
; then forward target is added to this array when forward source is requested.
|
||||||
|
filename dd ? ; pointer inside path array after dirname
|
||||||
|
filenamelen dd ? ; strlen(filename) + 1
|
||||||
|
path rb 0
|
||||||
|
ends
|
||||||
|
|
||||||
|
; Fills some fields in a new MODULE struct based on given PE image.
|
||||||
|
; Assumes that MODULE.path has been filled during the allocation,
|
||||||
|
; does not insert the structure in the common list, fills everything else.
|
||||||
|
; in: eax -> MODULE
|
||||||
|
; in: esi = module base
|
||||||
|
proc init_module_struct
|
||||||
|
; Straightforward initialization of all non-PE-specific fields.
|
||||||
|
lea edx, [eax+MODULE.path]
|
||||||
|
mov [eax+MODULE.filename], edx
|
||||||
|
@@:
|
||||||
|
inc edx
|
||||||
|
cmp byte [edx-1], 0
|
||||||
|
jz @f
|
||||||
|
cmp byte [edx-1], '/'
|
||||||
|
jnz @b
|
||||||
|
mov [eax+MODULE.filename], edx
|
||||||
|
jmp @b
|
||||||
|
@@:
|
||||||
|
sub edx, [eax+MODULE.filename]
|
||||||
|
mov [eax+MODULE.filenamelen], edx
|
||||||
|
xor edx, edx
|
||||||
|
mov [eax+MODULE.base], esi
|
||||||
|
mov [eax+MODULE.refcount], 1
|
||||||
|
mov [eax+MODULE.num_imports], edx
|
||||||
|
mov [eax+MODULE.imports], edx
|
||||||
|
; Let the PE-specific part do its job.
|
||||||
|
init_module_struct_pe_specific
|
||||||
|
endp
|
||||||
|
|
||||||
|
; Helper function for dlclose and resolving forwarded exports from dlsym.
|
||||||
|
; in: ecx = module base address
|
||||||
|
; out: esi -> MODULE or esi = NULL
|
||||||
|
; modules_mutex should be locked
|
||||||
|
proc find_module_by_addr
|
||||||
|
; Simple linear lookup in the list.
|
||||||
|
mov esi, [modules_list + MODULE.next]
|
||||||
|
.scan:
|
||||||
|
cmp esi, modules_list
|
||||||
|
jz .notfound
|
||||||
|
cmp ecx, [esi+MODULE.base]
|
||||||
|
jz .found
|
||||||
|
mov esi, [esi+MODULE.next]
|
||||||
|
jmp .scan
|
||||||
|
.notfound:
|
||||||
|
xor esi, esi
|
||||||
|
.found:
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; Helper function for whenever we have a module name
|
||||||
|
; and want to check whether it is already loaded.
|
||||||
|
; in: edi -> name with or without a path
|
||||||
|
; out: esi -> MODULE or esi = NULL
|
||||||
|
; modules_mutex should be locked
|
||||||
|
proc find_module_by_name uses edi
|
||||||
|
; 1. Skip the path, if it is present.
|
||||||
|
; eax = current pointer,
|
||||||
|
; edi is updated whenever the previous character is '/'
|
||||||
|
mov eax, edi
|
||||||
|
.find_basename:
|
||||||
|
cmp byte [eax], 0
|
||||||
|
jz .found_basename
|
||||||
|
inc eax
|
||||||
|
cmp byte [eax-1], '/'
|
||||||
|
jnz .find_basename
|
||||||
|
mov edi, eax
|
||||||
|
jmp .find_basename
|
||||||
|
.found_basename:
|
||||||
|
; 2. Simple linear lookup in the list.
|
||||||
|
mov eax, [modules_list + MODULE.next]
|
||||||
|
.scan:
|
||||||
|
cmp eax, modules_list
|
||||||
|
jz .notfound
|
||||||
|
; For every module, compare base names ignoring paths.
|
||||||
|
push edi
|
||||||
|
mov esi, [eax+MODULE.filename]
|
||||||
|
mov ecx, [eax+MODULE.filenamelen]
|
||||||
|
repz cmpsb
|
||||||
|
pop edi
|
||||||
|
jz .found
|
||||||
|
mov eax, [eax+MODULE.next]
|
||||||
|
jmp .scan
|
||||||
|
.found:
|
||||||
|
mov esi, eax
|
||||||
|
ret
|
||||||
|
.notfound:
|
||||||
|
xor esi, esi
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; Called when some module is implicitly loaded by another module,
|
||||||
|
; either due to a record in import table,
|
||||||
|
; or because some exported function forwards to another module.
|
||||||
|
; Checks whether the target module has already been referenced
|
||||||
|
; by the source module. The first reference is passed down
|
||||||
|
; to load_module increasing refcount of the target and possibly
|
||||||
|
; loading it if not yet, subsequent references just return
|
||||||
|
; without modifying refcount.
|
||||||
|
; We don't actually need to deduplicate DLLs from import table
|
||||||
|
; as long as we decrement refcount on unload the same number of times
|
||||||
|
; that we have incremented it on load.
|
||||||
|
; However, we need to keep track of references to forward targets,
|
||||||
|
; and we don't want to scan the entire export table and load all forward
|
||||||
|
; targets just in case some of those would be useful,
|
||||||
|
; so load them on-demand first time and ignore subsequential references.
|
||||||
|
; To be consistent, do the same for import table too.
|
||||||
|
;
|
||||||
|
; in: esi -> source MODULE struct
|
||||||
|
; in: edi -> target module name
|
||||||
|
; out: eax -> imported MODULE, 0 on error
|
||||||
|
; modules_mutex should be locked
|
||||||
|
proc load_imported_module uses edi
|
||||||
|
; 1. Find the target module in the loaded modules list.
|
||||||
|
; If not found, go to 5.
|
||||||
|
push esi
|
||||||
|
call find_module_by_name
|
||||||
|
test esi, esi
|
||||||
|
mov eax, esi
|
||||||
|
pop esi
|
||||||
|
jz .load
|
||||||
|
; 2. The module has been already loaded.
|
||||||
|
; Now check whether it is already stored in imports array.
|
||||||
|
; If yes, just return without doing anything.
|
||||||
|
mov edi, [esi+MODULE.imports]
|
||||||
|
mov ecx, [esi+MODULE.num_imports]
|
||||||
|
test ecx, ecx
|
||||||
|
jz .newref
|
||||||
|
repnz scasd
|
||||||
|
jz .nothing
|
||||||
|
.newref:
|
||||||
|
; The module is loaded, but not by us.
|
||||||
|
; 3. Increment the reference counter of the target.
|
||||||
|
inc [eax+MODULE.refcount]
|
||||||
|
.add_to_imports:
|
||||||
|
; 4. Add the new pointer to the imports array.
|
||||||
|
; 4a. Check whether there is place in the array.
|
||||||
|
; If so, go to 4c.
|
||||||
|
; We don't want to reallocate too often, since reallocation
|
||||||
|
; may involve copying our data to a new place.
|
||||||
|
; We always reserve space that is a power of two; in this way,
|
||||||
|
; the wasted space is never greater than the used space,
|
||||||
|
; and total time of copying the data is O(number of modules).
|
||||||
|
; The last fact is not really important right now,
|
||||||
|
; since the current implementation of step 2 makes everything
|
||||||
|
; quadratic and the number of modules is very small anyway,
|
||||||
|
; but since this enhancement costs only a few instructions, why not?
|
||||||
|
mov edi, eax
|
||||||
|
; X is a power of two or zero if and only if (X and (X - 1)) is zero
|
||||||
|
mov ecx, [esi+MODULE.num_imports]
|
||||||
|
lea edx, [ecx-1]
|
||||||
|
test ecx, edx
|
||||||
|
jnz .has_space
|
||||||
|
; 4b. Reallocate the imports array:
|
||||||
|
; if the current size is zero, allocate 1 item,
|
||||||
|
; otherwise double number of items.
|
||||||
|
; Item size is 4 bytes.
|
||||||
|
lea ecx, [ecx*8]
|
||||||
|
test ecx, ecx
|
||||||
|
jnz @f
|
||||||
|
mov ecx, 4
|
||||||
|
@@:
|
||||||
|
stdcall realloc, [esi+MODULE.imports], ecx
|
||||||
|
test eax, eax
|
||||||
|
jz .realloc_failed
|
||||||
|
mov [esi+MODULE.imports], eax
|
||||||
|
mov ecx, [esi+MODULE.num_imports]
|
||||||
|
.has_space:
|
||||||
|
; 4c. Append pointer to the target MODULE to imports array.
|
||||||
|
mov eax, [esi+MODULE.imports]
|
||||||
|
mov [eax+ecx*4], edi
|
||||||
|
inc [esi+MODULE.num_imports]
|
||||||
|
mov eax, edi
|
||||||
|
.nothing:
|
||||||
|
ret
|
||||||
|
.load:
|
||||||
|
; 5. This is a totally new module. Load it.
|
||||||
|
call load_module
|
||||||
|
; On error, return it to the caller. On success, go to 4.
|
||||||
|
test eax, eax
|
||||||
|
jz .nothing
|
||||||
|
jmp .add_to_imports
|
||||||
|
.realloc_failed:
|
||||||
|
; Out of memory for a couple of dwords? Should not happen.
|
||||||
|
; Dereference the target referenced by step 3 or 5
|
||||||
|
; and return error to the caller.
|
||||||
|
push esi
|
||||||
|
mov esi, edi
|
||||||
|
call dereference_module
|
||||||
|
pop esi
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; Helper procedure for load_module.
|
||||||
|
; Allocates MODULE structure for (given path) + (module name),
|
||||||
|
; calls the kernel to map it,
|
||||||
|
; on success, fills the MODULE structure.
|
||||||
|
; in: edi -> module name
|
||||||
|
; in: ebx = strlen(filename) + 1
|
||||||
|
proc try_map_module uses ebx esi, path_ptr, path_len
|
||||||
|
; 1. Allocate MODULE structure.
|
||||||
|
mov eax, [path_len]
|
||||||
|
lea eax, [eax+ebx+MODULE.path]
|
||||||
|
stdcall malloc, eax
|
||||||
|
test eax, eax
|
||||||
|
jz .nothing
|
||||||
|
; 2. Create the full name of module in MODULE structure:
|
||||||
|
; concatenate module path, if given, and module name.
|
||||||
|
mov ecx, [path_len]
|
||||||
|
mov esi, [path_ptr]
|
||||||
|
push edi
|
||||||
|
lea edi, [eax+MODULE.path]
|
||||||
|
rep movsb
|
||||||
|
mov ecx, ebx
|
||||||
|
mov esi, [esp]
|
||||||
|
rep movsb
|
||||||
|
pop edi
|
||||||
|
mov esi, eax
|
||||||
|
; 3. Call the kernel to map the module.
|
||||||
|
lea ecx, [eax+MODULE.path]
|
||||||
|
mov eax, 68
|
||||||
|
mov ebx, 28
|
||||||
|
call FS_SYSCALL_PTR
|
||||||
|
cmp eax, -0x1000
|
||||||
|
ja .failed
|
||||||
|
; 4. On success, fill the rest of MODULE structure and return it.
|
||||||
|
xchg eax, esi
|
||||||
|
call init_module_struct
|
||||||
|
ret
|
||||||
|
.failed:
|
||||||
|
; On failure, undo allocation at step 1 and return zero.
|
||||||
|
stdcall free, esi
|
||||||
|
xor eax, eax
|
||||||
|
.nothing:
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; Worker procedure for loading a new module.
|
||||||
|
; Does not check whether the module has been already loaded;
|
||||||
|
; find_module_by_name should be called beforehand.
|
||||||
|
; in: edi -> filename
|
||||||
|
; out: eax -> MODULE or 0
|
||||||
|
; modules_mutex should be locked
|
||||||
|
proc load_module uses ebx esi ebp
|
||||||
|
; 1. Map the module.
|
||||||
|
; 1a. Prepare for try_map_module: calculate length of the name.
|
||||||
|
mov ebx, edi
|
||||||
|
@@:
|
||||||
|
inc ebx
|
||||||
|
cmp byte [ebx-1], 0
|
||||||
|
jnz @b
|
||||||
|
sub ebx, edi
|
||||||
|
; 1b. Check whether the given path is absolute.
|
||||||
|
; If so, proceed to 1c. If not, go to 1d.
|
||||||
|
cmp byte [edi], '/'
|
||||||
|
jnz .relative
|
||||||
|
; 1c. The given path is absolute. Use it as is. Don't try any other paths.
|
||||||
|
stdcall try_map_module, 0, 0
|
||||||
|
test eax, eax
|
||||||
|
jnz .loaded_ok
|
||||||
|
ccall loader_say_error, msg_cannot_open, edi, 0
|
||||||
|
jmp .load_failed
|
||||||
|
.relative:
|
||||||
|
; 1d. The given path is relative.
|
||||||
|
; Try /rd/1/lib/, /kolibrios/lib/ and path to executable
|
||||||
|
; in this order.
|
||||||
|
stdcall try_map_module, module_path1, module_path1.size
|
||||||
|
test eax, eax
|
||||||
|
jnz .loaded_ok
|
||||||
|
stdcall try_map_module, module_path2, module_path2.size
|
||||||
|
test eax, eax
|
||||||
|
jnz .loaded_ok
|
||||||
|
; Note: we assume that the executable is always the first module in the list.
|
||||||
|
mov eax, [modules_list + MODULE.next]
|
||||||
|
mov ecx, [eax+MODULE.filename]
|
||||||
|
add eax, MODULE.path
|
||||||
|
mov esi, eax
|
||||||
|
sub ecx, eax
|
||||||
|
stdcall try_map_module, eax, ecx
|
||||||
|
test eax, eax
|
||||||
|
jnz .loaded_ok
|
||||||
|
mov ebx, dword [esi+MODULE.filename-MODULE.path]
|
||||||
|
movzx eax, byte [ebx]
|
||||||
|
mov byte [ebx], 0
|
||||||
|
push eax
|
||||||
|
ccall loader_say_error, msg_cannot_open, edi, msg_paths_begin, esi, 0
|
||||||
|
pop eax
|
||||||
|
mov byte [ebx], al
|
||||||
|
.load_failed:
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
.loaded_ok:
|
||||||
|
; Module has been mapped.
|
||||||
|
; MODULE structure has been initialized, but not yet inserted in the common list.
|
||||||
|
; 2. Insert the MODULE structure in the end of the common list.
|
||||||
|
mov esi, eax
|
||||||
|
mov eax, [modules_list+MODULE.prev]
|
||||||
|
mov [eax+MODULE.next], esi
|
||||||
|
mov [esi+MODULE.prev], eax
|
||||||
|
mov [modules_list+MODULE.prev], esi
|
||||||
|
mov [esi+MODULE.next], modules_list
|
||||||
|
; 3. Call PE-specific code to initialize the mapped module.
|
||||||
|
push esi
|
||||||
|
push edi ; for messages in fixup_pe_relocations
|
||||||
|
mov esi, [esi+MODULE.base]
|
||||||
|
call fixup_pe_relocations
|
||||||
|
pop ecx
|
||||||
|
pop esi
|
||||||
|
jc .fail_unload
|
||||||
|
call resolve_pe_imports
|
||||||
|
test eax, eax
|
||||||
|
jnz .fail_unload
|
||||||
|
mov eax, esi
|
||||||
|
ret
|
||||||
|
.fail_unload:
|
||||||
|
call dereference_module
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; Worker procedure for unloading a module.
|
||||||
|
; Drops one reference to the module; if it was the last one,
|
||||||
|
; unloads the module and all referenced modules recursively.
|
||||||
|
; in: esi -> MODULE struct
|
||||||
|
; modules_mutex should be locked
|
||||||
|
proc dereference_module
|
||||||
|
; 1. Decrement reference counter.
|
||||||
|
; If the decremented value is nonzero, exit.
|
||||||
|
dec [esi+MODULE.refcount]
|
||||||
|
jnz .nothing
|
||||||
|
; 2. Remove the module from the common list.
|
||||||
|
mov eax, [esi+MODULE.prev]
|
||||||
|
mov edx, [esi+MODULE.next]
|
||||||
|
mov [eax+MODULE.next], edx
|
||||||
|
mov [edx+MODULE.prev], eax
|
||||||
|
; 3. Recursively unload dependencies.
|
||||||
|
cmp [esi+MODULE.num_imports], 0
|
||||||
|
jz .import_deref_done
|
||||||
|
.import_deref_loop:
|
||||||
|
mov eax, [esi+MODULE.num_imports]
|
||||||
|
push esi
|
||||||
|
mov esi, [esi+MODULE.imports]
|
||||||
|
mov esi, [esi+(eax-1)*4]
|
||||||
|
call dereference_module
|
||||||
|
pop esi
|
||||||
|
dec [esi+MODULE.num_imports]
|
||||||
|
jnz .import_deref_loop
|
||||||
|
.import_deref_done:
|
||||||
|
stdcall free, [esi+MODULE.imports] ; free(NULL) is ok
|
||||||
|
; 4. Unmap the module.
|
||||||
|
push ebx
|
||||||
|
mov eax, 68
|
||||||
|
mov ebx, 29
|
||||||
|
mov ecx, [esi+MODULE.base]
|
||||||
|
call FS_SYSCALL_PTR
|
||||||
|
pop ebx
|
||||||
|
; 5. Free the MODULE struct.
|
||||||
|
stdcall free, esi
|
||||||
|
.nothing:
|
||||||
|
ret
|
||||||
|
endp
|
@ -19,6 +19,9 @@ STRIPPED_PE_SIGNATURE = 0x4503 ; 'PE' xor 'S'
|
|||||||
SPE_DIRECTORY_IMPORT = 0
|
SPE_DIRECTORY_IMPORT = 0
|
||||||
SPE_DIRECTORY_EXPORT = 1
|
SPE_DIRECTORY_EXPORT = 1
|
||||||
SPE_DIRECTORY_BASERELOC = 2
|
SPE_DIRECTORY_BASERELOC = 2
|
||||||
|
SPE_DIRECTORY_EXCEPTION = 3
|
||||||
|
SPE_DIRECTORY_TLS = 4
|
||||||
|
SPE_DIRECTORY_BOUND_IMPORT = 5
|
||||||
|
|
||||||
struct IMAGE_DATA_DIRECTORY
|
struct IMAGE_DATA_DIRECTORY
|
||||||
VirtualAddress dd ?
|
VirtualAddress dd ?
|
||||||
@ -62,6 +65,12 @@ ends
|
|||||||
IMAGE_DIRECTORY_ENTRY_EXPORT = 0
|
IMAGE_DIRECTORY_ENTRY_EXPORT = 0
|
||||||
IMAGE_DIRECTORY_ENTRY_IMPORT = 1
|
IMAGE_DIRECTORY_ENTRY_IMPORT = 1
|
||||||
IMAGE_DIRECTORY_ENTRY_BASERELOC = 5
|
IMAGE_DIRECTORY_ENTRY_BASERELOC = 5
|
||||||
|
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11
|
||||||
|
|
||||||
|
IMAGE_SUBSYSTEM_UNKNOWN = 0
|
||||||
|
IMAGE_SUBSYSTEM_NATIVE = 1
|
||||||
|
IMAGE_SUBSYSTEM_WINDOWS_GUI = 2
|
||||||
|
IMAGE_SUBSYSTEM_WINDOWS_CUI = 3
|
||||||
|
|
||||||
struct IMAGE_FILE_HEADER
|
struct IMAGE_FILE_HEADER
|
||||||
Machine dw ?
|
Machine dw ?
|
||||||
@ -95,7 +104,7 @@ struct IMAGE_EXPORT_DIRECTORY
|
|||||||
AddressOfNameOrdinals dd ?
|
AddressOfNameOrdinals dd ?
|
||||||
ends
|
ends
|
||||||
|
|
||||||
struct IMAGE_IMPORT_DIRECTORY
|
struct IMAGE_IMPORT_DESCRIPTOR
|
||||||
OriginalFirstThunk dd ?
|
OriginalFirstThunk dd ?
|
||||||
TimeDateStamp dd ?
|
TimeDateStamp dd ?
|
||||||
ForwarderChain dd ?
|
ForwarderChain dd ?
|
||||||
@ -103,6 +112,11 @@ struct IMAGE_IMPORT_DIRECTORY
|
|||||||
FirstThunk dd ?
|
FirstThunk dd ?
|
||||||
ends
|
ends
|
||||||
|
|
||||||
|
struct IMAGE_IMPORT_BY_NAME
|
||||||
|
Hint dw ?
|
||||||
|
Name rb 0
|
||||||
|
ends
|
||||||
|
|
||||||
struct IMAGE_BASE_RELOCATION
|
struct IMAGE_BASE_RELOCATION
|
||||||
VirtualAddress dd ?
|
VirtualAddress dd ?
|
||||||
SizeOfBlock dd ?
|
SizeOfBlock dd ?
|
||||||
@ -144,3 +158,9 @@ struct IMAGE_SECTION_HEADER
|
|||||||
NumberOfLinenumbers dw ?
|
NumberOfLinenumbers dw ?
|
||||||
Characteristics dd ?
|
Characteristics dd ?
|
||||||
ends
|
ends
|
||||||
|
|
||||||
|
struct IMAGE_BOUND_IMPORT_DESCRIPTOR
|
||||||
|
TimeDateStamp dd ?
|
||||||
|
OffsetModuleName dw ?
|
||||||
|
NumberOfModuleForwarderRefs dw ?
|
||||||
|
ends
|
||||||
|
File diff suppressed because it is too large
Load Diff
138
programs/system/os/sync.inc
Normal file
138
programs/system/os/sync.inc
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
; High-level synchronization primitives.
|
||||||
|
|
||||||
|
; Mutex: stands for MUTual EXclusion.
|
||||||
|
; Allows to enforce that only one thread executes some code at a time.
|
||||||
|
; mutex_lock acquires the given mutex, mutex_unlock releases it;
|
||||||
|
; if thread 1 holds the mutex and thread 2 calls mutex_lock,
|
||||||
|
; thread 2 is blocked until thread 1 calls mutex_unlock.
|
||||||
|
; Several threads can wait for the same mutex; when the owner
|
||||||
|
; releases the mutex, one of waiting threads grabs the released mutex,
|
||||||
|
; but it is unspecified which one.
|
||||||
|
|
||||||
|
; If there is no contention, i.e. no one calls mutex_lock
|
||||||
|
; while somebody is holding the mutex, then
|
||||||
|
; mutex_lock and mutex_unlock use just a few instructions.
|
||||||
|
; This is the fast path.
|
||||||
|
; Otherwise, mutex_lock and mutex_unlock require a syscall
|
||||||
|
; to enter waiting state and wake someone up correspondingly.
|
||||||
|
|
||||||
|
; Implementation. We use one dword for status and
|
||||||
|
; kernel handle for underlying futex to be able to sleep/wake.
|
||||||
|
; Bit 31, the highest bit of status dword,
|
||||||
|
; is set if someone holds the mutex and clear otherwise.
|
||||||
|
; Bits 0-30 form the number of threads waiting in mutex_lock.
|
||||||
|
; All modifications of status dword should be atomic.
|
||||||
|
|
||||||
|
struct MUTEX
|
||||||
|
status dd ?
|
||||||
|
handle dd ?
|
||||||
|
ends
|
||||||
|
|
||||||
|
; Initialization. Set status dword to zero and
|
||||||
|
; open the underlying futex.
|
||||||
|
; in: ecx -> MUTEX
|
||||||
|
proc mutex_init
|
||||||
|
mov [ecx+MUTEX.status], 0
|
||||||
|
push ebx
|
||||||
|
mov eax, 77
|
||||||
|
xor ebx, ebx
|
||||||
|
call FS_SYSCALL_PTR
|
||||||
|
pop ebx
|
||||||
|
mov [ecx+MUTEX.handle], eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; Finalization. Close the underlying futex.
|
||||||
|
; in: ecx = MUTEX handle
|
||||||
|
proc mutex_destroy
|
||||||
|
push ebx
|
||||||
|
mov eax, 77
|
||||||
|
mov ebx, 1
|
||||||
|
call FS_SYSCALL_PTR
|
||||||
|
pop ebx
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; Acquire the mutex.
|
||||||
|
macro mutex_lock mutex
|
||||||
|
{
|
||||||
|
local .done
|
||||||
|
; Atomically set the locked status bit and get the previous value.
|
||||||
|
lock bts [mutex+MUTEX.status], 31
|
||||||
|
; Fast path: the mutex was not locked. If so, we are done.
|
||||||
|
jnc .done
|
||||||
|
if ~(mutex eq ecx)
|
||||||
|
mov ecx, mutex
|
||||||
|
end if
|
||||||
|
call mutex_lock_slow_path
|
||||||
|
.done:
|
||||||
|
}
|
||||||
|
|
||||||
|
; Acquire the mutex, slow path.
|
||||||
|
; Someone holds the mutex... or has held it a moment ago.
|
||||||
|
; in: ecx -> MUTEX
|
||||||
|
proc mutex_lock_slow_path
|
||||||
|
; Atomically increment number of waiters.
|
||||||
|
lock inc [ecx+MUTEX.status]
|
||||||
|
; When the mutex owner will release the mutex and wake us up,
|
||||||
|
; another thread can sneak in and grab the mutex before us.
|
||||||
|
; So, the following actions are potentially repeated in a loop.
|
||||||
|
.wait_loop:
|
||||||
|
mov edx, [ecx+MUTEX.status]
|
||||||
|
; The owner could have unlocked the mutex in parallel with us.
|
||||||
|
; If so, don't sleep: nobody would wake us up.
|
||||||
|
test edx, edx
|
||||||
|
jns .skip_wait
|
||||||
|
; Pass the fetched value to the kernel along with futex handle.
|
||||||
|
; If the owner unlocks the mutex while we are here,
|
||||||
|
; the kernel will detect mismatch and exit without sleeping.
|
||||||
|
; Otherwise, the owner will wake us up explicitly.
|
||||||
|
push ebx ecx esi
|
||||||
|
mov eax, 77
|
||||||
|
mov ebx, 2
|
||||||
|
mov ecx, [ecx+MUTEX.handle]
|
||||||
|
xor esi, esi
|
||||||
|
call FS_SYSCALL_PTR
|
||||||
|
pop esi ecx ebx
|
||||||
|
.skip_wait:
|
||||||
|
; We have woken up.
|
||||||
|
; Or we didn't even sleep because status dword has been changed beneath us.
|
||||||
|
; Anyway, something may have changed, re-evaluate the situation.
|
||||||
|
; Atomically set the locked status bit and get the previous value.
|
||||||
|
lock bts [ecx+MUTEX.status], 31
|
||||||
|
; If the mutex was locked, someone has grabbed the mutex before us.
|
||||||
|
; Repeat the loop.
|
||||||
|
jc .wait_loop
|
||||||
|
; The mutex was unlocked and we have just managed to lock it.
|
||||||
|
; Our status has changed from a waiter to the owner.
|
||||||
|
; Decrease number of waiters and exit.
|
||||||
|
lock dec [ecx+MUTEX.status]
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; Release the mutex.
|
||||||
|
macro mutex_unlock mutex
|
||||||
|
{
|
||||||
|
local .done
|
||||||
|
; Atomically clear the locked status bit and check whether someone is waiting.
|
||||||
|
lock and [mutex+MUTEX.status], 0x7FFFFFFF
|
||||||
|
; Fast path: nobody is waiting.
|
||||||
|
jz .done
|
||||||
|
mov ecx, [mutex+MUTEX.handle]
|
||||||
|
call mutex_unlock_slow_path
|
||||||
|
.done:
|
||||||
|
}
|
||||||
|
|
||||||
|
; Release the mutex, slow path.
|
||||||
|
; Someone is sleeping in the kernel, or preparing for the sleep.
|
||||||
|
; Wake one of waiters.
|
||||||
|
; in: ecx = MUTEX handle
|
||||||
|
proc mutex_unlock_slow_path
|
||||||
|
push ebx
|
||||||
|
mov eax, 77
|
||||||
|
mov ebx, 3
|
||||||
|
mov edx, 1
|
||||||
|
call FS_SYSCALL_PTR
|
||||||
|
pop ebx
|
||||||
|
ret
|
||||||
|
endp
|
Loading…
Reference in New Issue
Block a user