~darkmuggle-deactivatedaccount/ubuntu/quantal/grub2/fix-872244

« back to all changes in this revision

Viewing changes to kern/dl.c

  • Committer: Bazaar Package Importer
  • Author(s): Otavio Salvador
  • Date: 2006-01-05 15:20:40 UTC
  • mto: (17.3.1 squeeze) (1.9.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 4.
  • Revision ID: james.westby@ubuntu.com-20060105152040-b72i5pq1a82z22yi
Tags: upstream-1.92
ImportĀ upstreamĀ versionĀ 1.92

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* dl.c - loadable module support */
 
2
/*
 
3
 *  GRUB  --  GRand Unified Bootloader
 
4
 *  Copyright (C) 2002, 2003, 2004, 2005  Free Software Foundation, Inc.
 
5
 *
 
6
 *  GRUB is free software; you can redistribute it and/or modify
 
7
 *  it under the terms of the GNU General Public License as published by
 
8
 *  the Free Software Foundation; either version 2 of the License, or
 
9
 *  (at your option) any later version.
 
10
 *
 
11
 *  This program is distributed in the hope that it will be useful,
 
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 *  GNU General Public License for more details.
 
15
 *
 
16
 *  You should have received a copy of the GNU General Public License
 
17
 *  along with GRUB; if not, write to the Free Software
 
18
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
19
 */
 
20
 
 
21
#include <config.h>
 
22
#include <grub/elf.h>
 
23
#include <grub/dl.h>
 
24
#include <grub/misc.h>
 
25
#include <grub/mm.h>
 
26
#include <grub/err.h>
 
27
#include <grub/types.h>
 
28
#include <grub/symbol.h>
 
29
#include <grub/file.h>
 
30
#include <grub/env.h>
 
31
#include <grub/cache.h>
 
32
 
 
33
#if GRUB_HOST_SIZEOF_VOID_P == 4
 
34
 
 
35
typedef Elf32_Word Elf_Word;
 
36
typedef Elf32_Addr Elf_Addr;
 
37
typedef Elf32_Ehdr Elf_Ehdr;
 
38
typedef Elf32_Shdr Elf_Shdr;
 
39
typedef Elf32_Sym Elf_Sym;
 
40
 
 
41
# define ELF_ST_BIND(val)       ELF32_ST_BIND (val)
 
42
# define ELF_ST_TYPE(val)       ELF32_ST_TYPE (val)
 
43
 
 
44
#elif GRUB_HOST_SIZEOF_VOID_P == 8
 
45
 
 
46
typedef Elf64_Word Elf_Word;
 
47
typedef Elf64_Addr Elf_Addr;
 
48
typedef Elf64_Ehdr Elf_Ehdr;
 
49
typedef Elf64_Shdr Elf_Shdr;
 
50
typedef Elf64_Sym Elf_Sym;
 
51
 
 
52
# define ELF_ST_BIND(val)       ELF64_ST_BIND (val)
 
53
# define ELF_ST_TYPE(val)       ELF64_ST_TYPE (val)
 
54
 
 
55
#endif
 
56
 
 
57
 
 
58
 
 
59
struct grub_dl_list
 
60
{
 
61
  struct grub_dl_list *next;
 
62
  grub_dl_t mod;
 
63
};
 
64
typedef struct grub_dl_list *grub_dl_list_t;
 
65
 
 
66
static grub_dl_list_t grub_dl_head;
 
67
 
 
68
static grub_err_t
 
69
grub_dl_add (grub_dl_t mod)
 
70
{
 
71
  grub_dl_list_t l;
 
72
 
 
73
  if (grub_dl_get (mod->name))
 
74
    return grub_error (GRUB_ERR_BAD_MODULE,
 
75
                       "`%s' is already loaded", mod->name);
 
76
  
 
77
  l = (grub_dl_list_t) grub_malloc (sizeof (*l));
 
78
  if (! l)
 
79
    return grub_errno;
 
80
 
 
81
  l->mod = mod;
 
82
  l->next = grub_dl_head;
 
83
  grub_dl_head = l;
 
84
 
 
85
  return GRUB_ERR_NONE;
 
86
}
 
87
 
 
88
static void
 
89
grub_dl_remove (grub_dl_t mod)
 
90
{
 
91
  grub_dl_list_t *p, q;
 
92
 
 
93
  for (p = &grub_dl_head, q = *p; q; p = &q->next, q = *p)
 
94
    if (q->mod == mod)
 
95
      {
 
96
        *p = q->next;
 
97
        grub_free (q);
 
98
        return;
 
99
      }
 
100
}
 
101
 
 
102
grub_dl_t
 
103
grub_dl_get (const char *name)
 
104
{
 
105
  grub_dl_list_t l;
 
106
 
 
107
  for (l = grub_dl_head; l; l = l->next)
 
108
    if (grub_strcmp (name, l->mod->name) == 0)
 
109
      return l->mod;
 
110
 
 
111
  return 0;
 
112
}
 
113
 
 
114
void
 
115
grub_dl_iterate (int (*hook) (grub_dl_t mod))
 
116
{
 
117
  grub_dl_list_t l;
 
118
 
 
119
  for (l = grub_dl_head; l; l = l->next)
 
120
    if (hook (l->mod))
 
121
      break;
 
122
}
 
123
 
 
124
 
 
125
 
 
126
struct grub_symbol
 
127
{
 
128
  struct grub_symbol *next;
 
129
  const char *name;
 
130
  void *addr;
 
131
  grub_dl_t mod;        /* The module to which this symbol belongs.  */
 
132
};
 
133
typedef struct grub_symbol *grub_symbol_t;
 
134
 
 
135
/* The size of the symbol table.  */
 
136
#define GRUB_SYMTAB_SIZE        509
 
137
 
 
138
/* The symbol table (using an open-hash).  */
 
139
static struct grub_symbol *grub_symtab[GRUB_SYMTAB_SIZE];
 
140
 
 
141
/* Simple hash function.  */
 
142
static unsigned
 
143
grub_symbol_hash (const char *s)
 
144
{
 
145
  unsigned key = 0;
 
146
 
 
147
  while (*s)
 
148
    key = key * 65599 + *s++;
 
149
 
 
150
  return (key + (key >> 5)) % GRUB_SYMTAB_SIZE;
 
151
}
 
152
 
 
153
/* Resolve the symbol name NAME and return the address.
 
154
   Return NULL, if not found.  */
 
155
void *
 
156
grub_dl_resolve_symbol (const char *name)
 
157
{
 
158
  grub_symbol_t sym;
 
159
 
 
160
  for (sym = grub_symtab[grub_symbol_hash (name)]; sym; sym = sym->next)
 
161
    if (grub_strcmp (sym->name, name) == 0)
 
162
      return sym->addr;
 
163
 
 
164
  return 0;
 
165
}
 
166
 
 
167
/* Register a symbol with the name NAME and the address ADDR.  */
 
168
grub_err_t
 
169
grub_dl_register_symbol (const char *name, void *addr, grub_dl_t mod)
 
170
{
 
171
  grub_symbol_t sym;
 
172
  unsigned k;
 
173
  
 
174
  sym = (grub_symbol_t) grub_malloc (sizeof (*sym));
 
175
  if (! sym)
 
176
    return grub_errno;
 
177
 
 
178
  if (mod)
 
179
    {
 
180
      sym->name = grub_strdup (name);
 
181
      if (! sym->name)
 
182
        {
 
183
          grub_free (sym);
 
184
          return grub_errno;
 
185
        }
 
186
    }
 
187
  else
 
188
    sym->name = name;
 
189
  
 
190
  sym->addr = addr;
 
191
  sym->mod = mod;
 
192
  
 
193
  k = grub_symbol_hash (name);
 
194
  sym->next = grub_symtab[k];
 
195
  grub_symtab[k] = sym;
 
196
 
 
197
  return GRUB_ERR_NONE;
 
198
}
 
199
 
 
200
/* Unregister all the symbols defined in the module MOD.  */
 
201
static void
 
202
grub_dl_unregister_symbols (grub_dl_t mod)
 
203
{
 
204
  unsigned i;
 
205
 
 
206
  if (! mod)
 
207
    grub_fatal ("core symbols cannot be unregistered");
 
208
  
 
209
  for (i = 0; i < GRUB_SYMTAB_SIZE; i++)
 
210
    {
 
211
      grub_symbol_t sym, *p, q;
 
212
 
 
213
      for (p = &grub_symtab[i], sym = *p; sym; sym = q)
 
214
        {
 
215
          q = sym->next;
 
216
          if (sym->mod == mod)
 
217
            {
 
218
              *p = q;
 
219
              grub_free ((void *) sym->name);
 
220
              grub_free (sym);
 
221
            }
 
222
          else
 
223
            p = &sym->next;
 
224
        }
 
225
    }
 
226
}
 
227
 
 
228
/* Return the address of a section whose index is N.  */
 
229
static void *
 
230
grub_dl_get_section_addr (grub_dl_t mod, unsigned n)
 
231
{
 
232
  grub_dl_segment_t seg;
 
233
 
 
234
  for (seg = mod->segment; seg; seg = seg->next)
 
235
    if (seg->section == n)
 
236
      return seg->addr;
 
237
 
 
238
  return 0;
 
239
}
 
240
 
 
241
/* Check if EHDR is a valid ELF header.  */
 
242
grub_err_t
 
243
grub_dl_check_header (void *ehdr, grub_size_t size)
 
244
{
 
245
  Elf_Ehdr *e = ehdr;
 
246
 
 
247
  /* Check the header size.  */
 
248
  if (size < sizeof (Elf_Ehdr))
 
249
    return grub_error (GRUB_ERR_BAD_OS, "ELF header smaller than expected");
 
250
 
 
251
  /* Check the magic numbers.  */
 
252
  if (grub_arch_dl_check_header (ehdr)
 
253
      || e->e_ident[EI_MAG0] != ELFMAG0
 
254
      || e->e_ident[EI_MAG1] != ELFMAG1
 
255
      || e->e_ident[EI_MAG2] != ELFMAG2
 
256
      || e->e_ident[EI_MAG3] != ELFMAG3
 
257
      || e->e_ident[EI_VERSION] != EV_CURRENT
 
258
      || e->e_version != EV_CURRENT)
 
259
    return grub_error (GRUB_ERR_BAD_OS, "invalid arch independent ELF magic");
 
260
 
 
261
  return GRUB_ERR_NONE;
 
262
}
 
263
 
 
264
/* Load all segments from memory specified by E.  */
 
265
static grub_err_t
 
266
grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
 
267
{
 
268
  unsigned i;
 
269
  Elf_Shdr *s;
 
270
  
 
271
  for (i = 0, s = (Elf_Shdr *)((char *) e + e->e_shoff);
 
272
       i < e->e_shnum;
 
273
       i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize))
 
274
    {
 
275
      if (s->sh_flags & SHF_ALLOC)
 
276
        {
 
277
          grub_dl_segment_t seg;
 
278
 
 
279
          seg = (grub_dl_segment_t) grub_malloc (sizeof (*seg));
 
280
          if (! seg)
 
281
            return grub_errno;
 
282
          
 
283
          if (s->sh_size)
 
284
            {
 
285
              void *addr;
 
286
 
 
287
              addr = grub_memalign (s->sh_addralign, s->sh_size);
 
288
              if (! addr)
 
289
                {
 
290
                  grub_free (seg);
 
291
                  return grub_errno;
 
292
                }
 
293
 
 
294
              switch (s->sh_type)
 
295
                {
 
296
                case SHT_PROGBITS:
 
297
                  grub_memcpy (addr, (char *) e + s->sh_offset, s->sh_size);
 
298
                  break;
 
299
                case SHT_NOBITS:
 
300
                  grub_memset (addr, 0, s->sh_size);
 
301
                  break;
 
302
                }
 
303
 
 
304
              seg->addr = addr;
 
305
            }
 
306
          else
 
307
            seg->addr = 0;
 
308
 
 
309
          seg->size = s->sh_size;
 
310
          seg->section = i;
 
311
          seg->next = mod->segment;
 
312
          mod->segment = seg;
 
313
        }
 
314
    }
 
315
 
 
316
  return GRUB_ERR_NONE;
 
317
}
 
