2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
5 Loader for shared libraries and devices.
8 #include <aros/asmcall.h>
9 #include <aros/debug.h>
10 #include <aros/symbolsets.h>
11 #include <exec/execbase.h>
12 #include <exec/resident.h>
13 #include <exec/memory.h>
14 #include <exec/errors.h>
15 #include <exec/devices.h>
16 #include <exec/ports.h>
17 #include <exec/alerts.h>
18 #include <exec/tasks.h>
20 #include <dos/dosextens.h>
21 #include <dos/dostags.h>
22 #include <proto/exec.h>
23 #include <proto/dos.h>
30 #define CHECK_DEPENDENCY 1
32 /* Please leave them here! They are needed on Linux-M68K */
33 AROS_LD2(struct Library
*, OpenLibrary
,
34 AROS_LDA(STRPTR
, libname
, A1
),
35 AROS_LDA(ULONG
, version
, D0
),
36 struct ExecBase
*, SysBase
, 0, Dos
);
37 AROS_LD4(LONG
, OpenDevice
,
38 AROS_LDA(STRPTR
, devname
, A0
),
39 AROS_LDA(IPTR
, unitNumber
, D0
),
40 AROS_LDA(struct IORequest
*, iORequest
, A1
),
41 AROS_LDA(ULONG
, flags
, D1
),
42 struct ExecBase
*, SysBase
, 0, Dos
);
43 AROS_LD1(void, CloseLibrary
,
44 AROS_LDA(struct Library
*, library
, A1
),
45 struct ExecBase
*, SysBase
, 0, Dos
);
46 AROS_LD1(void, CloseDevice
,
47 AROS_LDA(struct IORequest
*, iORequest
, A1
),
48 struct ExecBase
*, SysBase
, 0, Dos
);
49 AROS_LD1(void, RemLibrary
,
50 AROS_LDA(struct Library
*, library
, A1
),
51 struct ExecBase
*, SysBase
, 0, Dos
);
55 struct Message ldd_Msg
; /* Message link */
56 struct MsgPort ldd_ReplyPort
; /* Callers ReplyPort */
58 STRPTR ldd_Name
; /* Name of thing to load */
60 STRPTR ldd_BaseDir
; /* Base directory to load from */
61 BPTR ldd_Return
; /* Loaded seglist */
64 static const char ldDemonName
[] = "Lib & Dev Loader Daemon";
67 BPTR LDLoad( caller, name, basedir, DOSBase )
68 Try and load a segment from disk for the object <name>, relative
69 to directory <basedir>. Will also try <caller>'s current and home
72 static BPTR
LDLoad(struct Process
*caller
, STRPTR name
, STRPTR basedir
,
73 struct Library
*DOSBase
, struct ExecBase
*SysBase
)
75 struct Process
*me
= (struct Process
*)FindTask(NULL
);
82 If the caller was a process, we have more scope for loading
83 libraries. We can load them from the callers current directory,
84 or from the PROGDIR: assign. These could both be the same
88 "[LDLoad] caller=(%p) %s, name=%s, basedir=%s\n",
89 caller
, caller
->pr_Task
.tc_Node
.ln_Name
, name
, basedir
92 if (strncmp(name
, "PROGDIR:", 8) == 0)
94 /* Special case for explicit PROGDIR-based path */
95 if (caller
->pr_Task
.tc_Node
.ln_Type
== NT_PROCESS
)
97 if (caller
->pr_HomeDir
!= BNULL
)
99 BPTR oldHomeDir
= me
->pr_HomeDir
;
100 D(bug("[LDLoad] Trying homedir\n"));
101 /* Temporarily override pr_HomeDir to let GetDeviceProc handle
102 PROGDIR: case correctly while opening library file */
103 me
->pr_HomeDir
= caller
->pr_HomeDir
;
104 seglist
= LoadSeg(name
);
105 me
->pr_HomeDir
= oldHomeDir
;
109 else if (!strstr(name
, ":")) {
110 delimPos
= strlen(basedir
);
111 pathLen
= delimPos
+ strlen(name
) + 2;
112 path
= AllocMem(pathLen
, MEMF_ANY
);
114 strcpy(path
, basedir
);
115 path
[delimPos
] = '/';
116 strcpy(&path
[delimPos
+ 1], name
);
118 if (caller
->pr_Task
.tc_Node
.ln_Type
== NT_PROCESS
)
120 /* Try the current directory of the caller */
122 D(bug("[LDLoad] Process\n"));
123 me
->pr_CurrentDir
= caller
->pr_CurrentDir
;
124 D(bug("[LDLoad] Trying currentdir\n"));
125 seglist
= LoadSeg(name
);
126 if ((!seglist
) && path
)
127 seglist
= LoadSeg(path
);
129 /* The the program directory of the caller */
130 if((!seglist
) && (caller
->pr_HomeDir
!= BNULL
))
132 D(bug("[LDLoad] Trying homedir\n"));
133 me
->pr_CurrentDir
= caller
->pr_HomeDir
;
134 seglist
= LoadSeg(name
);
135 if ((!seglist
) && path
)
136 seglist
= LoadSeg(path
);
142 /* Nup, let's try the default directory as supplied. */
143 D(bug("[LDLoad] Trying defaultdir\n"));
144 path
[delimPos
] = ':';
145 seglist
= LoadSeg(path
);
147 FreeMem(path
, pathLen
);
150 seglist
= LoadSeg(name
);
156 Library *LDInit(seglist, DOSBase)
157 Initialise the library.
159 static struct Library
*LDInit(BPTR seglist
, struct List
*list
, struct ExecBase
*SysBase
)
163 /* we may not have any extension fields */
164 const int sizeofresident
= offsetof(struct Resident
, rt_Init
) + sizeof(APTR
);
168 STRPTR addr
= (STRPTR
)((IPTR
)BADDR(seg
) - sizeof(ULONG
));
169 ULONG size
= *(ULONG
*)addr
;
172 addr
+= sizeof(BPTR
) + sizeof(ULONG
),
173 size
-= sizeof(BPTR
) + sizeof(ULONG
);
174 size
>= sizeofresident
;
176 // size -= AROS_PTRALIGN, addr += AROS_PTRALIGN
179 struct Resident
*res
= (struct Resident
*)addr
;
180 if( res
->rt_MatchWord
== RTC_MATCHWORD
181 && res
->rt_MatchTag
== res
)
185 D(bug("[LDInit] Calling InitResident(%p) on %s\n", res
, res
->rt_Name
));
186 /* AOS compatibility requirement.
187 * Ramlib ignores InitResident() return code.
188 * After InitResident() it checks if lib/dev appeared
189 * in Exec lib/dev list via FindName().
191 * Evidently InitResident()'s return code was not
192 * reliable for some early AOS libraries.
195 InitResident(res
, seglist
);
196 node
= FindName(list
, res
->rt_Name
);
198 D(bug("[LDInit] Done calling InitResident(%p) on %s, seg %p, node %p\n", res
, res
->rt_Name
, BADDR(seglist
), node
));
200 return (struct Library
*)node
;
203 seg
= *(BPTR
*)BADDR(seg
);
205 D(bug("[LDInit] Couldn't find Resident for %p\n", seglist
));
209 #define ExecOpenLibrary(libname, version) \
210 AROS_CALL2(struct Library *, ldBase->__OpenLibrary, \
211 AROS_LCA(STRPTR, libname, A1), \
212 AROS_LCA(ULONG, version, D0), \
213 struct ExecBase *, SysBase)
215 #define ExecOpenDevice(devname, unitNumber, iORequest, flags) \
216 AROS_CALL4(LONG, ldBase->__OpenDevice, \
217 AROS_LCA(STRPTR, devname, A0), \
218 AROS_LCA(IPTR, unitNumber, D0), \
219 AROS_LCA(struct IORequest *, iORequest, A1), \
220 AROS_LCA(ULONG, flags, D1), \
221 struct ExecBase *, SysBase)
225 struct Node ldon_Node
;
226 struct SignalSemaphore ldon_SigSem
;
227 ULONG ldon_AccessCount
;
229 struct Task
*ldon_FirstLocker
;
233 static struct LDObjectNode
*LDNewObjectNode(STRPTR name
, struct ExecBase
*SysBase
)
235 struct LDObjectNode
*ret
= AllocVec(sizeof(struct LDObjectNode
), MEMF_ANY
);
238 ULONG len
= strlen(name
);
239 STRPTR dupname
= AllocVec(len
+1, MEMF_ANY
);
242 CopyMem(name
, dupname
, len
);
244 ret
->ldon_Node
.ln_Name
= dupname
;
245 InitSemaphore(&ret
->ldon_SigSem
);
246 ret
->ldon_AccessCount
= 0;
249 ret
->ldon_FirstLocker
= FindTask(0);
260 static struct LDObjectNode
*LDRequestObject(STRPTR libname
, ULONG version
, STRPTR dir
, struct List
*list
, struct ExecBase
*SysBase
)
262 /* We use FilePart() because the liblist is built from resident IDs,
263 and contain no path. Eg. The user can request gadgets/foo.gadget,
264 but the resident only contains foo.gadget
266 struct LDDemonBase
*ldBase
= SysBase
->ex_RamLibPrivate
;
267 struct Library
*DOSBase
= ldBase
->dl_DOSBase
;
268 STRPTR stripped_libname
= FilePart(libname
);
269 struct Library
*tmplib
;
270 struct LDObjectNode
*object
;
273 We get the DOS semaphore to prevent the following:
274 - task 1 tries to open foobar.library, needs to load it from disk...
275 - task 1 Permit()'s (since it's not doing list things)
276 - task switch (whilst LDDemon MAY get process next it might not)
277 - task 2 tries to open foobar.library, needs to load it from disk...
278 - it also requests LDDemon to open foobar.library, so it is now
279 trying to open it twice
281 We block all OpenLibrary() callers from searching the list until
282 all the other OpenLibrary() callers have returned. That way,
283 task #2 won't ask for foobar.library until task #1 has got its
284 response back from the LDDemon process.
286 falemagn: I changed the implementation of all that.
287 There's a list of "LDObjectNodes", that contain the name
288 of the object being opened. Since the problem is that more
289 processes can attempt to open the same device/library. Instead of
290 locking a global semaphore until the opening is done, we lock a
291 per-object semaphore, so that others libraries/devices can be opened
292 in the meantime. Before, a deadlock could happen if there was a
295 Process A opens L --------> LDDemon loads L and locks sem S
301 L spawns a process B and ----------> The process opens
302 waits for it to respond a library but gets loked
303 to a message <----/---- because sem S is locked
310 Hopefully this won't happen anymore now.
312 ObtainSemaphore(&ldBase
->dl_LDObjectsListSigSem
);
314 object
= (struct LDObjectNode
*)FindName(&ldBase
->dl_LDObjectsList
, stripped_libname
);
317 object
= LDNewObjectNode(stripped_libname
, SysBase
);
320 AddTail(&ldBase
->dl_LDObjectsList
, (struct Node
*)object
);
326 object
->ldon_AccessCount
+= 1;
329 ReleaseSemaphore(&ldBase
->dl_LDObjectsListSigSem
);
334 ObtainSemaphore(&object
->ldon_SigSem
);
336 /* Try to find the resident in the list */
337 tmplib
= (struct Library
*)FindName(list
, stripped_libname
);
341 /* Try to load from disk if not found */
344 ldd
.ldd_ReplyPort
.mp_SigBit
= SIGB_SINGLE
;
345 ldd
.ldd_ReplyPort
.mp_SigTask
= FindTask(NULL
);
346 ldd
.ldd_ReplyPort
.mp_Flags
= PA_SIGNAL
;
347 ldd
.ldd_ReplyPort
.mp_Node
.ln_Type
= NT_MSGPORT
;
349 NEWLIST(&ldd
.ldd_ReplyPort
.mp_MsgList
);
351 ldd
.ldd_Msg
.mn_Node
.ln_Type
= NT_MESSAGE
;
352 ldd
.ldd_Msg
.mn_Length
= sizeof(struct LDDMsg
);
353 ldd
.ldd_Msg
.mn_ReplyPort
= &ldd
.ldd_ReplyPort
;
355 ldd
.ldd_Name
= libname
;
356 ldd
.ldd_BaseDir
= dir
;
358 SetSignal(0, SIGF_SINGLE
);
359 D(bug("[LDCaller] Sending request for %s\n", stripped_libname
));
361 PutMsg(ldBase
->dl_LDDemonPort
, (struct Message
*)&ldd
);
362 WaitPort(&ldd
.ldd_ReplyPort
);
364 D(bug("[LDCaller] Returned 0x%p\n", ldd
.ldd_Return
));
368 tmplib
= LDInit(ldd
.ldd_Return
, list
, SysBase
);
370 UnLoadSeg(ldd
.ldd_Return
);
377 * The library is not on disk so check Resident List.
378 * It can be there if it is resident but was flushed.
380 struct Resident
*resident
= FindResident(stripped_libname
);
383 * Check if the resident is of required type and version is correct.
384 * This relies on the fact that lh_Type is set correctly for exec lists.
385 * In AROS this is true (see rom/exec/prepareexecbase.c).
387 if (resident
&& (resident
->rt_Type
== list
->lh_Type
) && (resident
->rt_Version
>= version
))
388 InitResident(resident
, BNULL
);
394 static void LDReleaseObject(struct LDObjectNode
*object
, struct ExecBase
*SysBase
)
396 struct LDDemonBase
*ldBase
= SysBase
->ex_RamLibPrivate
;
399 Release the semaphore here, after calling Open vector. This
400 means that library open is singlethreaded by the semaphore.
401 It also handles circular dependant libraries. (Won't deadlock),
402 and recursive OpenLibrary calls (Semaphores nest when obtained
403 several times in a row by the same task).
406 ObtainSemaphore(&ldBase
->dl_LDObjectsListSigSem
);
408 if (--(object
->ldon_AccessCount
) == 0)
410 Remove((struct Node
*)object
);
412 * CHECKME: In LDRequestObject() we obtain the object semaphore also on a new object.
413 * So shouldn't we release it here too ?
416 FreeVec(object
->ldon_Node
.ln_Name
);
420 ReleaseSemaphore(&object
->ldon_SigSem
);
422 ReleaseSemaphore(&ldBase
->dl_LDObjectsListSigSem
);
425 AROS_LH2(struct Library
*, OpenLibrary
,
426 AROS_LHA(STRPTR
, libname
, A1
),
427 AROS_LHA(ULONG
, version
, D0
),
428 struct ExecBase
*, SysBase
, 0, Dos
)
432 struct LDDemonBase
*ldBase
= SysBase
->ex_RamLibPrivate
;
433 struct Library
*library
;
434 struct LDObjectNode
*object
= LDRequestObject(libname
, version
, "libs", &SysBase
->LibList
, SysBase
);
439 /* Call the EXEC's OpenLibrary function */
440 library
= ExecOpenLibrary(object
->ldon_Node
.ln_Name
, version
);
442 LDReleaseObject(object
, SysBase
);
449 AROS_LH4(LONG
, OpenDevice
,
450 AROS_LHA(STRPTR
, devname
, A0
),
451 AROS_LHA(IPTR
, unitNumber
, D0
),
452 AROS_LHA(struct IORequest
*, iORequest
, A1
),
453 AROS_LHA(ULONG
, flags
, D1
),
454 struct ExecBase
*, SysBase
, 0, Dos
)
458 struct LDObjectNode
*object
;
459 struct LDDemonBase
*ldBase
= SysBase
->ex_RamLibPrivate
;
461 object
= LDRequestObject(devname
, 0, "devs", &SysBase
->DeviceList
, SysBase
);
464 /* Call exec.library/OpenDevice(), it will do the job */
465 ExecOpenDevice(object
->ldon_Node
.ln_Name
, unitNumber
, iORequest
, flags
);
466 LDReleaseObject(object
, SysBase
);
470 iORequest
->io_Error
= IOERR_OPENFAIL
;
471 iORequest
->io_Device
= NULL
;
472 iORequest
->io_Unit
= NULL
;
475 D(bug("[LDCaller] Open result: %d\n", iORequest
->io_Error
));
477 return iORequest
->io_Error
;
482 AROS_LH1(void, CloseLibrary
,
483 AROS_LHA(struct Library
*, library
, A1
),
484 struct ExecBase
*, SysBase
, 0, Dos
)
488 struct LDDemonBase
*ldBase
= SysBase
->ex_RamLibPrivate
;
489 struct Library
*DOSBase
= ldBase
->dl_DOSBase
;
492 if( library
!= NULL
)
495 seglist
= AROS_LVO_CALL0(BPTR
, struct Library
*, library
, 2, );
498 ldBase
->dl_LDReturn
= MEM_TRY_AGAIN
;
500 /* Safe to call from a Task */
509 AROS_LH1(void, CloseDevice
,
510 AROS_LHA(struct IORequest
*, iORequest
, A1
),
511 struct ExecBase
*, SysBase
, 0, Dos
)
514 struct LDDemonBase
*ldBase
= SysBase
->ex_RamLibPrivate
;
515 struct Library
*DOSBase
= ldBase
->dl_DOSBase
;
516 BPTR seglist
= BNULL
;
519 if( iORequest
->io_Device
!= NULL
)
521 seglist
= AROS_LVO_CALL1(BPTR
,
522 AROS_LCA(struct IORequest
*, iORequest
, A1
),
523 struct Device
*, iORequest
->io_Device
, 2, );
524 iORequest
->io_Device
=(struct Device
*)-1;
527 ldBase
->dl_LDReturn
= MEM_TRY_AGAIN
;
535 AROS_LH1(void, RemLibrary
,
536 AROS_LHA(struct Library
*, library
, A1
),
537 struct ExecBase
*, SysBase
, 0, Dos
)
541 struct LDDemonBase
*ldBase
= SysBase
->ex_RamLibPrivate
;
542 struct Library
*DOSBase
= ldBase
->dl_DOSBase
;
546 /* calling ExpungeLib: library ends up in D0 and A6 for compatibility */
547 seglist
= AROS_CALL1(BPTR
, __AROS_GETVECADDR(library
, 3),
548 AROS_LCA(struct Library
*, library
, D0
),
549 struct Library
*, library
553 ldBase
->dl_LDReturn
= MEM_TRY_AGAIN
;
561 AROS_UFH3(LONG
, LDFlush
,
562 AROS_UFHA(struct MemHandlerData
*, lmhd
, A0
),
563 AROS_UFHA(APTR
, data
, A1
),
564 AROS_UFHA(struct ExecBase
*, SysBase
, A6
)
569 struct LDDemonBase
*ldBase
= SysBase
->ex_RamLibPrivate
;
570 struct Library
*library
;
572 D(bug("[LDDemon] Flush called\n"));
573 ldBase
->dl_LDReturn
= MEM_DID_NOTHING
;
575 /* Forbid() is already done, but I don't want to rely on it. */
578 /* Follow the linked list of shared libraries. */
579 library
= (struct Library
*)SysBase
->LibList
.lh_Head
;
580 while(library
->lib_Node
.ln_Succ
!= NULL
)
582 /* Flush libraries with a 0 open count */
583 if( ! library
->lib_OpenCnt
)
585 /* the library list node will be wiped from memory */
586 struct Library
*nextLib
= (struct Library
*)library
->lib_Node
.ln_Succ
;
588 /* Did it really go away? */
589 if( ldBase
->dl_LDReturn
!= MEM_DID_NOTHING
)
591 /* Yes! Return it. */
593 return MEM_TRY_AGAIN
;
599 /* Go on to next library. */
600 library
= (struct Library
*)library
->lib_Node
.ln_Succ
;
604 /* Do the same with the device list. */
605 library
= (struct Library
*)SysBase
->DeviceList
.lh_Head
;
606 while(library
->lib_Node
.ln_Succ
!= NULL
)
608 /* Flush libraries with a 0 open count */
609 if( ! library
->lib_OpenCnt
)
611 struct Library
*nextDev
= (struct Library
*)library
->lib_Node
.ln_Succ
;
612 RemDevice((struct Device
*)library
);
613 /* Did it really go away? */
614 if( ldBase
->dl_LDReturn
!= MEM_DID_NOTHING
)
616 /* Yes! Return it. */
618 return MEM_TRY_AGAIN
;
624 /* Go on to next library. */
625 library
= (struct Library
*)library
->lib_Node
.ln_Succ
;
629 return MEM_DID_NOTHING
;
636 The LDDemon process entry. Sits around and does nothing until a
637 request for a library comes, when it will then find the library
638 and hopefully open it.
640 static AROS_ENTRY(VOID
, LDDemon
,
641 AROS_UFHA(APTR
, argptr
, A0
),
642 AROS_UFHA(APTR
, argsize
, D0
),
643 struct ExecBase
*, SysBase
)
647 struct LDDemonBase
*ldBase
= SysBase
->ex_RamLibPrivate
;
648 struct Library
*DOSBase
= ldBase
->dl_DOSBase
;
653 WaitPort(ldBase
->dl_LDDemonPort
);
654 while( (ldd
= (struct LDDMsg
*)GetMsg(ldBase
->dl_LDDemonPort
)) )
656 D(bug("[LDDemon] Got a request for %s in %s\n", ldd
->ldd_Name
, ldd
->ldd_BaseDir
));
658 ldd
->ldd_Return
= LDLoad(ldd
->ldd_ReplyPort
.mp_SigTask
, ldd
->ldd_Name
, ldd
->ldd_BaseDir
, DOSBase
, SysBase
);
660 D(bug("[LDDemon] Replying with %p as result\n", ldd
->ldd_Return
));
661 ReplyMsg((struct Message
*)ldd
);
662 } /* messages available */
668 static ULONG
LDDemon_Init(struct LDDemonBase
*ldBase
)
670 struct Library
*DOSBase
;
671 struct TagItem tags
[] =
673 { NP_Entry
, (IPTR
)LDDemon
},
676 { NP_WindowPtr
, -1 },
677 { NP_Name
, (IPTR
)ldDemonName
},
682 DOSBase
= TaggedOpenLibrary(TAGGEDOPEN_DOS
);
684 Alert( AN_RAMLib
| AG_OpenLib
| AO_DOSLib
| AT_DeadEnd
);
687 if ((ldBase
->dl_LDDemonPort
= CreateMsgPort()) == NULL
)
689 Alert( AN_RAMLib
| AG_NoMemory
| AT_DeadEnd
);
692 FreeSignal(ldBase
->dl_LDDemonPort
->mp_SigBit
);
693 ldBase
->dl_LDDemonPort
->mp_SigBit
= SIGBREAKB_CTRL_F
;
695 ldBase
->dl_LDHandler
.is_Node
.ln_Name
= (STRPTR
)ldDemonName
;
696 ldBase
->dl_LDHandler
.is_Node
.ln_Pri
= 0;
697 ldBase
->dl_LDHandler
.is_Code
= (void (*)())LDFlush
;
698 ldBase
->dl_LDHandler
.is_Data
= NULL
;
700 ldBase
->dl_DOSBase
= DOSBase
;
702 NEWLIST(&ldBase
->dl_LDObjectsList
);
703 InitSemaphore(&ldBase
->dl_LDObjectsListSigSem
);
705 SysBase
->ex_RamLibPrivate
= ldBase
;
707 AddMemHandler(&ldBase
->dl_LDHandler
);
710 * Grab the semaphore ourself. The reason for this is that it will
711 * cause all other tasks to wait until we have finished initialising
712 * before they try and open something.
714 ObtainSemaphore(&ldBase
->dl_LDObjectsListSigSem
);
716 #define SetFunc(offs,ptr) \
717 SetFunction(&SysBase->LibNode, (-offs)*(LONG)LIB_VECTSIZE, \
718 AROS_SLIB_ENTRY(ptr,Dos,0))
720 /* Do not set the vectors until you have initialised everything else. */
721 ldBase
->__OpenLibrary
= SetFunc(92, OpenLibrary
);
722 ldBase
->__OpenDevice
= SetFunc(74, OpenDevice
);
723 (void)SetFunc(69, CloseLibrary
);
724 (void)SetFunc(75, CloseDevice
);
725 (void)SetFunc(67, RemLibrary
);
726 (void)SetFunc(73, RemLibrary
);
728 if( !(ldBase
->dl_LDDemonTask
= CreateNewProc((struct TagItem
*)tags
)) )
730 Alert( AT_DeadEnd
| AN_RAMLib
| AG_ProcCreate
);
733 /* Fix up the MsgPort */
735 ldBase
->dl_LDDemonPort
->mp_SigTask
= ldBase
->dl_LDDemonTask
;
737 /* Then unlock the semaphore to allow other processes to run. */
738 ReleaseSemaphore(&ldBase
->dl_LDObjectsListSigSem
);
743 ADD2INITLIB(LDDemon_Init
, 0)