diff --git a/programs/develop/libraries/dll/ReadMe.txt b/programs/develop/libraries/dll/ReadMe.txt index 7a5308500..105634e2d 100644 --- a/programs/develop/libraries/dll/ReadMe.txt +++ b/programs/develop/libraries/dll/ReadMe.txt @@ -2,26 +2,28 @@ --- History -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 +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 + +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 Automatically libraries loads and linking imports. --- -Limitations +TODO - -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) +1) The library format needs to be improved, see intorduction of KX header extension bellow --- 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 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 -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" 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" - 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 (but can't take values higher than 64) diff --git a/programs/develop/libraries/dll/dll.asm b/programs/develop/libraries/dll/dll.asm index f7835afec..7d6150dce 100644 --- a/programs/develop/libraries/dll/dll.asm +++ b/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' - -include '../../../proc32.inc' -include '../../../macros.inc' - 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] 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 @@ APP_STARTUP_THUNK: 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,22 +64,22 @@ APP_STARTUP_THUNK: ; 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) 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 @@ APP_STARTUP_THUNK: @@: 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,13 +102,98 @@ APP_STARTUP_THUNK: ; } 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 @EXPORT: export \ - dll.Load, 'dll_load', \ - dll.Link, 'dll_link', \ - dll.GetProcAddress, 'dll_sym' ; \ No newline at end of file + dll.Load, 'dll_load', \ + dll.Link, 'dll_link', \ + 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 \ No newline at end of file diff --git a/programs/develop/libraries/dll/dll.inc b/programs/develop/libraries/dll/dll.inc new file mode 100644 index 000000000..063cd36db --- /dev/null +++ b/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 +;----------------------------------------------------------------------------- diff --git a/programs/develop/libraries/dll/external.inc b/programs/develop/libraries/dll/external.inc new file mode 100644 index 000000000..78c259bc1 --- /dev/null +++ b/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' \ No newline at end of file diff --git a/programs/develop/libraries/dll/strhlp.inc b/programs/develop/libraries/dll/strhlp.inc new file mode 100644 index 000000000..dc7ffdcca --- /dev/null +++ b/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 + diff --git a/programs/develop/libraries/dll/strings.rus b/programs/develop/libraries/dll/strings.rus new file mode 100644 index 000000000..fc2e28ba2 --- /dev/null +++ b/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 \ No newline at end of file