Wiley.Symbian.OS.Internals.Real.time.Kernel.Programming.Dec.2005.eBook-DDU (779891), страница 88
Текст из файла (страница 88)
If an exceptional case is encountered with an XIP imagefile, the kernel can’t do this, since XIP files no longer have relocationinformation present. In this case, the load request is rejected.To implement sharing of code segments, the loader will search for analready loaded code segment with a root file name (ignoring the drive andpath, just including the file name and extension) matching the requestedfile before attempting to create a new code segment. The kernel providesa ‘‘find matching code segment’’ function for this purpose.
It finds thenext fully loaded code segment (that is, one that is relocated, and hasall imports loaded and fixed up) that matches the provided UIDs andattributes and that can be loaded into the client process. The loader thenKERNEL-SIDE CODE MANAGEMENT417checks the filename and, if it matches, opens a reference on the matchingcode segment and on each member of the sub-graph below it, markingeach one with EMarkLdr.To support the exceptional cases where sharing is not allowed, a codesegment may have one of two restrictions on its use:1. If the iExeCodeSeg field is non-null, it indicates that this codesegment may only be loaded into a process with that EXE codesegment. A code segment which links directly or indirectly to an EXEwill have this field pointing to the code segment representing thatEXE.
This restriction arises in the case where the code segment hasan EXE code segment in the sub-graph below it. When this restrictionapplies, the code segment could potentially be loaded into multipleprocesses, but these processes must all be instantiations of the sameEXE file2. If the iAttachProcess field is non-null, it indicates that this codesegment may only be loaded into that specific process. This restrictionarises in the case where the code segment, or one in the sub-graphbelow it, has .data or .bss and this data must reside at a uniqueaddress, for example if the code segment is loaded into a fixedprocess in the moving memory model. When the iAttachProcessfield is non-null, the iExeCodeSeg field points to the EXE codesegment of the attach process.
The iAttachProcess pointer isreference counted.Figures 10.7, 10.8 and 10.9 show example code graphs. They depict aprocess instantiated from an EXE file which links implicitly to five DLLs.Figure 10.7 shows the graph which results where all these modules arenon-XIP. Shaded modules signify the presence of writeable static data.Figure 10.8 shows the graph which results from loading the same processif two of the DLLs (E.DLL and F.DLL) are in XIP ROM. Since these DLLsare XIP and they have no writeable static data, they do not appear in thecode graph.
Figure 10.9 shows the graph resulting from loading the sameprocess where all modules are in XIP ROM. Only the EXE and any DLLswith writeable static data appear in the code graph.10.4.2 LibrariesThe kernel creates a kernel-side library object (DLibrary) for every DLLthat is explicitly loaded into a user process (in other words, one that is thetarget of an RLibrary::Load rather than a DLL that is implicitly linkedto by another DLL or EXE). Library objects are specific to, and owned by,the process for which they were created; if two processes both load thesame DLL, the kernel creates two separate DLibrary objects.
A libraryhas two main uses:418THE LOADER1.It represents a link from a process to the global code graph. Each process has at least one such connection – the DProcess::iCodeSegpointer links each process to its own EXE code segment. The loadercreates this link when loading the process. DLibrary objects represent additional such links that the kernel creates at run time2.It provides a state machine to ensure constructors and destructors forobjects resident in .data and .bss are called correctly.Libraries have two reference counts.
One is the standard DObjectreference count (since DLibrary derives from DObject); a non-zerovalue for this reference count simply stops the DLibrary itself beingdeleted – it does not stop the underlying code segment being deleted orremoved from any process. The second reference count (iMapCount)is the number of user references on the library, which is equal to thenumber of handles on the library opened by the process or by any ofits threads. This is always updated with the CodeSegLock mutex held.When the last user handle is closed, iMapCount will reach zero andthis triggers the calling of static destructors and the removal of the librarycode segment from the process address space. (We need a separate counthere because static destructors for a DLL must be called in user modewhenever a library is removed from a process, and those destructors mustbe called in the context of the process involved.
However, the normalreference count may be incremented and decremented kernel-side bythreads running in other processes, so it would not be acceptable to calldestructors when the normal reference count reached zero.) The loadercreates DLibrary objects on behalf of a client loading a DLL. A processmay only have one DLibrary referring to the same code segment. If aprocess loads the same library twice, a second handle will be opened onthe already existing DLibrary and its map count will be incremented.A DLibrary object transitions through the following states during itslife, as shown in Figure 10.10.• ECreated – transient state in which object is created. Switches toELoaded or EAttached when library and corresponding code segment are added to the target process.
The state transitions to ELoadedif constructors must be run in user mode and directly to EAttachedif no such constructors are required; this is the case if neither the DLLitself nor any DLLs that it depends on have writeable static data• ELoaded – code segment is loaded and attached to the target processbut static constructors have not been called• EAttaching – the target process is currently running the code segment static constructors.
Transition to EAttached when constructorshave completedKERNEL-SIDE CODE MANAGEMENT419• EAttached – static constructors have completed and the code segment is fully available for use by the target process• EDetachPending – the last user handle has been closed on theDLibrary (that is, the map count has reached zero) but staticdestructors have not yet been called. Transitions to EDetaching justbefore running static destructors• EDetaching – the target process is currently running the code segment static destructors. Transitions to ELoaded when destructorshave completed, so that if the library is reloaded by another thread inthe same process before being destroyed, the constructors run again.Problems could be caused by multiple threads in a process loading andunloading DLLs simultaneously – for example, static constructors runningin one thread while destructors for the same DLL run in another thread.To prevent this, each process has a mutex (the DLL lock), which protectsstatic constructors and destructors running in that process.
The mutex isheld for the entire duration of a dynamic library load from connectingto the loader to completion of static constructors for the DLL. It is alsoheld when unloading a library, from the point at which the iMapCountreaches zero to the point where the code segment is unmapped from theprocess following the running of static destructors. The kernel creates theDLL lock mutex, named DLL$LOCK, during process creation, but it isheld while running user-side code; it is the only mutex with this property.10.4.3 Loading a processThe kernel’s involvement in process loading begins after the loader hascompleted its search for the requested executable image and decidedwhich of the available files should be used to instantiate the new process.
The loader will have created an E32Image object on its heap,to represent the new image file it is loading. The loader then queriesthe kernel to discover if the selected image file is already loaded. TheE32Loader::CodeSegNext() API, which, like all the E32Loaderfunctions, is a kernel executive call, is used for this purpose. If the selectedimage file is an XIP image, the loader will already have found the addressof the TRomImageHeader describing it. In this case, the kernel willsearch for a code segment derived from the same TRomImageHeader.If the selected image file is not an XIP image, the loader will know thefull path name of the image file and will have read its E32Image header.The kernel will search for a code segment with the same root name, sameUIDs and same version number.