| TIP: Click on subject to list as thread! | ANSI |
| echo: | |
|---|---|
| to: | |
| from: | |
| date: | |
| subject: | How do DLLs load and unl |
Original from Jonathan de Boyne Pollard to Denis Tonn on 09-21-1998
Original Subject: How do DLLs load and unlo
---------------------------------------
JP> I'm trying to figure out something, part of which you, with
JP> your knowledge of the internals of OS/2, may be able to
JP> help me with.
JP>
JP> How are DLLs loaded and unloaded ? In particular, how does OS/2 Warp keep
JP> track of which DLL_InitTerm functions it needs to call when
JP> executing DosFreeModule ? It seems to me that there is a
DosFreeModule takes a handle to a module. This is an index for an MTE
(Module Table Entry). There is one MTE per module loaded, regardless
of how many processes it is mapped into (also applies to EXE and other
modules). The system can also locate all the modules (actually
addresses) available to a process and map them to associated MTEs.
Once the InitTerm (term) has returned, the module is then "unmapped"
from the process (asynchronously at some release levels) and it's use
count decremented. It may still remain in the "system" if the use
count if not zero.
JP> chicken-and-egg problem here. It cannot know which
JP> InitTerm functions to call before it starts unloading
JP> modules from the address space, but it cannot unload the
JP> modules from the address space since they must remain
JP> mapped so that their InitTerm functions can be called. How
JP> does OS/2 solve this ?
InitTerm is called before the module is released from the process
address space. A dll may have to free private or shared address ranges
as part of it's cleanup.
If you are asking about the sequence that each DLL's InitTerm are
called during process end, I would have to check into it. I suspect it
is the reverse of the load sequence.. InitTerm may not always be
called (abnormal termination), which is where exception handlers and
exitlists come into play..
JP> If you are about to tell me that OS/2 uses callbacks into ring 3 from ring 0
JP> in order to call the InitTerm functions, how does it
JP> *return* from those callbacks into ring 0 ? And how does
Yep, it calls ring 3 from ring 0, and sets up a return address on the
stack that points into DOSCALL1. The code in DOSCALL1 then gets back
to ring 0 through a callgate (actually, the stack setup is done in
DOSCALL1). Simple stack manipulation ..
All this is done from within the context of a process. Kernel mode is
not required (even though ring 0 code is involved).
This kind of callback to Ring 3 from Ring 0 is needed in all kinds of
places (exception handling, exitlist processing, etc).
JP> it cope with the pathological, but permissible, case that
JP> the InitTerm function of a module may itself call
JP> DosLoadModule or DosFreeModule ? Or the equally
Calling DosLoadModule from within an init function is not all that
pathological, a dll may require other dll functions too.. Likewise
calling DosFreeModule from within term is just as valid. The sequences
that the kernel and doscall1 follows are the same..
Doing it the other way is what I would call "pathological". I can't
see a use for freeing a module during init, and the loading of a
module during term would just add it to the process for the duration
of the cleanup (and it's term would eventually get called). It's
possible to create a loop here during cleanup. I don't know if there
are any checks to prevent this. Term is called at process cleanup even
if the app does not explicitly free the dll, as part of DosExit
processing.
The loader will automaticly do a DosLoadModule (and fixup) for any
modules referenced in the header of the executable. I call this "load
time" linking vs "run time" linking done with an explicit
DosLoadModule and DosQueryProcAddr from within the app.
I don't like to use the terms "static" or "dynamic"
linking here,
they can have slightly different connotations in the context of a
compiler and it's run time libraries..
JP> pathological case that the InitTerm function, either
JP> accidentally or deliberately, never returns at all (leaving
JP> the kernel internals in an intermediate state) ?
All this is really process related, not "kernel" related. Many of
the control blocks for the process are contained in kernel address
space (heap), but are still related to a specific process.
If the term function "does not return", then the module is not freed
from the process, and the process can not end. You can do similar
kinds of things in exit handlers.. The "kernel" is unaffected, just
the process is in an "incomplete" state. As soon as the dll returns
(via doscall1 address on the stack), the kernel and/or doscall1 takes
the next step in the flow.
Think of the case of a DosStartThread where the thread runs totally
in DLL code, possibly started in the init function of the DLL. The
term function may have to wait until the started thread reaches a
"stable" state before it terminates the thread and returns. The
secondary thread may be working with private addresses, shared
addresses, files, pipes, etc..
I think if you reconsider your question with a couple of thoughts in
mind that it will all fall into place.
A) DOSCALL1 return addresses are involved in the ring0 > ring3 >ring0
transition. Doscall1 gets back to Ring0 through a call gate.
B) All this is process related, not "system" related. The module is
not freed from the system until the references to the module are zero.
There is one MTE for every module loaded, regardless of how many
process references there are to that module.
C) InitTerm are called in the context of the process. Ring 0 is not
always "Kernel mode".
BTW, Not directly related but: I have discussed it a couple of times
before, still it might be worth mentioning again. All modules (EXE,
DLL, SYS, etc) have an MTE. The system will do page mapping of the
readonly pages of this module across as many processes as that module
exists in. It does not matter if the module loads in the "private"
address space or not.
EG: x:\OS2\CMD.EXE loads into the private address space, multiple
processes using x:\OS2\CMD.EXE will "share" the readonly pages of
CMD.EXE (all code and readonly pages) implicitly. The way the system
determines if this is possible is via the fullpathname of the module
(contained in the MTE). If the MTE already exists in the system the
loader will only load the writable portions of the executable into the
new process and "map" the readonly pages into the process from
existing pages (and update the use count in the MTE).
If you had 2 copies of CMD.EXE in \os2 and in \os2\bin, and 2
processes used each of these modules they would NOT be "code page
shared". They would have separate MTEs, even if "module" is the same
code. This has implications for those people running multiple copies
of the same program (BBS sysops are one category). Less memory will be
used if the SAME copy (full path name!) of the executable is used in
all the processes. Copying a complete directory and modifying the
config files in the new directory is a common way of adding a process,
but keep the executables in one common directory and explicitly point
to them.
DLLs are exempt from the above kind of fullpath MTE search, you
cannot load 2 different DLLs of the same name into the system. Also,
Dos code running in a VDM cannot be shared this way (Dos "code" is
writable!)
Denis
All opinions are my very own, IBM has no claim upon them
.
.
.
--- Maximus/2 3.01
* Origin: T-Board - (604) 277-4574 (1:153/908)SEEN-BY: 396/1 632/0 371 633/210 260 267 270 371 635/506 728 639/252 670/218 @PATH: 153/908 8086 800 140/1 12/12 396/1 633/260 635/506 728 633/267 |
|
| SOURCE: echomail via fidonet.ozzmosis.com | |
Email questions or comments to sysop@ipingthereforeiam.com
All parts of this website painstakingly hand-crafted in the U.S.A.!
IPTIA BBS/MUD/Terminal/Game Server List, © 2025 IPTIA Consulting™.