318
 
 
319
static grub_err_t
 
320
grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e)
 
321
{
 
322
  unsigned i;
 
323
  Elf_Shdr *s;
 
324
  Elf_Sym *sym;
 
325
  const char *str;
 
326
  Elf_Word size, entsize;
 
327
  
 
328
  for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
 
329
       i < e->e_shnum;
 
330
       i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
 
331
    if (s->sh_type == SHT_SYMTAB)
 
332
      break;
 
333
 
 
334
  if (i == e->e_shnum)
 
335
    return grub_error (GRUB_ERR_BAD_MODULE, "no symbol table");
 
336
 
 
337
  sym = (Elf_Sym *) ((char *) e + s->sh_offset);
 
338
  size = s->sh_size;
 
339
  entsize = s->sh_entsize;
 
340
  
 
341
  s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shentsize * s->sh_link);
 
342
  str = (char *) e + s->sh_offset;
 
343
 
 
344
  for (i = 0;
 
345
       i < size / entsize;
 
346
       i++, sym = (Elf_Sym *) ((char *) sym + entsize))
 
347
    {
 
348
      unsigned char type = ELF_ST_TYPE (sym->st_info);
 
349
      unsigned char bind = ELF_ST_BIND (sym->st_info);
 
350
      const char *name = str + sym->st_name;
 
351
      
 
352
      switch (type)
 
353
        {
 
354
        case STT_NOTYPE:
 
355
          /* Resolve a global symbol.  */
 
356
          if (sym->st_name != 0 && sym->st_shndx == 0)
 
357
            {
 
358
              sym->st_value = (Elf_Addr) grub_dl_resolve_symbol (name);
 
359
              if (! sym->st_value)
 
360
                return grub_error (GRUB_ERR_BAD_MODULE,
 
361
                                   "the symbol `%s' not found", name);
 
362
            }
 
363
          else
 
364
            sym->st_value = 0;
 
365
          break;
 
366
 
 
367
        case STT_OBJECT:
 
368
          sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod,
 
369
                                                                sym->st_shndx);
 
370
          if (bind != STB_LOCAL)
 
371
            if (grub_dl_register_symbol (name, (void *) sym->st_value, mod))
 
372
              return grub_errno;
 
373
          break;
 
374
 
 
375
        case STT_FUNC:
 
376
          sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod,
 
377
                                                                sym->st_shndx);
 
