dll.obj: Added error handling with detailed inform user which error occurred through @notyfy

git-svn-id: svn://kolibrios.org@9669 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
Coldy 2022-01-25 18:04:10 +00:00
parent 254d30c075
commit 803d2a454e
6 changed files with 640 additions and 34 deletions

View File

@ -2,26 +2,28 @@
--- ---
History History
0.1 + First realised, kernel load dll.obj at runtime as starting point berfore app startup 0.1 + First realised, kernel load dll.obj at runtime as starting point berfore app startup
dll.obj process app import table, but not depended librarys, after that app gots control in his starting point dll.obj process app import table, but not depended librarys, after that app gots control in his starting point
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
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)
---
Purpose Purpose
Automatically libraries loads and linking imports. 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 How to use
@ -30,7 +32,7 @@ How to use
1) In the version field of a header, (after MENUET0x) you must specify the number 2 1) In the version field of a header, (after MENUET0x) you must specify the number 2
2) After existing header add KX header extension as descriprion bellow 2) After existing header add KX header extension as descriprion bellow
3) Specify imported libraries. Currentry format of import table same as in case of using dll.Load 3) Specify imported libraries. Currentry format of import table same as in case of using dll.Load
4) Add code, without connecting dll.inc and, accordingly, without calling dll.Load. The heap initialization function (f. 68.11) does not need to be called either. 4) Add code, without connecting dll.inc and, accordingly, without calling dll.Load. The heap initialization function (f. 68.11) does not need to be called either.
5) Compile the app and run. If everything is done correctly, then on startup the debug board will display the message "App header version 2" 5) Compile the app and run. If everything is done correctly, then on startup the debug board will display the message "App header version 2"
If the DLL.OBJ library is missing, a message will be displayed, incl. via @NOTIFY. If you get a page error make sure you have completed steps 2 and 3 If the DLL.OBJ library is missing, a message will be displayed, incl. via @NOTIFY. If you get a page error make sure you have completed steps 2 and 3
@ -55,7 +57,7 @@ Fields between offset 8 and at end of KX header may be added later.
0 2 SigMagic Module identifier with the value "KX" 0 2 SigMagic Module identifier with the value "KX"
2 1 SigRevision This field should be 0. 2 1 SigRevision This field should be 0.
In the future, it can take on the revision value In the future, it can take on the revision value
(but can't take values higher than 64) (but can't take values higher than 64)

View File

@ -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! ; Thank's you for use this code and software based on it!
; I will glad if it's will be helpful. ; I will glad if it's will be helpful.
; ;
; Distributed under terms of GPL ; Distributed under terms of GPL
; ;
format MS COFF format MS COFF
public @EXPORT as 'EXPORTS' public @EXPORT as 'EXPORTS'
include '../../../proc32.inc'
include '../../../macros.inc'
section '.flat' code readable align 16 section '.flat' code readable align 16
include 'external.inc'
include 'dll.inc'
; 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] app_version equ word[8]
i_table_min_size = 1 i_table_min_size = 1
sizeof.kx_header = 8 sizeof.kx_header = 8
ERROR_BAD_IMAGE = 0x010
APP_STARTUP_THUNK: APP_STARTUP_THUNK:
; First make shure that app ; First make shure that app
; have header version 2.0 or more ; have header version 2.0 or more
@ -41,10 +51,10 @@ APP_STARTUP_THUNK:
mov esi,0x24 mov esi,0x24
lodsw lodsw
cmp ax, 'KX' cmp ax, 'KX'
jne @f ; Not KX jne .image_error ; Not KX
lodsw lodsw
cmp ax, 0 cmp ax, 0
jne @f ; Bad magic jne .image_error ; Bad magic
lodsw lodsw
bt ax, 6 ; Have import? bt ax, 6 ; Have import?
@ -54,22 +64,22 @@ APP_STARTUP_THUNK:
; Test import table (use legacy style) ; Test import table (use legacy style)
mov eax, [sizeof.kx_header + 0x24] ; i_table_ptr mov eax, [sizeof.kx_header + 0x24] ; i_table_ptr
test eax, eax 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 ;js .error
mov esi, [0x10] mov esi, [0x10]
cmp esi, eax cmp esi, eax
jbe @f ; i_table_ptr >= img_size ? jbe .image_error;@f ; i_table_ptr >= img_size ?
mov ebx, eax mov ebx, eax
add ebx, i_table_min_size add ebx, i_table_min_size
cmp esi, ebx 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 ; 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) ; TODO: need revision of the exists lib format and dll.Load (for libs import binds)
stdcall dll.Load,eax stdcall dll.Load,eax
test eax, eax test eax, eax
jnz .import_error jnz .link_error;.import_error
.app_start: .app_start:
; Start of app code ; Start of app code
mov eax, [0x0C] mov eax, [0x0C]
@ -78,9 +88,11 @@ APP_STARTUP_THUNK:
@@: @@:
mov eax, -1 mov eax, -1
int 0x40 int 0x40
.import_error: .image_error:
; Run @NOTIFY and tell user then error occured mov eax, ERROR_BAD_IMAGE
; BOARD will contaits details .link_error:
; Run @NOTIFY and tell user then error occurred
call show_error
jmp @b jmp @b
.denied: .denied:
; Kolibri has no ability kill app if this enter from no from main thread ; Kolibri has no ability kill app if this enter from no from main thread
@ -90,13 +102,98 @@ APP_STARTUP_THUNK:
; } APP_STARTUP_THUNK ; } APP_STARTUP_THUNK
; WARNING! This code must be after app initialization thunk! ; eax = error code (see ERROR_xxx above in this and dll.inc files)
include '../../../dll.inc'
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 align 4
;dd 0xdeadbeef ;dd 0xdeadbeef
dd APP_STARTUP_THUNK dd APP_STARTUP_THUNK
@EXPORT: @EXPORT:
export \ export \
dll.Load, 'dll_load', \ dll.Load, 'dll_load', \
dll.Link, 'dll_link', \ 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

View File

@ -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
;-----------------------------------------------------------------------------

View File

@ -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'

View File

@ -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

View File

@ -0,0 +1,5 @@
szBanner db "舘芋<E88898>!\n",0
szWrongFormat db "$ - $摚能𨯂走鴙諰 銈酮罱 能走恩平剁!",0
szLibraryNotLoaded db "$ - $摚╳妞恩陋 <20>␡膇兩<E88687> ”‵車漭芥 $", 0
szEntryNotFound db "$ - $<24>恕 $\n<> ”‵車漭炙 $\n郊 <20>岸平!",0