TIP: Click on subject to list as thread! ANSI
echo: os2prog
to: Denis Tonn
from: Jonathan de Boyne Pollard
date: 1998-10-06 09:14:28
subject: How do DLLs load and unload ?

JP>> All other referenced DLLs are loaded by a special user-
 JP>> mode function in DOSCALL1 that is where the first thread in 
 JP>> the process first starts executing.  

 DT>  That's pretty close to my understanding too.. 

Run an OS/2 program under a debugger, turning off the option to
automatically run initialisation code, and one can see it happening. 
There's a little loop in DOSCALL1 that executes before anything else.  It's
just difficult to work out what that loop does in the absence of any symbol
information.  (-:

JP> But this brings up a further question: What happens when a DLL cannot be 
JP> found ?  This should be, and *is*, reported in the pObjName 
JP> buffer for DosExecPgm.  I can see how this would be easy to 
JP> implement if it were the *parent* process that resolved all 
JP> of the import module references and built the initial user 
JP> address space of the child.  But how does this happen if 
JP> the loading is occurring as part of the execution of user-
JP> mode code in the child process ?  Don't tell me that 
JP> there's a back door in OS/2 Warp for user-mode code in one 
JP> process to write to the user address space of another 
JP> process!

 DT> The loader is part of the kernel code, even though it operates in the
 DT> context of the process (and is called on a thread of the process). 

Naturally.  Unless one has a client-server design like Windows NT 3.x, this
is the only sensible way to implement it.  I hope that Herbert Rosenau is
reading this.  (-:

 DT> Being part of the kernel code, it can switch contexts if required to 
 DT> access the parent address space...

Now *this* is interesting, because it implies that, essentially, a thread
can migrate back and forth between processes -- or, at least, migrate back
and forth between the address spaces that processes have.  It's a very ugly
design, to my mind, though.  To be honest, I think that having the parent
process be fully responsible for resolving all of the load-time DLLs as
well as DOSCALL1 and the main EXE would have been a far "cleaner"
solution.  

The solution that you describe means that there has to be special case code
in the loader for when it is loading "load-time" DLLs as opposed
to "run-time" DLLs.  This is an important point that I shall
return to in a bit.

 DT> Keep in mind that DosExecPgm is a kernel API, it does enter ring 0 
 DT> (through Doscall1) and can access all processes. 

It can access all of the kernel objects that represent processes, true. 
That is a given, and is how just about *all* multiprocess operating systems
are designed.  But what we are talking about is accessing the *user address
space* of a different process.  That's a far more involved procedure, and a
far more unusual one, because on most operating systems threads cannot
"migrate" between processes, or their address spaces, in this
fashion.  Threads switch between user and kernel mode, and can poke around
with the kernel-mode data structures of other processes.  But I've not
before encountered an operating system where a thread switches to the page
tables of a completely different process in order to directly access its
user address space.  All other operating systems that need to communicate
results in this way generally use message passing to achieve this, or some
common kernel data area.

 DT> Until the loader has resolved all the "load time" linking, the 
 DT> parent process is in a kind of "childwait", [...] Once the child is
 DT> loaded, the parent does not receive  notifications for explicit
 DT> DosLoadModule call done in the child. 

As I said above, this again implies that there is special case code for
load-time loading as opposed to run-time loading, to deal with the
different ways of reporting a load failure (for load-time the thread has to
temporarily switch address spaces and write to its parent process' address
space, for run-time the thread has to write to its own process' address
space).  This is not a particularly clean design, in my view.

It also causes some very strange race conditions.  Suppose that one thread
in the parent had called DosExecPgm.  This would cause the child to run,
and its primary thread to load all of the load-time DLLs.  Now suppose that
the child encountered a failure, *and* in the meantime another thread *in
the parent* had maliciously called DosFreeMem for the pObjName buffer that
the first thread had initially passed to DosExecPgm.  The primary thread in
the child process would switch address spaces, attempt to write the name of
the module that caused the failure to the buffer, and fault because the
pages were no longer valid.  This would have the bizarre effect of causing
a page fault in the *child* process, when, intuitively, one would expect
that an invalid buffer passed as an argument to a kernel function would
cause a page fault *in the thread that had passed the buffer*.  

And what, in the above scenario, happens to the first thread, that is
sitting patiently in "child wait" mode waiting for the child
process either to report either "all load-time DLLs have been
loaded" or "a load-time DLL failed, and I've written its name to
your result buffer for you".  The child process has died with a page
fault.  Does the parent thread simply hang forever in "child
wait" mode ?

I'm sorry for all of the questions.  But I'm trying to form the clearest
picture that I can of this whole area, and it has turned out to be a *very*
complicated subject, full of hidden pitfalls and nasty situations like the
above.

 ¯ JdeBP ®

--- FleetStreet 1.19 NR
* Origin: JdeBP's point, using Squish (2:440/4.3)
SEEN-BY: 396/1 632/0 371 633/210 260 267 270 371 635/506 728 639/252 670/218
@PATH: 440/4 255/1 251/25 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™.