378
          if (bind != STB_LOCAL)
 
379
            if (grub_dl_register_symbol (name, (void *) sym->st_value, mod))
 
380
              return grub_errno;
 
381
          
 
382
          if (grub_strcmp (name, "grub_mod_init") == 0)
 
383
            mod->init = (void (*) (grub_dl_t)) sym->st_value;
 
384
          else if (grub_strcmp (name, "grub_mod_fini") == 0)
 
385
            mod->fini = (void (*) (void)) sym->st_value;
 
386
          break;
 
387
 
 
388
        case STT_SECTION:
 
389
          sym->st_value = (Elf_Addr) grub_dl_get_section_addr (mod,
 
390
                                                               sym->st_shndx);
 
391
          break;
 
392
 
 
393
        case STT_FILE:
 
394
          sym->st_value = 0;
 
395
          break;
 
396
 
 
397
        default:
 
398
          return grub_error (GRUB_ERR_BAD_MODULE,
 
399
                             "unknown symbol type `%d'", (int) type);
 
400
        }
 
401
    }
 
402
 
 
403
  return GRUB_ERR_NONE;
 
404
}
 
405
 
 
406
static void
 
407
grub_dl_call_init (grub_dl_t mod)
 
408
{
 
409
  if (mod->init)
 
410
    (mod->init) (mod);
 
411
}
 
