source: trunk/kLdr/kLdrDyldMod.c@ 2842

Last change on this file since 2842 was 2842, checked in by bird, 19 years ago

toplevel load api code is done.

File size: 8.5 KB
RevLine 
[2835]1/* $Id: $ */
2/** @file
3 *
4 * kLdr - The Dynamic Loader, Dyld module methods.
5 *
6 * Copyright (c) 2006 knut st. osmundsen <bird-kbuild-src@anduin.net>
7 *
8 *
9 * This file is part of kLdr.
10 *
11 * kLdr is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * kLdr is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with kLdr; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 */
26
27
28
29/*******************************************************************************
30* Header Files *
31*******************************************************************************/
32#include <kLdr.h>
33#include "kLdrInternal.h"
34#include "kLdrHlp.h"
35
36
[2837]37/*******************************************************************************
38* Defined Constants And Macros *
39*******************************************************************************/
40/** @def KLDRDYLDMOD_ASSERT
41 * Assert that an expression is true when KLDRDYLD_STRICT is defined.
42 */
43#ifdef KLDRDYLD_STRICT
44# define KLDRDYLDMOD_ASSERT(expr) kldrHlpAssert(expr)
45#else
46# define KLDRDYLDMOD_ASSERT(expr) do {} while (0)
47#endif
[2835]48
49
50
[2842]51/**
52 * Decrement the dynamic load count of the module and unload the module
53 * if the total reference count reaches zero.
54 *
55 * This may cause a cascade of unloading to occure. See kldrDyldModUnloadPrerequisites().
56 *
57 * @returns status code.
58 * @retval 0 on success.
59 * @retval KLDR_ERR_NOT_LOADED_DYNAMICALLY if the module wasn't loaded dynamically.
60 * @param pMod The module to unload.
61 */
62int kldrDyldModDynamicUnload(PKLDRDYLDMOD pMod)
63{
64 if (pMod->cDynRefs == 0)
65 return KLDR_ERR_NOT_LOADED_DYNAMICALLY;
66 KLDRDYLDMOD_ASSERT(pMod->cDynRefs <= pMod->cRefs);
67 KLDRDYLDMOD_ASSERT(pMod->enmState == KLDRSTATE_GOOD);
[2837]68
[2842]69 pMod->cRefs--;
70 pMod->cDynRefs--;
71 if (pMod->cRefs > 0)
72 return 0;
73
74 /*
75 * The module should be unloaded.
76 */
77 kldrDyldModUnloadPrerequisites(pMod);
78 return 0;
79}
80
81
82/**
83 * Worker for kldrDyldModUnloadPrerequisites.
84 *
85 * @returns The number of modules that now can be unloaded.
86 * @param pMod The module in question.
87 */
88static uint32_t kldrDyldModUnloadPrerequisitesOne(PKLDRDYLDMOD pMod)
89{
90 PKLDRDYLDMOD pMod2;
91 uint32_t cToUnload = 0;
92 uint32_t i;
93
94 KLDRDYLDMOD_ASSERT(pMod->papPrereqs);
95
96 /*
97 * Release the one in this module.
98 */
99 for (i = 0; i < pMod->cPrereqs; i++)
100 {
101 pMod2 = pMod->papPrereqs[i];
102 if (pMod2)
103 {
104 /* do the derefering ourselves or we'll end up in a recursive loop here. */
105 KLDRDYLDMOD_ASSERT(pMod2->cDepRefs > 0);
106 KLDRDYLDMOD_ASSERT(pMod2->cRefs >= pMod2->cDepRefs);
107 pMod2->cDepRefs--;
108 pMod2->cRefs--;
109 cToUnload += !pMod2->cRefs;
110 }
111 }
112
113 /*
114 * Change the state
115 */
116 switch (pMod->enmState)
117 {
118 case KLDRSTATE_LOADED_PREREQUISITES:
119 case KLDRSTATE_FIXED_UP:
120 pMod->enmState = KLDRSTATE_PENDING_DESTROY;
121 kldrDyldModUnlink(pMod);
122 break;
123
124 case KLDRSTATE_PENDING_INITIALIZATION:
125 pMod->enmState = KLDRSTATE_PENDING_GC;
126 break;
127
128 case KLDRSTATE_RELOADED_FIXED_UP:
129 case KLDRSTATE_RELOADED_LOADED_PREREQUISITES:
130 case KLDRSTATE_GOOD:
131 pMod->enmState = KLDRSTATE_PENDING_TERMINATION;
132 break;
133
134 case KLDRSTATE_INITIALIZATION_FAILED:
135 break;
136
137 default:
138 KLDRDYLDMOD_ASSERT(!"invalid state");
139 break;
140 }
141
142 return cToUnload;
143}
144
145
146/**
147 * This is the heart of the unload code.
148 *
149 * It will recursivly (using the load list) initiate module unloading
150 * of all affected modules.
151 *
152 * This function will cause a state transition ot PENDING_DESTROY, PENDING_GC
153 * or PENDING_TERMINATION depending on the module state. There is one exception
154 * to this, and that's INITIALIZATION_FAILED, where the state will not be changed.
155 *
156 * @param pMod The module which prerequisites should be unloaded.
157 */
[2837]158void kldrDyldModUnloadPrerequisites(PKLDRDYLDMOD pMod)
159{
[2842]160 uint32_t cToUnload;
[2837]161
[2842]162 /* sanity */
163#ifdef KLDRDYLD_STRICT
164 {
165 PKLDRDYLDMOD pMod2;
166 for (pMod2 = kLdrDyldHead; pMod2; pMod2 = pMod2->Load.pNext)
167 KLDRDYLDMOD_ASSERT(pMod2->enmState != KLDRSTATE_GOOD || pMod2->cRefs);
168 }
169#endif
170 KLDRDYLDMOD_ASSERT(pMod->papPrereqs);
171
172 /*
173 * Unload prereqs of the module we're called on first.
174 */
175 cToUnload = kldrDyldModUnloadPrerequisitesOne(pMod);
176
177 /*
178 * Iterate the load list in a cyclic manner until there are no more
179 * modules that can be pushed on into unloading.
180 */
181 while (cToUnload)
182 {
183 cToUnload = 0;
184 for (pMod = kLdrDyldHead; pMod; pMod = pMod->Load.pNext)
185 {
186 if ( pMod->cRefs
187 || pMod->enmState >= KLDRSTATE_PENDING_TERMINATION
188 || pMod->enmState < KLDRSTATE_LOADED_PREREQUISITES)
189 continue;
190 cToUnload += kldrDyldModUnloadPrerequisitesOne(pMod);
191 }
192 }
[2837]193}
194
195
[2842]196
197
198
[2837]199void kldrDyldModDeref(PKLDRDYLDMOD pMod)
200{
201 /* validate input */
202 KLDRDYLDMOD_ASSERT(pMod->enmState > KLDRSTATE_INVALID && pMod->enmState < KLDRSTATE_END);
203 KLDRDYLDMOD_ASSERT(pMod->cRefs > 0);
204 KLDRDYLDMOD_ASSERT(pMod->cRefs >= pMod->cDepRefs + pMod->cDynRefs);
205
206 /* decrement. */
207 if (pMod->cRefs > 0)
208 pMod->cRefs--;
209
210 /*
211 * Drop prerequisites for stats that implies that no recursion can have taken place yet.
212 * This ASSUMES that we're only dereferencing modules when an operation completes.
213 * (This is *required* for the reloaded state.)
214 */
215 switch (pMod->enmState)
216 {
217 case KLDRSTATE_MAPPED:
218 case KLDRSTATE_LOADED_PREREQUISITES:
219 case KLDRSTATE_FIXED_UP:
220 case KLDRSTATE_RELOADED:
221 kldrDyldModUnloadPrerequisites(pMod);
222 pMod->enmState = KLDRSTATE_PENDING_GC;
223 break;
224
225 case KLDRSTATE_PENDING_INITIALIZATION:
226 case KLDRSTATE_INITIALIZING:
227 case KLDRSTATE_GOOD:
228 case KLDRSTATE_PENDING_TERMINATION:
229 case KLDRSTATE_TERMINATING:
230 case KLDRSTATE_PENDING_GC:
231 case KLDRSTATE_GC:
232 case KLDRSTATE_PENDING_DESTROY:
233 break;
234
235 default:
236 KLDRDYLDMOD_ASSERT(!"Invalid deref state");
237 break;
238 }
239
240 /*
241 * Also drop prerequisites if noone is referencing the module.
242 */
243 if (!pMod->cDepRefs && !pMod->cDynRefs)
244 {
245 switch (pMod->enmState)
246 {
247 case KLDRSTATE_MAPPED:
248 case KLDRSTATE_LOADED_PREREQUISITES:
249 case KLDRSTATE_FIXED_UP:
250 case KLDRSTATE_RELOADED:
251 /* already dropped. */
252 break;
253
254 case KLDRSTATE_PENDING_INITIALIZATION:
255 kldrDyldModUnloadPrerequisites(pMod);
256 pMod->enmState = KLDRSTATE_PENDING_GC;
257 break;
258
259 case KLDRSTATE_GOOD:
260 pMod->enmState = KLDRSTATE_PENDING_TERMINATION;
261 case KLDRSTATE_PENDING_TERMINATION:
262 case KLDRSTATE_PENDING_GC:
263 kldrDyldModUnloadPrerequisites(pMod);
264 break;
265
266 case KLDRSTATE_TERMINATING:
267 case KLDRSTATE_GC:
268 case KLDRSTATE_PENDING_DESTROY:
269 break;
270
271 default:
272 KLDRDYLDMOD_ASSERT(!"Invalid deref state (b)");
273 break;
274 }
275 }
276
277 /*
278 * If there are no references whatsoever now and the module
279 * is pending destruction, destroy it.
280 */
281 if ( !pMod->cRefs
282 && ( pMod->enmState == KLDRSTATE_PENDING_DESTROY
283 || pMod->enmState == KLDRSTATE_GC))
284 kldrDyldModDestroy(pMod);
285}
Note: See TracBrowser for help on using the repository browser.