07 February 2022, 23:33 | #1 |
Registered User
Join Date: Apr 2009
Location: N/A
Posts: 962
|
Writing a dynamic library
For the next phase of my project I want to write dynamic libraries. I have found an example dynamic library (screennotify) on this forum, here;
http://eab.abime.net/showthread.php?t=77885 After going through the code I re-wrote the library with mainly two source files, one is mylib.c (based on screennotify.c) which contains the LibraryInit, LibraryOpen, LibraryClose and LibraryExpunge functions, and the other one is the functions.c which contains just one function that returns int value when called named giveMeInt() The library compiles successfully and I use the following code to call it. Code:
struct Library *lib = OpenLibrary("System:MyLib/mylib.library",0); if(lib) { printf("Loaded.\n"); } What I did try is to include functions.h from the library (which has the prototype for giveMeInt() function) in the application, but when I try to compile the application I get an error "Reference to undefined symbol _giveMeInt". Is this because I do not have the proto, pragma and clib header files? The proto and clib header files look straight forward to write, but the pragma header file looks like it is generated. This is from screennotify; Code:
#pragma libcall ScreenNotifyBase AddCloseScreenClient 1e 09803 #pragma libcall ScreenNotifyBase RemCloseScreenClient 24 801 ... I use vbcc as a compiler which is why I tried to base my solution around screennotify. Last edited by Sim085; 08 February 2022 at 11:23. |
08 February 2022, 11:35 | #2 |
Registered User
Join Date: Jan 2019
Location: Germany
Posts: 3,215
|
The numbers in the pragma files identify the registers through which the functions receive parameters. This requires some compiler magic to retrieve the parameters from there, and this magic is of course compiler specific. I'm not sure how or if vbcc can do this for you.
To call library functions from your code, you need to include a pragma file as above, though this file is also compiler specific. There is a FD2Pragma program out in Aminet that can create these pragma files automatically for you, from a library ".fd" file. Thus, the following steps need to be done: 1) Prepare your library implementation such that it retrieves parameters from registers, not from the stack. 2) document these registers in a ".fd" file, listing the parameters. 3) Create a library "prototypes" file that contains all the function prototypes 4) Create from the ".fd" file a "pragma" file for your target compiler. 5) Include in your project using the library both the prototype file and the pragma file. |
08 February 2022, 12:15 | #3 |
Natteravn
Join Date: Nov 2009
Location: Herford / Germany
Posts: 2,496
|
Usually there are two ways to call shared library functions from Amiga compilers:
But fear not, you don't have to create all these headers manually. As Thomas already mentioned, there are tools like FD2Pragma, which generate them automatically for all common compilers. All you need is an up to date FD or SFD file for your library. Looking at the ScreenNotify source, an FD file might look like: Code:
##base _ScreenNotifyBase ##bias 30 ##public AddCloseScreenClient(scr,port,pri)(a0/a1/d0) RemCloseScreenClient(node)(a0) etc.. ##end Proto header: fd2pragma special 38 my_lib.fd Inlines: fd2pragma special 70 voidbase my_lib.fd clib path_to/clib/mylib_protos.h Link library with stubs: fd2pragma special 12 hunkname CODE onlycnames my_lib.fd Make sure you have a recent fd2pragma version. The one on Aminet is quite old. Here is the last release: http://phoenix.owl.de/fd2pragma.tar.gz Looking at the ScreenNotify code I noticed that it uses a static library base. This should be avoided. Use the library base passed as an argument via register A6 instead. You can also call the vbcc-specific assembler inlines directly to pass the library base as an argument. |
08 February 2022, 12:17 | #4 |
Registered User
Join Date: Jan 2002
Location: Germany
Posts: 6,985
|
fd2pragma is included with vbcc.
And... vbcc does not use pragmas! You should rather create vbcc inline files with fd2pragma. |
10 February 2022, 22:18 | #5 | |
Registered User
Join Date: Apr 2009
Location: N/A
Posts: 962
|
Using fd2pragma that comes with vbcc I generated the required files and managed to get the application using the library to compile. However it crashes the moment I call the library function. I am trying to see what I did wrong. However in the meantime wanted to ask about the below. What does "Use the library base passed as an argument via register A6 instead" mean?
Quote:
|
|
11 February 2022, 15:15 | #6 | ||
Natteravn
Join Date: Nov 2009
Location: Herford / Germany
Posts: 2,496
|
Quote:
Did you call your library function via the stub routine or the assembler inline? When including <proto/my..._protos.h> it should be the latter. Quote:
Code:
SAVEDS BOOL _RemCloseScreenClient(REG(a0) struct Node *n) Code:
SAVEDS BOOL _RemCloseScreenClient(REG(a6) struct ScreenNotifyBase *ScreenNotifyBase, REG(a0) struct Node *n) ScreenNotifyBase->SysBase. Then you can call OS functions directly via their inline function, by passing the base as the first argument (this is vbcc-specific). For example: Code:
__Remove(ScreenNotifyBase->SysBase, n); |
||
11 February 2022, 21:20 | #7 | ||
Registered User
Join Date: Apr 2009
Location: N/A
Posts: 962
|
So if I am understanding correctly, when opening a library the LibraryInit function is called. This calls the MakeLibrary function which returns a Library (+ extra space as per the dSize parameter for further initialization) and then through AddLibrary function this is sort of registered so that it can be accessed from other applications.
The registered library is then passed to any of the library function calls automatically (this parameter does not need to be passed by the function caller as is done by the OS). Quote:
Quote:
Something else which is confusing me is if the Library is really getting initialized or not. Reason I say this is that I am trying to write into a file from the LibraryInit function (just for testing), however, while when calling OpenLibrary I do get a Library back, I cannot see the file being created . I also try using kprintf but likewise, nothing gets printed on sashimi. Last edited by Sim085; 12 February 2022 at 08:01. |
||
12 February 2022, 09:21 | #8 |
Total Chaos forever!
Join Date: Aug 2007
Location: Waterville, MN, USA
Age: 49
Posts: 2,186
|
Use only DOS.library functions to access files. The static libs invoked by fopen and fprintf are not reentrant code. Never use stdio.h from a shared library. It might crash the system.
Also: be sure to open DOS.library explicitly in the library startup code and close it in the shutdown code. Auto opening invokes another static library so it also can crash the system. Last edited by Samurai_Crow; 12 February 2022 at 09:28. |
12 February 2022, 10:32 | #9 | ||||
Registered User
Join Date: Jan 2019
Location: Germany
Posts: 3,215
|
Quote:
Whatever it is, I would probably suggest a compiler that has somewhat better support to create libaries. SAS/C with its LIBCODE flag can take over a lot of the lower level magic for you. Quote:
A library is just a big table of function pointers at negative offset from a struct Library, plus a calling convention to put the pointer to this function table in register a6. Quote:
For the very same reason "global" and static objects should not be used in the library unless they are "const". Again, such objecs exist only once per library intance, and as the library is just a thin layer around your function, they are shared by every caller of your library. Thus, if you modify an object with global linkage in a function,that change becomes visible immediately to every other caller of the same function (or every other function that uses that object). Since you do have no control on which task calls which library function at which point in time, you run into the risk of "data races", of objects that are modified (by a second task) under the feet of the calling task. Actually, C stdio depends on a couple of such global objects (such as the file table) and these objects are not even initialized properly as the C startup code (which usually does that) is not run. Thus - don't. stdio and library code do not mix. Quote:
Actually, traditional debuggers are not well prepared to debug library code. However, if your library opens, libinit has been called - at some point in time. Probably from of an earlier version of your same library if you run through mulitple debug cycles. Your library does not go away just because the using program dies. Instead, the Os removes libraries whenever it "feels like it", potentially because memory runs low. In such a case, loaded but unused libraries are flushed from memory. Actually, your library has to do that itself through the LibExpunge() vector which you have to provide, and which has to do the cleanup job, all provided there are no users of your library left. So again, it is all up to you again to remove your library from the system if the Os asks for it. To trigger a library expunge, type "avail flush" from a shell, or use the "flush libraries" from the workbench debug menu if that is enabled. |
||||
12 February 2022, 13:06 | #10 | ||||
Natteravn
Join Date: Nov 2009
Location: Herford / Germany
Posts: 2,496
|
Quote:
When calling such a function from C then either the amiga.lib stub routine will load the library base for you (from a static variable with a fixed name, like SysBase), or a macro/pragma will do it, before calling compiler-specific inline code. In any case, you can always rely on A6 having your library base. Quote:
There are only few clib functions which are safe to use, like those from <string.h>. Quote:
#define NULL ((void *)0), which helps you to detect these bugs (unlike the AmigaOS headers, which define NULL as 0L). Quote:
__asm("\tdc.w\t$cf4f");. Then type the command fi cf4fin the UAE-debugger to break on it. Then trace through the following code. |
||||
17 February 2022, 11:31 | #11 |
Registered User
Join Date: Apr 2009
Location: N/A
Posts: 962
|
Thank you all for all the help. I took some time to try and absorb everything and then I started again.
First I wanted to ensure my library was initializing and opening correctly, so I simply created the library, found how to enable debugging and using kprintf confirmed library is loading correctly from the application. While I still have MyLibBase declared as global, I do not make use of this reference directly from any function (apart when initializing this). The problem starts when I try to add a new function to call from the application. Given I do not want to use MyLibBase global reference I have declared my function as follows; Code:
SAVEDS int mylib_myfunc(REG(a6) struct Library *base) { DEBUGLOG(kprintf("mylib_myfunc() called.\n");) return 1; } Next I created /clib/mylib_protos.h as follows; Code:
... int mylib_myfunc(struct Library *); ... Code:
... mylib_myfunc(base)(a6) ... fd2pragma special 70 mylib_lib.fd clib /include/clib/mylib_protos.h So my /inlines/mylib_protos.h has the following; Code:
... int __mylib_myfunc(__reg("a6") struct Library *)="\tjsr\t-30(a6)"; #define mylib_myfunc() __mylib_myfunc(MyLibBase) ... I moved /clib/mylib_protos.h, /proto/mylib.h, /inline/mylib_protos.h and mylib.lib to my application source directory. Only think I changes is the includes in /proto/mylib.h so instead of including <clib/mylib_protos.h> and <inline/mylib_protos.h> I include them as "/clib/mylib_protos.h" and "/inline/mylib_protos.h" (as these are in my source directory). I updated my makefile to include the link library (... -lamiga mylib.lib) I compile... But I get the following error... Code:
Reference to undefined symbol _MyLibBase vlink failed return code 20 From what I can see the "undefined symbol _MyLibBase" is in relation to the following entry in the /inlines/mylib_protos.h file. Code:
#define mylib_myfunc() __mylib_myfunc(MyLibBase) Code:
#ifndef __NOLIBBASE__ extern struct Library *MyLibBase; #endif So I am little bit stuck Last edited by Sim085; 17 February 2022 at 11:53. |
17 February 2022, 12:06 | #12 | |
bye
Join Date: Jun 2016
Location: Some / Where
Posts: 680
|
Quote:
you need struct Library *MyLibBase; in your code. |
|
17 February 2022, 13:23 | #13 |
Registered User
Join Date: Dec 2010
Location: Athens/Greece
Age: 53
Posts: 719
|
|
17 February 2022, 14:15 | #14 | |||||||
Natteravn
Join Date: Nov 2009
Location: Herford / Germany
Posts: 2,496
|
Right. Your test application does not link, because you didn't define a Library Base. The
externkeyword means that the symbol is externally defined. No memory will be allocated by this statement. It is just a type declaration. Somebody has to define it (typically your application)! But there are more serious problems in the library itself. Quote:
Quote:
mylib_myfuncis not your library's real function name, to avoid name conflicts. This name is only important for entering it into your LibraryVectorstable. Nothing else. It may even be static (local), when the function table is part of the same file. On a sidenote, SAVEDS doesn't hurt, but is not required at all, as long as you don't use the small data model inside your library (which is quite uncommon). Quote:
Code:
int myfunc(void); Quote:
Code:
myfunc()() Quote:
Quote:
mylib.libas an object file would effectively add the whole link library to your executable, instead of just the objects which have symbol references. Tell the linker that it is a library: ... -lamiga -lmylib. Also add another search path with -L, when needed. To give another example, including the AddThese() function from Alkis' example library would look like this: Code:
int mylib_AddThese(REG(a6) struct Library *MyLibBase, REG(d0) int a, REG(d1) int b) { return a + b; } int AddThese(int, int); FD-File: AddThese(a,b)(d0,d1) And if you have to use functions from other shared libraries, open them in your LibraryInit function and store their base in your library base. Example for a function using DOS Delay(): Code:
#include <proto/dos.h> ... void mylib_WaitSeconds(REG(a6) struct MyLibBase *base, REG(d0) int seconds) { __Delay(base->DOSBase, seconds*50); } void WaitSeconds(int); FD: WaitSeconds(seconds)(d0) Quote:
Last edited by phx; 17 February 2022 at 14:42. Reason: -lmy -> -lmylib |
|||||||
17 February 2022, 16:41 | #15 | |
Registered User
Join Date: Dec 2010
Location: Athens/Greece
Age: 53
Posts: 719
|
Quote:
Code:
#define DOSBase base->DOSBase (example here http://franke.ms/cex/z/sfGhsj ) |
|
17 February 2022, 21:25 | #16 | |
Registered User
Join Date: Apr 2009
Location: N/A
Posts: 962
|
I have applied all the fixes (renamed function in library, modified fd and clib header file, used -L in make file for the link library, etc.) and the good news is that it works .
Thank you so much for all the help with this. Quote:
Code:
struct Library *MyLibBase = OpenLibrary(".../.../MyLib.library"); if(MyLibBase) { mylib_myfunc(); } Last edited by Sim085; 17 February 2022 at 21:43. |
|
17 February 2022, 22:01 | #17 | |
Natteravn
Join Date: Nov 2009
Location: Herford / Germany
Posts: 2,496
|
Quote:
|
|
21 February 2022, 13:42 | #18 |
Registered User
Join Date: Apr 2009
Location: N/A
Posts: 962
|
I just realized my test application is compiling and running successfully regardless if I include the link library or not (-LMyLib.lib).
When is the link library required (or not required)? |
21 February 2022, 13:53 | #19 |
Total Chaos forever!
Join Date: Aug 2007
Location: Waterville, MN, USA
Age: 49
Posts: 2,186
|
The link library is not required if you use the inlines/pragmas and required if you don't.
|
21 February 2022, 13:59 | #20 |
Registered User
Join Date: Apr 2009
Location: N/A
Posts: 962
|
|
Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
Thread Tools | |
Similar Threads | ||||
Thread | Thread Starter | Forum | Replies | Last Post |
Dynamic Drums | illy5603 | request.Music | 2 | 21 September 2017 21:39 |
Dynamic HDF - Please explain | ransom1122 | support.WinUAE | 18 | 04 January 2017 07:32 |
creating a dynamic exec library with vbcc ? | weiju | Coders. System | 2 | 20 April 2015 19:13 |
Dynamic Palette Ripper | Yesideez | Coders. General | 0 | 14 May 2010 18:48 |
Dynamic HiRes not working | Pyromania | support.Demos | 3 | 09 May 2003 19:40 |
|
|