412
 
 
413
static grub_err_t
 
414
grub_dl_resolve_name (grub_dl_t mod, Elf_Ehdr *e)
 
415
{
 
416
  Elf_Shdr *s;
 
417
  const char *str;
 
418
  unsigned i;
 
419
 
 
420
  s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
 
421
  str = (char *) e + s->sh_offset;
 
422
 
 
423
  for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
 
424
       i < e->e_shnum;
 
425
       i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
 
426
    if (grub_strcmp (str + s->sh_name, ".modname") == 0)
 
427
      {
 
428
        mod->name = grub_strdup ((char *) e + s->sh_offset);
 
429
        if (! mod->name)
 
430
          return grub_errno;
 
431
        break;
 
432
      }
 
433
 
 
434
  if (i == e->e_shnum)
 
435
    return grub_error (GRUB_ERR_BAD_MODULE, "no module name found");
 
436
 
 
437
  return GRUB_ERR_NONE;
 
438
}
 
439
 
 
440
static grub_err_t
 
441
grub_dl_resolve_dependencies (grub_dl_t mod, Elf_Ehdr *e)
 
442
{
 
443
  Elf_Shdr *s;
 
444
  const char *str;
 
445
  unsigned i;
 
446
 
 
447
  s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
 
448
  str = (char *) e + s->sh_offset;
 
449
  
 
450
  for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
 
451
       i < e->e_shnum;
 
452
       i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
 
453
    if (grub_strcmp (str + s->sh_name, ".moddeps") == 0)
 
454
      {
 
455
        const char *name = (char *) e + s->sh_offset;
 
456
        const char *max = name + s->sh_size;
 
457
 
 
458
        while (name < max)
 
459
          {
 
460
            grub_dl_t m;
 
461
            grub_dl_dep_t dep;
 
462
            
 
463
            m = grub_dl_load (name);
 
464
            if (! m)
 
465
              return grub_errno;
 
466
 
 
467
            grub_dl_ref (m);
 
468
            
 
469
            dep = (grub_dl_dep_t) grub_malloc (sizeof (*dep));
 
470
            if (! dep)
 
471
              return grub_errno;
 
472
            
 
473
            dep->mod = m;
 
474
            dep->next = mod->dep;
 
475
            mod->dep = dep;
 
476
            
 
477
            name += grub_strlen (name) + 1;
 
478
          }
 
479
      }
 
480
 
 
481
  return GRUB_ERR_NONE;
 
482
}
 
