/programs/develop/libraries/dll/ReadMe.txt |
---|
8,21 → 8,23 |
0.2 + Introduced new KX header as extension for current format (see decription below) |
+ Add KX header processing |
+ Improved import table test logic, no reason to kill app for import absence - skip import processing (tnx ProMiNick) |
+ Added ReadMe.txt (this doc) |
--- |
0.2.1 + Branch from dll.inc, now this file is not external. Improved error handling. Now dll.Load return 0 in success only |
Added corrsponding error codes if one of library or entry not found |
+ Added error handling with detailed inform user which error occurred through @notyfy. |
Now application is not crashed if bad format, can't load library or no found entry |
Purpose |
Automatically libraries loads and linking imports. |
--- |
Limitations |
TODO |
1) The library format needs to be improved, see intorduction of KX header extension bellow |
1) No error messages are issued if the library or symbol in the library is not found or somthing went wrong |
2) There is no autoloading of dependent libraries (the library format needs to be improved, see intorduction of KX header extension bellow) |
--- |
How to use |
/programs/develop/libraries/dll/dll.asm |
---|
1,25 → 1,35 |
; |
; KolibriOS Dll load support |
; Support for Dll auto load & linking |
; |
; (C) 2020-2021 Coldy |
; (C) 2020-2022 Coldy |
; Thank's you for use this code and software based on it! |
; I will glad if it's will be helpful. |
; |
; Distributed under terms of GPL |
; |
format MS COFF |
public @EXPORT as 'EXPORTS' |
section '.flat' code readable align 16 |
include '../../../proc32.inc' |
include '../../../macros.inc' |
include 'external.inc' |
include 'dll.inc' |
section '.flat' code readable align 16 |
; This need for @notyfy pre/postformat |
STR_BUILD_OFFSET = 1 |
STR_BUILD_EXTRA = 5 |
mem_alloc = mem.Alloc |
include 'strhlp.inc' |
app_version equ word[8] |
i_table_min_size = 1 |
sizeof.kx_header = 8 |
ERROR_BAD_IMAGE = 0x010 |
APP_STARTUP_THUNK: |
; First make shure that app |
; have header version 2.0 or more |
41,10 → 51,10 |
mov esi,0x24 |
lodsw |
cmp ax, 'KX' |
jne @f ; Not KX |
jne .image_error ; Not KX |
lodsw |
cmp ax, 0 |
jne @f ; Bad magic |
jne .image_error ; Bad magic |
lodsw |
bt ax, 6 ; Have import? |
54,15 → 64,15 |
; Test import table (use legacy style) |
mov eax, [sizeof.kx_header + 0x24] ; i_table_ptr |
test eax, eax |
jz .app_start ; i_table_ptr = 0 ? |
jz .image_error;.import_error;.app_start ; i_table_ptr = 0 ? => Bad image |
;js .error |
mov esi, [0x10] |
cmp esi, eax |
jbe @f ; i_table_ptr >= img_size ? |
jbe .image_error;@f ; i_table_ptr >= img_size ? |
mov ebx, eax |
add ebx, i_table_min_size |
cmp esi, ebx |
jb @f ; i_table_ptr + i_table_min_size > img_size ? |
jb .image_error;@f ; i_table_ptr + i_table_min_size > img_size ? |
; Link app/dependent libs import tables with libs export table |
; TODO: need revision of the exists lib format and dll.Load (for libs import binds) |
69,7 → 79,7 |
stdcall dll.Load,eax |
test eax, eax |
jnz .import_error |
jnz .link_error;.import_error |
.app_start: |
; Start of app code |
mov eax, [0x0C] |
78,9 → 88,11 |
@@: |
mov eax, -1 |
int 0x40 |
.import_error: |
; Run @NOTIFY and tell user then error occured |
; BOARD will contaits details |
.image_error: |
mov eax, ERROR_BAD_IMAGE |
.link_error: |
; Run @NOTIFY and tell user then error occurred |
call show_error |
jmp @b |
.denied: |
; Kolibri has no ability kill app if this enter from no from main thread |
90,8 → 102,67 |
; } APP_STARTUP_THUNK |
; WARNING! This code must be after app initialization thunk! |
include '../../../dll.inc' |
; eax = error code (see ERROR_xxx above in this and dll.inc files) |
show_error: |
; Store error code |
mov edx, eax |
; Get app name |
sub esp,1024 |
mov eax, 9 |
mov ebx, esp |
mov ecx, -1 |
int 0x40 |
; |
mov esi, esp |
add esi, 10 ; esi = app name |
cmp edx, ERROR_ENTRY_NOT_FOUND |
je .entry_not_found |
; Init heap not needed |
; (kernel already initialized heap implicitly when load dll.obj) |
cmp edx, ERROR_LIBRARY_NOT_LOAD |
je .library_not_loaded |
ccall str_build, szWrongFormat, esi, szBanner |
jmp @f |
.library_not_loaded: |
ccall str_build, szLibraryNotLoaded, esi, szBanner, s_libdir.fname |
jmp @f |
.entry_not_found: |
mov eax, [szEntryName] |
ccall str_build, szEntryNotFound, esi, szBanner, eax, s_libdir.fname |
@@: |
add esp, 1024 |
mov byte[eax],'"' |
mov byte[edi],'"' |
mov dword[edi+1],"-tdE" ; with title, disable autoclose, error icon |
mov esi, eax |
; Display error |
mov [pNotify.params], eax |
mov ebx, pNotify |
mov eax, 70 |
int 0x40 |
stdcall mem.Free, esi |
.exit: |
ret |
align 4 |
;dd 0xdeadbeef |
dd APP_STARTUP_THUNK |
99,4 → 170,30 |
export \ |
dll.Load, 'dll_load', \ |
dll.Link, 'dll_link', \ |
dll.GetProcAddress, 'dll_sym' ; |
dll.GetProcAddress, 'dll_sym' |
pNotify: |
dd 7, 0 |
.params dd 0 |
dd 0, 0 |
db "/sys/@notify", 0 |
; { Strings |
if defined LANG_RUS |
include 'strings.rus' |
;elseif defined LANG_xxx |
; TODO: Add another supported languges here |
; - Copy 'elseif defined LANG_xxx', change xxx here and below to the |
; corresponded constat of you languge |
; - Create strings.xxx in the root of this project and do translate, |
; follow the format message below. |
; - add include 'strings.xxx' |
else ; Default languge (English) |
szBanner db "Error!\n",0 |
szWrongFormat db "$ - $Application has wrong format!",0 |
szLibraryNotLoaded db "$ - $Can't load library $", 0 |
szEntryNotFound db "$ - $Entry $\nin library $\nnot found!",0 |
end if |
; } Strings |
/programs/develop/libraries/dll/dll.inc |
---|
0,0 → 1,192 |
; |
; (C) KolibriOS team (original dll.inc) |
; (C) 2022, Edited by Coldy |
; |
; This module based on original dll.inc. |
; |
; - Improved error handling. Now dll.Load return 0 is success guarantie |
; Also added corrsponding error codes if one of library or entry not found |
; |
ERROR_LIBRARY_NOT_LOAD = 0x100 |
ERROR_ENTRY_NOT_FOUND = 0x101 |
;----------------------------------------------------------------------------- |
; load one or more DLL file in COFF format and try to import functions by our list |
; if first function in import list begins with 'lib_', call it as DLL initialization |
; return eax = 1 as fail, if anyone of .obj file not found in /sys/lib |
; return 0 if all fine or error code LIBRARY_NOT_LOAD or ENTRY_NOT_FOUND |
; dirties all registers! eax, ebx, ecx, edx, esi, edi |
proc dll.Load, import_table:dword |
mov esi, [import_table] |
.next_lib: |
mov edx, [esi] |
or edx, edx |
jz .exit |
push esi |
mov esi, [esi + 4] |
mov edi, s_libdir.fname |
@@: |
lodsb |
stosb |
or al, al |
jnz @b |
mcall 68, 19, s_libdir |
or eax, eax |
jz .fail_load |
push eax |
stdcall dll.Link, eax, edx |
test eax, eax |
jnz .fail_link |
;push eax |
mov eax, [esp] |
mov eax, [eax] |
cmp dword[eax], 'lib_' |
pop eax |
jnz @f |
stdcall dll.Init, [eax + 4] |
@@: |
pop esi |
add esi, 8 |
jmp .next_lib |
.exit: |
xor eax, eax |
ret |
.fail_load: |
add esp, 4 |
;xor eax, eax |
;inc eax |
mov eax, ERROR_LIBRARY_NOT_LOAD |
ret |
.fail_link: |
add esp, 4 |
ret |
endp |
;----------------------------------------------------------------------------- |
; scans dll export table for a functions we want to import |
; break scan on first unresolved import |
; return value: 0 - success or ENTRY_NOT_FOUND |
proc dll.Link, exp:dword, imp:dword |
;push eax |
mov esi, [imp] |
; Import table alreary checked in APP_STARTUP_THUNK |
;test esi, esi |
;jz .fail1;.done |
.next: |
lodsd |
test eax, eax |
jz .done |
mov ebx, eax |
stdcall dll.GetProcAddress, [exp], eax |
or eax, eax |
jz .fail ;.done |
mov [esi - 4], eax |
jmp .next |
; @@: |
;mov dword[esp], 0 |
;.fail1: |
; No imports |
;mov eax, BAD_IMAGE |
;jmp .done |
.fail: |
mov [szEntryName],ebx |
mov eax, ERROR_ENTRY_NOT_FOUND |
.done: |
;pop eax |
ret |
endp |
;----------------------------------------------------------------------------- |
; calls lib_init with predefined parameters |
; no return value |
proc dll.Init, dllentry:dword |
pushad |
mov eax, mem.Alloc |
mov ebx, mem.Free |
mov ecx, mem.ReAlloc |
mov edx, dll.Load |
stdcall [dllentry] |
popad |
ret |
endp |
;----------------------------------------------------------------------------- |
; scans export table for a sz_name function |
; returns in eax function address or 0 if not found |
proc dll.GetProcAddress, exp:dword, sz_name:dword |
mov edx, [exp] |
xor eax, eax |
.next: |
or edx, edx |
jz .end |
cmp dword[edx], 0 |
jz .end |
stdcall strcmp, [edx], [sz_name] |
test eax, eax |
jz .ok |
add edx, 8 |
jmp .next |
.ok: |
mov eax, [edx + 4] |
.end: |
cmp eax, -1 |
jnz @f |
xor eax, eax |
@@: |
ret |
endp |
;----------------------------------------------------------------------------- |
; compares strings |
; returns eax = 0 if equal, -1 otherwise |
proc strcmp, str1:dword, str2:dword |
push esi edi |
mov esi, [str1] |
mov edi, [str2] |
xor eax, eax |
@@: |
lodsb |
scasb |
jne .fail |
or al, al |
jnz @b |
jmp .ok |
.fail: |
or eax, -1 |
.ok: |
pop edi esi |
ret |
endp |
;----------------------------------------------------------------------------- |
s_libdir: |
db '/sys/lib/' |
.fname rb 32 |
szEntryName dd 0 |
;----------------------------------------------------------------------------- |
proc mem.Alloc, size |
push ebx ecx |
mov ecx, [size] |
mcall 68, 12 |
pop ecx ebx |
ret |
endp |
;----------------------------------------------------------------------------- |
proc mem.ReAlloc, mptr, size |
push ebx ecx edx |
mov ecx, [size] |
mov edx, [mptr] |
mcall 68, 20 |
pop edx ecx ebx |
ret |
endp |
;----------------------------------------------------------------------------- |
proc mem.Free, mptr |
push ebx ecx |
mov ecx,[mptr] |
mcall 68, 13 |
pop ecx ebx |
ret |
endp |
;----------------------------------------------------------------------------- |
/programs/develop/libraries/dll/external.inc |
---|
0,0 → 1,7 |
; |
; This file is used for external includes |
; If server and locale relative paths is different |
; then need a corresponding copy on each side |
include '../../../proc32.inc' |
include '../../../macros.inc' |
/programs/develop/libraries/dll/strhlp.inc |
---|
0,0 → 1,303 |
; |
; String helpers |
; |
; (C) KolibriOS team (parts from another project) |
; (C) 2022 Coldy (str_buld function) |
; Thank's you for use this code and software based on it! |
; I will glad if it's will be helpful. |
; |
; Distributed under terms of GPL |
; |
;**************************************** |
;* input: esi = pointer to string * |
;* output: ecx = length of the string * |
;**************************************** |
strlen: |
push eax esi |
xor ecx, ecx |
@@: |
lodsb |
or al, al |
jz @f |
inc ecx |
jmp @b |
@@: |
pop esi eax |
ret |
;************************************************* |
;* input: esi = pointer to the src string * |
;* edi = pointer to the dest string * |
;* ecx = number of bytes to copy * |
;************************************************* |
strncpy: |
push eax ecx esi edi |
@@: |
lodsb |
stosb |
or al, al |
jz @f |
dec ecx |
jz @f |
jmp @b |
@@: |
pop edi esi ecx eax |
ret |
if 0 ; { Not used |
;************************************************* |
;* input: esi = pointer to the src string * |
;* edi = pointer to the dest string * |
;************************************************* |
strcpy: |
push esi edi |
; ecx = ??? |
; ZF = 0 |
rep movsb |
pop edi esi |
ret |
;************************************************* |
;* input: esi = pointer to the src string * |
;* edi = pointer to the dest string * |
;* ecx = number of bytes to copy * |
;************************************************* |
strncat: |
push edi |
push ecx esi |
mov esi, edi |
call strlen |
add edi, ecx |
pop esi ecx |
call strncpy |
pop edi |
ret |
;************************************************* |
;* (c) Coldy 2022 * |
;* input: edi = pointer to the dest string * |
;* ecx = number of bytes to zero * |
;************************************************* |
;memnz: |
; push eax ecx edi |
; xor eax, eax |
; rep stosb |
; pop edi ecx eax |
; ret |
end if ; } |
; |
; str_build |
; |
; Purose: Build output string by template. Allocate necessary output |
; buffer, copy parts from template and insert strings instead |
; of $ wildcard. |
; |
; SPECIAL CASE: |
; For use dollar sing ($) in text, just mark this plase(s) in |
; template and provide pointer(s) on string with this sign in args) |
; |
; PRECAUTION: |
; 1. Not safe, caller must provide args count >= $ wildcard count |
; 2. If used dynamic memory allocator then caller must free output |
; buffer |
; 3. Looks like cdecl, but she is not. For cdecl need compat wrapper |
; 4. Dirties all registers, incl. ebp |
; |
; Input: |
; esp+4 = pointer to template string |
; esp+8 = wildcard strings pointers in reverse order |
; |
; Options: |
if ~STR_BUILD_OFFSET |
STR_BUILD_OFFSET = 0 |
; Optional, specify STR_BUILD_OFFSET value for offset from start |
; of output buffer (this useful for postinsert initial characters |
; before output sting). By default - no offset (0) |
end if |
if ~STR_BUILD_EXTRA |
STR_BUILD_EXTRA = 0 |
; Optional, specify STR_BUILD_EXTRA value for extra length of |
; output bufer (this useful for postadding characters after |
; output string). By default - no extra length (0) |
end if |
; |
; { STR_BUILD_NO_DOLLAR_SIGN - should be removed, see cpecial case above } |
; |
; Next two options below can reduse total code size by exclude |
; corresponding parts if they are not used |
; |
if ~STR_BUILD_NO_STARTING_TEXT |
STR_BUILD_NO_STARTING_TEXT = 0 |
; Specify STR_BUILD_NO_STARTING_TEXT if you do not used templates |
; starting with text, e.g."Some text first $, $" |
; By default is disabled (0) |
end if |
; |
if ~STR_BUILD_NO_DOUBLE_WILDCARD |
STR_BUILD_NO_DOUBLE_WILDCARD = 0 |
; Specify STR_BUILD_NO_DOUBLE_WILDCARD if you not used templates |
; with double wildcards, e.g. "$$ some text" or "Some text $$" |
; By default is disabled (0) |
end if |
; |
; mem_alloc(size) |
; external memory allocator, stdcall. Must return pointer (in eax) |
; to base of memory block by size length. By defauld used internal |
; |
; Output: |
; eax = Pointer to output string (see PRECAUTION #2) or 0 if error |
; edi = Cursor of output string. No mean inf if eax = 0 |
; |
; Stack struct |
; ------ |
; | ArgN | |
; ------- |
; | ... | |
; ------- |
; ebp+4 | Arg1 | |
; ------ ------- |
; ebp | TplS | Template string |
; ------ ------- |
; | ret | Caller return address (not used), esp when entry |
; ------- |
; | $off1 | 1st offset in template string |
; ------- |
; | ... | |
; ------- |
; | $offN | N-offset in template string |
; ------- |
; | EOTpl | End of template string, esp after phase 1.1 |
; ------- |
; | Len1 | Length of 1st wildcard string |
; ------- |
; | ... | |
; ------- |
; | LenN | Length of N wildcard string, esp after phase 1.2 |
; ------- |
; |
str_build: |
mov ebp, esp ; Store caller esp... |
add ebp, 8 ; ... and shift return address and tamplate string |
mov esi, [ebp-4] |
xor edx, edx ; Offsets, length... |
xor edi, edi ; Found count... |
; Phase 1.1. Scan to find positions $ and store to stack offsets $+1 |
; and end of template string. Break scan if zero byte appear |
.scan: |
lodsb |
inc edx |
or al, al |
jz .end_scan |
cmp al, '$' |
je .found |
jmp .scan |
.found: |
push edx ; Store offset |
inc edi |
jmp .scan |
.end_scan: |
or edi, edi |
jz .error ; Not found |
push edx ; Store last offset (end of template string) |
sub edx, edi |
dec edx ; Total length + zero string |
; Phase 1.2. Store to stack lengths of wildcard strings |
mov eax, edi |
@@: |
mov esi,[ebp+4*(eax-1)] |
call strlen |
add edx, ecx |
push ecx |
dec eax |
inc edi ; once edi*2 instead |
test eax,eax |
jnz @b |
add edx, STR_BUILD_OFFSET + STR_BUILD_EXTRA |
; Phase 1.3. Allocate buffer for output string |
if defined mem_alloc |
stdcall mem_alloc, edx |
else |
mov eax, 68 |
mov ebx, 12 |
mov ecx, edx |
int 0x40 |
end if |
test eax,eax |
jz .exit |
mov byte[eax+edx],0 ; Mark end of output string |
; Phase 2. Build output string |
; eax = base of output string |
xor ebx, ebx ; ebx = index of args data |
; ecx = free, but used below |
mov edx, edi ; edx = index of stored data |
; esi = free, but used below |
mov edi, eax ; edi = cursor of output string |
add edi, STR_BUILD_OFFSET |
if ~STR_BUILD_NO_STARTING_TEXT ; { |
mov ecx, [esp+4*edx] ; Offset |
cmp ecx,1 ; Wildcard first? |
je .build |
mov esi, -2 ; One or double wildcard at the end |
neg ecx |
add ecx, [esp+4*edx-4] ; Next offset |
cmp ecx, 1 ; More one wildcard at the end? |
je @f |
dec esi |
@@: |
mov ecx,esi |
add ecx,[esp+4*edx-4] ; Next offset |
mov esi,[ebp-4] ; Template string |
call strncpy |
add edi, ecx ; Advance cursor |
end if; } STR_BUILD_NO_STARTING_TEXT |
.build: |
mov esi, [ebp+4*ebx] ; Wildcard string |
mov ecx,[esp+4*ebx] ; Length |
call strncpy |
add edi, ecx ; Advance cursor |
mov ecx, [esp+4*edx] ; Offset |
mov esi,[ebp-4] ; Template string |
add esi, ecx |
cmp byte [esi], 0 ; End of string? |
je .exit |
if ~STR_BUILD_NO_DOUBLE_WILDCARD ; { |
cmp byte [esi], '$' |
je @f |
end if; } STR_BUILD_NO_DOUBLE_WILDCARD |
neg ecx |
add ecx,[esp+4*edx-4] ; Next offset |
dec ecx |
call strncpy |
add edi, ecx ; Advance cursor |
@@: ; { STR_BUILD_NO_DOUBLE_WILDCARD } |
inc ebx |
dec edx |
cmp ebx, edx |
jne .build |
.exit: |
; Restore stack |
sub ebp, 8 |
mov esp,ebp |
ret |
.error: |
xor eax, eax |
ret |
/programs/develop/libraries/dll/strings.rus |
---|
0,0 → 1,5 |
szBanner db "訡ª !\n",0 |
szWrongFormat db "$ - $¥¯à ¢¨«ìë© ä®à¬ ⠯ਫ®¦¥¨ï!",0 |
szLibraryNotLoaded db "$ - $¥¢®§¬®¦® § £à㧨âì ¡¨¡«¨®â¥ªã $", 0 |
szEntryNotFound db "$ - $室 $\n¢ ¡¨¡«¨®â¥ª¥ $\n¥ ©¤¥!",0 |