| TIP: Click on subject to list as thread! | ANSI |
| echo: | |
|---|---|
| to: | |
| from: | |
| date: | |
| 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™.