483
 
 
484
int
 
485
grub_dl_ref (grub_dl_t mod)
 
486
{
 
487
  grub_dl_dep_t dep;
 
488
 
 
489
  for (dep = mod->dep; dep; dep = dep->next)
 
490
    grub_dl_ref (dep->mod);
 
491
  
 
492
  return ++mod->ref_count;
 
493
}
 
494
 
 
495
int
 
496
grub_dl_unref (grub_dl_t mod)
 
497
{
 
498
  grub_dl_dep_t dep;
 
499
 
 
500
  for (dep = mod->dep; dep; dep = dep->next)
 
501
    grub_dl_unref (dep->mod);
 
502
  
 
503
  return --mod->ref_count;
 
504
}
 
505
 
 
506
static void
 
507
grub_dl_flush_cache (grub_dl_t mod)
 
508
{
 
509
  grub_dl_segment_t seg;
 
510
 
 
511
  for (seg = mod->segment; seg; seg = seg->next) {
 
512
    if (seg->size) {
 
513
      grub_dprintf ("modules", "flushing 0x%x bytes at %p\n", seg->size,
 
514
                    seg->addr);
 
515
      grub_arch_sync_caches (seg->addr, seg->size);
 
516
    }
 
517
  }
 
518
}
 
519
 
 
520
/* Load a module from core memory.  */
 
521
grub_dl_t
 
522
grub_dl_load_core (void *addr, grub_size_t size)
 
523
{
 
524
  Elf_Ehdr *e;
 
525
  grub_dl_t mod;
 
526
 
 
527
  grub_dprintf ("modules", "module at %p, size 0x%x\n", addr, size);
 
528
  e = addr;
 
529
  if (grub_dl_check_header (e, size))
 
530
    return 0;
 
531
 
 
532
  if (e->e_type != ET_REL)
 
533
    {
 
534
      grub_error (GRUB_ERR_BAD_MODULE, "invalid ELF file type");
 
535
      return 0;
 
536
    }
 
537
 
 
538
  /* Make sure that every section is within the core.  */
 
539
  if (size < e->e_shoff + e->e_shentsize * e->e_shnum)
 
540
    {
 
541
      grub_error (GRUB_ERR_BAD_OS, "ELF sections outside core");
 
542
      return 0;
 
543
    }
 
544
 
 
545
  mod = (grub_dl_t) grub_malloc (sizeof (*mod));
 
546
  if (! mod)
 
547
    return 0;
 
548
 
 
549
  mod->name = 0;
 
550
  mod->ref_count = 1;
 
551
  mod->dep = 0;
 
552
  mod->segment = 0;
 
553
  mod->init = 0;
 
554
  mod->fini = 0;
 
555
 
 
556
  grub_dprintf ("modules", "relocating to %p\n", mod);
 
557
  if (grub_dl_resolve_name (mod, e)
 
558
      || grub_dl_resolve_dependencies (mod, e)
 
559
      || grub_dl_load_segments (mod, e)
 
560
      || grub_dl_resolve_symbols (mod, e)
 
561
      || grub_arch_dl_relocate_symbols (mod, e))
 
562
    {
 
563
      mod->fini = 0;
 
564
      grub_dl_unload (mod);
 
565
      return 0;
 
566
    }
 
567
 
 
568
  grub_dl_flush_cache (mod);
 
569
 
 
570
  grub_dprintf ("modules", "module name: %s\n", mod->name);
 
571
  grub_dprintf ("modules", "init function: %p\n", mod->init);
 
572
  grub_dl_call_init (mod);
 
573
 
 
574
  if (grub_dl_add (mod))
 
575
    {
 
576
      grub_dl_unload (mod);
 
577
      return 0;
 
578
    }
 
579
 
 
580
  return mod;
 
581
}
 
582
 
 
583
/* Load a module from the file FILENAME.  */
 
584
grub_dl_t
 
585
grub_dl_load_file (const char *filename)
 
586
{
 
587
  grub_file_t file;
 
588
  grub_ssize_t size;
 
589
  void *core = 0;
 
590
  grub_dl_t mod = 0;
 
591
  
 
592
  file = grub_file_open (filename);
 
593
  if (! file)
 
594
    return 0;
 
595
 
 
596
  size = grub_file_size (file);
 
597
  core = grub_malloc (size);
 
598
  if (! core)
 
599
    goto failed;
 
600
 
 
601
  if (grub_file_read (file, core, size) != (int) size)
 
602
    goto failed;
 
603
 
 
604
  mod = grub_dl_load_core (core, size);
 
605
  if (! mod)
 
606
    goto failed;
 
607
  
 
608
  mod->ref_count = 0;
 
609
 
 
610
 failed:
 
611
  grub_file_close (file);
 
612
  grub_free (core);
 
613
 
 
614
  return mod;
 
615
}
 
616
 
 
617
/* Load a module using a symbolic name.  */
 
618
grub_dl_t
 
619
grub_dl_load (const char *name)
 
620
{
 
621
  char *filename;
 
622
  grub_dl_t mod;
 
623
  char *grub_dl_dir = grub_env_get ("prefix");
 
624
 
 
625
  mod = grub_dl_get (name);
 
626
  if (mod)
 
627
    return mod;
 
628
  
 
629
  if (! grub_dl_dir)
 
630
    grub_fatal ("module dir is not initialized yet");
 
631
 
 
632
  filename = (char *) grub_malloc (grub_strlen (grub_dl_dir) + 1
 
633
                                   + grub_strlen (name) + 4 + 1);
 
634
  if (! filename)
 
635
    return 0;
 
636
  
 
637
  grub_sprintf (filename, "%s/%s.mod", grub_dl_dir, name);
 
638
  mod = grub_dl_load_file (filename);
 
639
  grub_free (filename);
 
640
 
 
641
  if (! mod)
 
642
    return 0;
 
643
  
 
644
  if (grub_strcmp (mod->name, name) != 0)
 
645
    grub_error (GRUB_ERR_BAD_MODULE, "mismatched names");
 
646
  
 
647
  return mod;
 
648
}
 
649
 
 
650
/* Unload the module MOD.  */
 
651
int
 
652
grub_dl_unload (grub_dl_t mod)
 
653
{
 
654
  grub_dl_dep_t dep, depn;
 
655
  grub_dl_segment_t seg, segn;
 
656
 
 
657
  if (mod->ref_count > 0)
 
658
    return 0;
 
659
 
 
660
  if (mod->fini)
 
661
    (mod->fini) ();
 
662
  
 
663
  grub_dl_remove (mod);
 
664
  grub_dl_unregister_symbols (mod);
 
665
  
 
666
  for (dep = mod->dep; dep; dep = depn)
 
667
    {
 
668
      depn = dep->next;
 
669
      
 
670
      if (! grub_dl_unref (dep->mod))
 
671
        grub_dl_unload (dep->mod);
 
672
      
 
673
      grub_free (dep);
 
674
    }
 
675
 
 
676
  for (seg = mod->segment; seg; seg = segn)
 
677
    {
 
678
      segn = seg->next;
 
679
      grub_free (seg->addr);
 
680
      grub_free (seg);
 
681
    }
 
682
  
 
683
  grub_free (mod->name);
 
684
  grub_free (mod);
 
685
  return 1;
 
686
}
 
687
 
 
688
/* Unload unneeded modules.  */
 
689
void
 
690
grub_dl_unload_unneeded (void)
 
691
{
 
692
  /* Because grub_dl_remove modifies the list of modules, this
 
693
     implementation is tricky.  */
 
694
  grub_dl_list_t p = grub_dl_head;
 
695
  
 
696
  while (p)
 
697
    {
 
698
      if (grub_dl_unload (p->mod))
 
699
        {
 
700
          p = grub_dl_head;
 
701
          continue;
 
702
        }
 
703
 
 
704
      p = p->next;
 
705
    }
 
706
}
 
707
 
 
708
/* Unload all modules.  */
 
709
void
 
710
grub_dl_unload_all (void)
 
711
{
 
712
  while (grub_dl_head)
 
713
    {
 
714
      grub_dl_list_t p;
 
715
      
 
716
      grub_dl_unload_unneeded ();
 
717
 
 
718
      /* Force to decrement the ref count. This will purge pre-loaded
 
719
         modules and manually inserted modules.  */
 
720
      for (p = grub_dl_head; p; p = p->next)
 
721
        p->mod->ref_count--;
 
722
    }
 
723
}