/* * depmod * * Create dependency file for modprobe, the kernel loadable modules loader. * * Copyright 1994, 1995, 1996, 1997 Jacques Gelinas * Additional modifications: Björn Ekwall February, March 1999 * * This file is part of the Linux modutils. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Fixes: Add -r flag: Keith Owens October 1999. Rationalize common code for 32/64 bit architectures. Fix error message for modules.dep. Keith Owens December 1999 Add arch64(). Keith Owens December 1999. Ignore any genksyms suffix when using -F. Keith Owens April 2000. */ #ident "$Id: depmod.c,v 1.1.1.5 2000/12/04 19:27:25 msw Exp $" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ELF_MACHINE_H #include "module.h" #include "version.h" #include "util.h" #include "obj.h" #include "config.h" #include "modstat.h" #define ALLOC_MODULE 5000 /* Number of modules we can handle... */ #define ALLOC_SYM 10000 /* Number of symbols allocated per chunk */ #define MAX_MAP_SYM 100000 /* Maximum number of symbols in map */ typedef struct SYMBOL{ struct SYMBOL *next; /* List connected via hashing */ struct MODULE *module; /* Module declaring this symbol */ const char *name; unsigned short hashval; unsigned short hashval_nosuffix; unsigned short length_nosuffix; char status; } SYMBOL; /* Extracted from 2.4.0-test6-pre6/include/linux/pci.h */ struct pci_device_id { unsigned int vendor, device; /* Vendor and device ID or PCI_ANY_ID */ unsigned int subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */ unsigned int class, class_mask; /* (class,subclass,prog-if) triplet */ unsigned tgt_long driver_data; /* Data private to the driver */ }; /* Extracted from 2.4.0-test10-pre1 /include/linux/isapnp.h */ struct isapnp_device_id { unsigned short card_vendor, card_device; unsigned short vendor, function; unsigned tgt_long driver_data; /* data private to the driver */ }; /* Extracted from 2.4.0-test10-pre1 /include/linux/isapnp.h */ #define ISAPNP_CARD_DEVS 8 struct isapnp_card_id { unsigned tgt_long driver_data; /* data private to the driver */ unsigned short card_vendor, card_device; struct { unsigned short vendor, function; } devs[ISAPNP_CARD_DEVS]; /* logical devices */ }; /* Extracted from 2.4.0-test10-pre3 + David Brownell usb-device_id patch 2 */ #ifndef __u16 #define __u16 u_int16_t #endif #ifndef __u8 #define __u8 u_int8_t #endif /* * Device table entry for "new style" table-driven USB drivers. * User mode code can read these tables to choose which modules to load. * Declare the table as __devinitdata, and as a MODULE_DEVICE_TABLE. * * With a device table provide bind() instead of probe(). Then the * third bind() parameter will point to a matching entry from this * table. (Null value reserved.) * * Terminate the driver's table with an all-zeroes entry. * Init the fields you care about; zeroes are not used in comparisons. */ struct usb_device_id { /* * vendor/product codes are checked, if vendor is nonzero * Range is for device revision (bcdDevice), inclusive; * zero values here mean range isn't considered */ __u16 idVendor; __u16 idProduct; __u16 bcdDevice_lo, bcdDevice_hi; /* * if device class != 0, these can be match criteria; * but only if this bDeviceClass value is nonzero */ __u8 bDeviceClass; __u8 bDeviceSubClass; __u8 bDeviceProtocol; /* * if interface class != 0, these can be match criteria; * but only if this bInterfaceClass value is nonzero */ __u8 bInterfaceClass; __u8 bInterfaceSubClass; __u8 bInterfaceProtocol; /* * for driver's use; not involved in driver matching. */ unsigned tgt_long driver_info; }; typedef struct MODULE { char *name; int resolved; struct { /* Defined + required symbols for this module */ SYMBOL **symtab; int n_syms; } defsym, undefs; struct pci_device_id *pci_device; int n_pci_device; struct isapnp_device_id *isapnp_device; int n_isapnp_device; struct isapnp_card_id *isapnp_card; int n_isapnp_card; struct usb_device_id *usb_device; int n_usb_device; } MODULE; typedef enum SYM_STATUS { SYM_UNDEF, /* Required by another module */ SYM_DEFINED, /* We have found a module that knows about it */ SYM_RESOLVED } SYM_STATUS; typedef struct LIST_SYMBOL { struct LIST_SYMBOL *next; SYMBOL alloc[ALLOC_SYM]; } LIST_SYMBOL; /*==================================================*/ static MODULE modules[ALLOC_MODULE]; static int n_modules; static SYMBOL *symhash[2048]; static SYMBOL *symavail; static SYMBOL *maxsyms; static LIST_SYMBOL *chunk; static int ignore_symbol_versions; static int quiet; /* Don't print errors */ static int showerror; /* Shows undefined symbols */ #if !defined(COMMON_3264) || defined(ONLY_64) int flag_verbose = 0; #endif extern int depmod_main(int argc, char **argv); extern int depmod_main_32(int argc, char **argv); extern int depmod_main_64(int argc, char **argv); extern int quick; /* Option -A */ /* * Create a symbol definition. * Add defined symbol to the head of the list of defined symbols. * Return the new symbol. */ static SYMBOL *addsym(const char *name, MODULE *module, SYM_STATUS status, int ignore_suffix) { SYMBOL added; SYMBOL *me; unsigned short hashval = 0; const char *pt = name, *pt_R; char *p; int l; for (pt = name; *pt; ++pt) hashval = (hashval << 1) ^ *pt; hashval %= 2048; memset(&added, 0, sizeof(SYMBOL)); added.module = module; added.name = xstrdup(name); added.hashval = hashval; added.status = status; /* Compute the hash without the trailing genksym suffix if required */ if (ignore_suffix && (pt_R = strstr(name, "_R"))) { l = strlen(pt_R); if (l >= 10) { /* May be _R.*xxxxxxxx */ (void)strtoul(pt_R+l-8, &p, 16); if (!*p) { /* It is _R.*xxxxxxxx */ added.length_nosuffix = pt_R - name; hashval = 0; for (pt = name; pt < pt_R; ++pt) hashval = (hashval << 1) ^ *pt; hashval %= 2048; added.hashval_nosuffix = hashval; } } } else { added.hashval_nosuffix = 0; added.length_nosuffix = 0; } if (status == SYM_DEFINED) { if (symavail == NULL || symavail == maxsyms) { /* * Allocate a new buffer for the collection of symbols */ LIST_SYMBOL *list = (LIST_SYMBOL *)xmalloc(sizeof(LIST_SYMBOL)); list->next = chunk; chunk = list; symavail = list->alloc; maxsyms = list->alloc + ALLOC_SYM; } me = symavail++; *me = added; me->next = symhash[added.hashval]; symhash[added.hashval] = me; } else { me = (SYMBOL *)xmalloc(sizeof(SYMBOL)); *me = added; } return me; } /************************************************************ * Module.c */ static void resolve(void) { MODULE *mod; SYMBOL **sym; int i; int n; int resolved; /* * Resolve, starting with the most recently added * module symbol definitions. */ if (n_modules <= 0) return; /* else */ for (i = n_modules - 1, mod = modules + i; i >= 0; --i, --mod) { if (mod->resolved) continue; for (resolved = 1, n = 0, sym = mod->undefs.symtab; n < mod->undefs.n_syms; ++sym, ++n) { SYMBOL *psym = *sym, *find; if (psym->status != SYM_UNDEF) continue; /* This is an undefined symbol. Find first definition. Symbol hash linked list has newest first, so we want the last match in the chain. In particular, this prefers kernel symbols to that of any module. If we are ignoring the genksyms suffix in this comparision then there are two hashes to try. */ find = symhash[psym->hashval]; while (find) { if (strcmp(find->name, psym->name) == 0) { psym->status = SYM_RESOLVED; psym->module = find->module; } find = find->next; } if (psym->status == SYM_UNDEF && psym->length_nosuffix) { /* Try again ignoring the suffix */ find = symhash[psym->hashval_nosuffix]; while (find) { if (strncmp(find->name, psym->name, psym->length_nosuffix) == 0 && !find->name[psym->length_nosuffix]) { psym->status = SYM_RESOLVED; psym->module = find->module; } find = find->next; } } if (psym->status == SYM_UNDEF) resolved = 0; } mod->resolved = resolved; } } /* * Verify that an "address" falls within the module image. * Note: assumes same machine and arch for depmod and module. */ static int in_range(const struct obj_file *f, unsigned long m_size, ElfW(Addr) start, unsigned int length) { return(start >= f->baseaddr && (start + length - 1 - f->baseaddr) <= m_size); } static const char has_kernel_changed[] = "\n\tIt is likely that the kernel structure has changed, if so then" "\n\tyou probably need a new version of modutils to handle this kernel." "\n\tCheck linux/Documentation/Changes."; /* * Extract any pci_device_table from the module. * Note: assumes same machine and arch for depmod and module. */ static void extract_pci_device_id(struct obj_file *f, void *image, unsigned long m_size, MODULE *mod) { struct pci_device_id pci_device; ElfW(Addr) ref_pci; ElfW(Addr) ref_ref_pci; unsigned tgt_long pci_device_size; ref_pci = obj_symbol_final_value(f, obj_find_symbol(f, "__module_pci_device_size")); /* Kernels before 2.4.0-test10-pre1 do not have __module_pci_device_size */ if (ref_pci) { if (!in_range(f, m_size, ref_pci, sizeof(pci_device_size))) return; memcpy(&pci_device_size, (char *)image + ref_pci - f->baseaddr, sizeof(pci_device_size)); } else pci_device_size = sizeof(pci_device); if (pci_device_size != sizeof(pci_device)) { error("Unexpected value (%" tgt_long_fmt "d) in '%s' for pci_device_size%s", pci_device_size, f->filename, has_kernel_changed); exit(-1); } ref_ref_pci = obj_symbol_final_value(f, obj_find_symbol(f, "__module_pci_device_table")); if (!in_range(f, m_size, ref_ref_pci, sizeof(ref_pci))) return; memcpy(&ref_pci, (char *)image + ref_ref_pci - f->baseaddr, sizeof(ref_pci)); while (in_range(f, m_size, ref_pci, sizeof(pci_device))) { memcpy(&pci_device, (char *)image + ref_pci - f->baseaddr, sizeof(pci_device)); ref_pci += sizeof(pci_device); if (!pci_device.vendor) break; mod->pci_device = xrealloc(mod->pci_device, ++(mod->n_pci_device)*sizeof(*(mod->pci_device))); mod->pci_device[mod->n_pci_device-1] = pci_device; } } /* * Extract any isapnp_device_table from the module. * Note: assumes same machine and arch for depmod and module. */ static void extract_isapnp_device_id(struct obj_file *f, void *image, unsigned long m_size, MODULE *mod) { struct isapnp_device_id isapnp_device; ElfW(Addr) ref_isapnp, ref_ref_isapnp; unsigned tgt_long isapnp_device_size; ref_isapnp = obj_symbol_final_value(f, obj_find_symbol(f, "__module_isapnp_device_size")); if (!in_range(f, m_size, ref_isapnp, sizeof(isapnp_device_size))) return; memcpy(&isapnp_device_size, (char *)image + ref_isapnp - f->baseaddr, sizeof(isapnp_device_size)); if (isapnp_device_size != sizeof(isapnp_device)) { error("Unexpected value (%" tgt_long_fmt "d) in '%s' for isapnp_device_size%s", isapnp_device_size, f->filename, has_kernel_changed); exit(-1); } ref_ref_isapnp = obj_symbol_final_value(f, obj_find_symbol(f, "__module_isapnp_device_table")); if (!in_range(f, m_size, ref_ref_isapnp, sizeof(ref_isapnp))) return; memcpy(&ref_isapnp, (char *)image + ref_ref_isapnp - f->baseaddr, sizeof(ref_isapnp)); while (in_range(f, m_size, ref_isapnp, sizeof(isapnp_device))) { memcpy(&isapnp_device, (char *)image + ref_isapnp - f->baseaddr, sizeof(isapnp_device)); ref_isapnp += sizeof(isapnp_device); if (!isapnp_device.card_vendor) break; mod->isapnp_device = xrealloc(mod->isapnp_device, ++(mod->n_isapnp_device)*sizeof(*(mod->isapnp_device))); mod->isapnp_device[mod->n_isapnp_device-1] = isapnp_device; } } /* * Extract any isapnp_card_table from the module. * Note: assumes same machine and arch for depmod and module. */ static void extract_isapnp_card_id(struct obj_file *f, void *image, unsigned long m_size, MODULE *mod) { struct isapnp_card_id isapnp_card; ElfW(Addr) ref_isapnp, ref_ref_isapnp; unsigned tgt_long isapnp_card_size; ref_isapnp = obj_symbol_final_value(f, obj_find_symbol(f, "__module_isapnp_card_size")); if (!in_range(f, m_size, ref_isapnp, sizeof(isapnp_card_size))) return; memcpy(&isapnp_card_size, (char *)image + ref_isapnp - f->baseaddr, sizeof(isapnp_card_size)); if (isapnp_card_size != sizeof(isapnp_card)) { error("Unexpected value (%" tgt_long_fmt "d) in '%s' for isapnp_card_size%s", isapnp_card_size, f->filename, has_kernel_changed); exit(-1); } ref_ref_isapnp = obj_symbol_final_value(f, obj_find_symbol(f, "__module_isapnp_card_table")); if (!in_range(f, m_size, ref_ref_isapnp, sizeof(ref_isapnp))) return; memcpy(&ref_isapnp, (char *)image + ref_ref_isapnp - f->baseaddr, sizeof(ref_isapnp)); while (in_range(f, m_size, ref_isapnp, sizeof(isapnp_card))) { memcpy(&isapnp_card, (char *)image + ref_isapnp - f->baseaddr, sizeof(isapnp_card)); ref_isapnp += sizeof(isapnp_card); if (!isapnp_card.card_vendor) break; mod->isapnp_card = xrealloc(mod->isapnp_card, ++(mod->n_isapnp_card)*sizeof(*(mod->isapnp_card))); mod->isapnp_card[mod->n_isapnp_card-1] = isapnp_card; } } /* * Extract any usb_device_table from the module. * Note: assumes same machine and arch for depmod and module. */ static void extract_usb_device_id(struct obj_file *f, void *image, unsigned long m_size, MODULE *mod) { struct usb_device_id usb_device; ElfW(Addr) ref_usb, ref_ref_usb; unsigned tgt_long usb_device_size; ref_usb = obj_symbol_final_value(f, obj_find_symbol(f, "__module_usb_device_size")); if (!in_range(f, m_size, ref_usb, sizeof(usb_device_size))) return; memcpy(&usb_device_size, (char *)image + ref_usb - f->baseaddr, sizeof(usb_device_size)); if (usb_device_size != sizeof(usb_device)) { error("Unexpected value (%" tgt_long_fmt "d) in '%s' for usb_device_size%s", usb_device_size, f->filename, has_kernel_changed); exit(-1); } ref_ref_usb = obj_symbol_final_value(f, obj_find_symbol(f, "__module_usb_device_table")); if (!in_range(f, m_size, ref_ref_usb, sizeof(ref_usb))) return; memcpy(&ref_usb, (char *)image + ref_ref_usb - f->baseaddr, sizeof(ref_usb)); while (in_range(f, m_size, ref_usb, sizeof(usb_device))) { memcpy(&usb_device, (char *)image + ref_usb - f->baseaddr, sizeof(usb_device)); ref_usb += sizeof(usb_device); if (!usb_device.idVendor && !usb_device.bDeviceClass && !usb_device.bInterfaceClass && !usb_device.driver_info) break; mod->usb_device = xrealloc(mod->usb_device, ++(mod->n_usb_device)*sizeof(*(mod->usb_device))); mod->usb_device[mod->n_usb_device-1] = usb_device; } } /* * Read the symbols in an object and register them in the symbol table. * Return -1 if there is an error. */ static int loadobj(const char *objname, int ignore_suffix) { static char *currdir; static int currdir_len; int fp; MODULE *mod; SYMBOL *undefs[5000]; SYMBOL *defsym[5000]; struct obj_file *f; struct obj_section *sect; struct obj_symbol *objsym; int i; int is_2_2; int ksymtab; int len; int n_undefs = 0; int n_defsym = 0; char *p; void *image; unsigned long m_size; p = strrchr(objname, '/'); len = 1 + (int)(p - objname); if ((fp = gzf_open(objname, O_RDONLY)) < 0) return 1; if (!(f = obj_load(fp, ET_REL, objname))) { gzf_close(fp); return -1; } gzf_close(fp); /* * If we have changed base directory * then use the defined symbols from modules * in the _same_ directory to resolve whatever * undefined symbols there are. * * This strategy ensures that we will have * as correct dependencies as possible, * even if the same symbol is defined by * other modules in other directories. */ if (currdir_len != len || currdir == NULL || strncmp(currdir, objname, len) != 0) { if (currdir) resolve(); currdir = xstrdup(objname); currdir_len = len; } mod = modules + n_modules++; mod->name = xstrdup(objname); if ((sect = obj_find_section(f, "__ksymtab")) != NULL) ksymtab = sect->idx; /* Only in 2.2 (or at least not 2.0) */ else ksymtab = -1; if (sect || obj_find_section(f, ".modinfo") || obj_find_symbol(f, "__this_module")) is_2_2 = 1; else is_2_2 = 0; for (i = 0; i < HASH_BUCKETS; ++i) { for (objsym = f->symtab[i]; objsym; objsym = objsym->next) { if (objsym->secidx == SHN_UNDEF) { if (ELFW(ST_BIND)(objsym->info) != STB_WEAK && objsym->r_type /* assumes that R_arch_NONE is always 0 on all arch */) { undefs[n_undefs++] = addsym(objsym->name, mod, SYM_UNDEF, ignore_suffix); } continue; } /* else */ if (is_2_2 && ksymtab != -1) { /* A 2.2 module using EXPORT_SYMBOL */ if (objsym->secidx == ksymtab && ELFW(ST_BIND)(objsym->info) == STB_GLOBAL) { /* Ignore leading "__ksymtab_" */ defsym[n_defsym++] = addsym(objsym->name + 10, mod, SYM_DEFINED, ignore_suffix); } continue; } /* else */ if (is_2_2) { /* * A 2.2 module _not_ using EXPORT_SYMBOL * It might still want to export symbols, * although strictly speaking it shouldn't... * (Seen in pcmcia) */ if (ELFW(ST_BIND)(objsym->info) == STB_GLOBAL) { defsym[n_defsym++] = addsym(objsym->name, mod, SYM_DEFINED, ignore_suffix); } continue; } /* else */ /* Not undefined or 2.2 ksymtab entry */ if (objsym->secidx < SHN_LORESERVE /* * The test below is removed for 2.0 compatibility * since some 2.0-modules (correctly) hide the * symbols it exports via register_symtab() */ /* && ELFW(ST_BIND)(objsym->info) == STB_GLOBAL */ ) { defsym[n_defsym++] = addsym(objsym->name, mod, SYM_DEFINED, ignore_suffix); } } } /* * Finalize the information about a module. */ mod->defsym.n_syms = n_defsym; if (n_defsym > 0) { int size = n_defsym * sizeof(SYMBOL *); mod->defsym.symtab = (SYMBOL **)xmalloc(size); memcpy(mod->defsym.symtab, defsym, size); } else mod->defsym.symtab = NULL; mod->undefs.n_syms = n_undefs; if (n_undefs > 0) { int size = n_undefs * sizeof(SYMBOL *); mod->undefs.symtab = (SYMBOL **) xmalloc(size); memcpy(mod->undefs.symtab, undefs, size); mod->resolved = 0; } else { mod->undefs.symtab = NULL; mod->resolved = 1; } /* Do a pseudo relocation to base address 0x1000 (arbitrary). * All undefined symbols are treated as absolute 0. This builds * enough of a module to allow extraction of internal data such * as device tables. */ obj_clear_undefineds(f); obj_allocate_commons(f); arch_create_got(f); m_size = obj_load_size(f); if (!obj_relocate(f, 0x1000)) { error("depmod obj_relocate failed\n"); return(-1); } image = xmalloc(m_size); obj_create_image(f, image); extract_pci_device_id(f, image, m_size, mod); extract_isapnp_device_id(f, image, m_size, mod); extract_isapnp_card_id(f, image, m_size, mod); extract_usb_device_id(f, image, m_size, mod); free(image); obj_free(f); return 0; } /* * Add the symbol defined in the kernel, simulating a pseudo module. * Create a dummy module entry in the list of modules. * This module is used to handle the exported kernel symbols. * * Load from the currently running kernel * OR * from a file containing kernel symbols from a different kernel. * The file can be either a "System.map" file or a copy of "/proc/ksyms". * * Return -1 if any error. */ static int addksyms(char *file_syms) { FILE *fp; MODULE *mod = modules + n_modules++; SYMBOL *symtab[MAX_MAP_SYM]; struct module_symbol *ksym; unsigned int so_far = 0; int is_mapfile = 0; int n_syms = 0; int size; char line[PATH_MAX]; char *p; mod->name = "-"; mod->defsym.n_syms = 0; mod->defsym.symtab = NULL; mod->undefs.n_syms = 0; mod->undefs.symtab = NULL; /* Fake the _mod_use_count_ symbol provided by insmod */ /* Dummy */ symtab[n_syms++] = addsym("mod_use_count_", mod, SYM_DEFINED, 0); symtab[n_syms++] = addsym("__this_module", mod, SYM_DEFINED, 0); /* * Specification: depmod / kernel syms only * When initialising its symbol table from the kernel * depmod silently discards all symbol from loaded modules. * * This means that depmod may be used at any time to compute * the dependancy table, even if there are modules already * loaded. * * depmod use the kernel system call to obtain the * symbol table, not /proc/ksyms. */ if (file_syms) { if ((fp = fopen(file_syms, "r")) == NULL) { error("Can't read %s", file_syms); return -1; } if (!fgets(line, sizeof(line), fp)) { fclose(fp); error("premature EOF on %s", file_syms); return -1; } p = strtok(line, " \t\n"); if (!p) { fclose(fp); error("Illegal format of %s", file_syms); return -1; } if (!isspace(*line)) /* Adressless symbol? */ p = strtok(NULL, " \t\n"); /* The second word is either the symbol name or a type */ if (p && strlen(p) == 1) { /* System.map */ is_mapfile = 1; if (*p != '?') p = NULL; else p = strtok(NULL, " \t\n"); } else { /* /proc/ksyms copy */ if (p && strtok(NULL, " \t\n")) p = NULL; } if (p) symtab[n_syms++] = addsym(p, mod, SYM_DEFINED, 0); while (fgets(line, sizeof(line), fp)) { if (!is_mapfile && strchr(line, '[')) continue; p = strtok(line, " \t\n"); /* Skip first word */ if (!isspace(*line)) /* Adressless symbol? */ p = strtok(NULL, " \t\n"); if (is_mapfile) { if (*p != '?') continue; p = strtok(NULL, " \t\n"); if (!strncmp(p, "__kstrtab_", 10)) p += 10; else if (!strncmp(p, "__export_priv_", 14)) { /* Sparc has some weird exported symbols marked * __export_priv_ instead of the normal __kstrtab_. * Replace the 'v' with '_' and point at the start of * the '__' before the name. I see no good reason * to use __export_priv_, but for compatibility with * old sparc kernels, it is supported. Try to remove * __export_priv_ from sparc and remove this code * four releases after the last kernel that uses * __export_priv_. */ p += 12; *p = '_'; } else continue; } assert(n_syms < MAX_MAP_SYM); symtab[n_syms++] = addsym(p, mod, SYM_DEFINED, 0); } fclose(fp); } else { /* Use the exported symbols from currently running kernel */ if (!get_kernel_info(K_SYMBOLS)) return -1; for (ksym = ksyms; so_far < nksyms; ++so_far, ksym++) { assert(n_syms < MAX_MAP_SYM); symtab[n_syms++] = addsym((char *)ksym->name, mod, SYM_DEFINED, 0); } } size = n_syms * sizeof(SYMBOL *); mod->defsym.symtab = (SYMBOL **)xmalloc(size); mod->defsym.n_syms = n_syms; memcpy(mod->defsym.symtab, symtab, size); return 0; } /* Open a generated file for output */ static FILE *gen_file_open(const struct gen_files *gf) { FILE *f = fopen(gf->name, "w"); if (!f) { error("Can't open %s for writing", gf->name); exit(-1); } return(f); } static char *shortname(const char *name) { char *name2 = xstrdup(name); char *s = strrchr(name2, '/'); int l; if (s) { ++s; l = strlen(s); if (l > 3 && strcmp(s+l-3, ".gz") == 0) { *(s+l-3) = '\0'; l -= 3; } if (l > 2 && strcmp(s+l-2, ".o") == 0) { *(s+l-2) = '\0'; l -= 2; } else if (l > 4 && strcmp(s+l-4, ".mod") == 0) { *(s+l-4) = '\0'; l -= 4; } } else s = name2; /* No '/' in name, strange */ return(s); } /* * Format the dependancy list of a module into a simple makefile. * Print the dependancies in the depfile (or stdout if nflag is true). * Print the pcimap in the pcimapfile (or stdout if nflag is true). * Print the isapnpmap in the isapnpmapfile (or stdout if nflag is true). * Print the usbmap in the usbmapfile (or stdout if nflag is true). */ static void prtdepend(char *base_dir, int nflag) { FILE *dep = stdout; FILE *pcimap = stdout; FILE *isapnpmap = stdout; FILE *usbmap = stdout; MODULE **tbdep; MODULE *ptmod; int i; int skipchars; /* For depmod -a in image of a tree */ if (!nflag) { dep = gen_file_open(gen_file+GEN_DEPFILE); pcimap = gen_file_open(gen_file+GEN_PCIMAPFILE); isapnpmap = gen_file_open(gen_file+GEN_ISAPNPMAPFILE); usbmap = gen_file_open(gen_file+GEN_USBMAPFILE); } skipchars = strlen(base_dir); tbdep = (MODULE **)alloca(sizeof(MODULE) * n_modules); ptmod = modules; for (i = 0; i < n_modules; i++, ptmod++) { SYMBOL **undefs = ptmod->undefs.symtab; int n_undefs = ptmod->undefs.n_syms; int nbdepmod = 0; int nberr = 0; int e; int m; /* Don't print a dependency list for the _kernel_ ... */ if (strcmp(ptmod->name, "-") == 0) continue; /* Look for unresolved symbols in this module */ for (e = 0; e < n_undefs; e++, undefs++) { MODULE *mod = (*undefs)->module; if ((*undefs)->status == SYM_RESOLVED) { /* Resolved by self or exported kernel symbol */ if (strcmp(mod->name, ptmod->name) == 0 || strcmp(mod->name, "-") == 0) continue; /* No duplicate dependencies, please */ for (m = 0; m < nbdepmod; m++) { if (tbdep[m] == mod) break; } if (m == nbdepmod) tbdep[nbdepmod++] = mod; } else { if (!quiet && nberr == 0) error("*** Unresolved symbols in %s", ptmod->name); if (!quiet && showerror) error("\t%s", (*undefs)->name); nberr++; } } verbose("%s\n", ptmod->name + skipchars); fprintf(dep, "%s:", ptmod->name + skipchars); for (m = 0; m < nbdepmod; m++) { if (m != 0 /*&& (m & 3) == 0*/) fprintf(dep, " \\\n"); fprintf(dep, "\t%s", tbdep[m]->name + skipchars); } fprintf(dep, "\n\n"); } ptmod = modules; fprintf(pcimap, "# pci module vendor device subvendor subdevice class class_mask driver_data\n"); for (i = 0; i < n_modules; i++, ptmod++) { int j; struct pci_device_id *pci_device = ptmod->pci_device; if (!ptmod->n_pci_device) continue; for (j = 0; j < ptmod->n_pci_device; j++, pci_device++) { fprintf(pcimap, "%-20s 0x%0*x 0x%0*x 0x%0*x 0x%0*x 0x%0*x 0x%0*x 0x%0*" tgt_long_fmt "x\n", shortname(ptmod->name), 2*sizeof(pci_device->vendor), pci_device->vendor, 2*sizeof(pci_device->device), pci_device->device, 2*sizeof(pci_device->subvendor), pci_device->subvendor, 2*sizeof(pci_device->subdevice), pci_device->subdevice, 2*sizeof(pci_device->class), pci_device->class, 2*sizeof(pci_device->class_mask), pci_device->class_mask, 2*sizeof(pci_device->driver_data), pci_device->driver_data); } } ptmod = modules; fprintf(isapnpmap, "# isapnp module cardvendor carddevice driver_data vendor function ...\n"); for (i = 0; i < n_modules; i++, ptmod++) { int j, l; struct isapnp_device_id *isapnp_device = ptmod->isapnp_device; struct isapnp_card_id *isapnp_card = ptmod->isapnp_card; if (!ptmod->n_isapnp_device && !ptmod->n_isapnp_card) continue; for (j = 0; j < ptmod->n_isapnp_device; j++, isapnp_device++) { fprintf(isapnpmap, "%-20s 0x%0*x 0x%0*x 0x%0*" tgt_long_fmt "x 0x%0*x 0x%0*x\n", shortname(ptmod->name), 2*sizeof(isapnp_device->card_vendor), isapnp_device->card_vendor, 2*sizeof(isapnp_device->card_device), isapnp_device->card_device, 2*sizeof(isapnp_device->driver_data), isapnp_device->driver_data, 2*sizeof(isapnp_device->vendor), isapnp_device->vendor, 2*sizeof(isapnp_device->function), isapnp_device->function); } for (j = 0; j < ptmod->n_isapnp_card; j++, isapnp_card++) { fprintf(isapnpmap, "%-20s 0x%0*x 0x%0*x 0x%0*" tgt_long_fmt "x ", shortname(ptmod->name), 2*sizeof(isapnp_card->card_vendor), isapnp_card->card_vendor, 2*sizeof(isapnp_card->card_device), isapnp_card->card_device, 2*sizeof(isapnp_card->driver_data), isapnp_card->driver_data); for (l = 0; l < ISAPNP_CARD_DEVS; ++l) { if (isapnp_card->devs[l].vendor) { fprintf(isapnpmap, " 0x%0*x 0x%0*x ", 2*sizeof(isapnp_card->devs[l].vendor), isapnp_card->devs[l].vendor, 2*sizeof(isapnp_card->devs[l].function), isapnp_card->devs[l].function); } } fprintf(isapnpmap, "\n"); } } ptmod = modules; fprintf(usbmap, "# usb module idVendor idProduct bcdDevice_lo bcdDevice_hi" " bDeviceClass bDeviceSubClass bDeviceProtocol bInterfaceClass bInterfaceSubClass" " bInterfaceProtocol driver_info\n"); for (i = 0; i < n_modules; i++, ptmod++) { int j; struct usb_device_id *usb_device = ptmod->usb_device; if (!ptmod->n_usb_device) continue; for (j = 0; j < ptmod->n_usb_device; j++, usb_device++) { fprintf(usbmap, "%-20s 0x%0*x 0x%0*x 0x%0*x 0x%0*x " "0x%0*x 0x%0*x 0x%0*x 0x%0*x 0x%0*x " "0x%0*x 0x%0*" tgt_long_fmt "x\n", shortname(ptmod->name), 2*sizeof(usb_device->idVendor), usb_device->idVendor, 2*sizeof(usb_device->idProduct), usb_device->idProduct, 2*sizeof(usb_device->bcdDevice_lo), usb_device->bcdDevice_lo, 2*sizeof(usb_device->bcdDevice_hi), usb_device->bcdDevice_hi, 2*sizeof(usb_device->bDeviceClass), usb_device->bDeviceClass, 2*sizeof(usb_device->bDeviceSubClass), usb_device->bDeviceSubClass, 2*sizeof(usb_device->bDeviceProtocol), usb_device->bDeviceProtocol, 2*sizeof(usb_device->bInterfaceClass), usb_device->bInterfaceClass, 2*sizeof(usb_device->bInterfaceSubClass), usb_device->bInterfaceSubClass, 2*sizeof(usb_device->bInterfaceProtocol), usb_device->bInterfaceProtocol, 2*sizeof(usb_device->driver_info), usb_device->driver_info); } } if (pcimap != stdout) fclose(pcimap); if (isapnpmap != stdout) fclose(isapnpmap); if (usbmap != stdout) fclose(usbmap); /* Close depfile last, it's timestamp is critical */ if (dep != stdout) fclose(dep); } /* For common 3264, only compile the usage message once, in the 64 bit version */ #if defined(COMMON_3264) && defined(ONLY_32) extern void depmod_usage(void); /* Use the copy in the 64 bit version */ #else /* Common 64 bit version or any non common code - compile usage routine */ void depmod_usage(void) { fprintf(stderr, "depmod " MODUTILS_VERSION "\n" "depmod -[aA] [-n -e -v -q -V]\n" " [-C configfile] [-F kernelsyms] [-b basedirectory] [forced_version]\n" "depmod [-n -e -v -q] [-f kernelsyms] module1.o module2.o ...\n" "If no arguments (except options) are given, \"depmod -a\" is assumed\n" "\n" "depmod will output a dependancy list suitable for the modprobe utility.\n" "depmod -a will find the list of modules to probe from the file\n" ETC_MODULES_CONF ". It will output the result into the depfile specified\n" "in this configuration file\n" "\n" "depmod -A is the same as depmod -a, but will first compare the timestamps\n" "of the files involved to see if the depfile needs updating.\n" "\n" "Normally depmod operates silently, reporting only the list of modules that\n" "won't load properly (missing symbols).\n" "\n" "Options:\n" "\t-a, --all Probe modules listed in " ETC_MODULES_CONF "\n" "\t-A Like -a, compares timestamps first\n" "\t-q, --quiet Don't report missing symbols\n" "\t-e, --errsyms List unresolved symbols for the given module\n" "\t-s, --syslog Report errors using syslog\n" "\t-v, --verbose Print all visited modules\n" "\t-n, --show Write the dependency file on stdout only\n" "\t-r, --root Allow root to allow modules not owned by root\n" "\t-V, --version Print the release version\n" "\t-h, --help Print this usage message\n" "\n" "The following options are useful for people managing distributions:\n" "\t-b basedirectory\n" "\t --basedir basedirectory Use an image of a module tree.\n" "\t-C configfile\n" "\t --config configfile Use the file instead of\n" "\t " ETC_MODULES_CONF ".\n" "\t-F kernelsyms\n" "\t --filesyms kernelsyms Use the file instead of the\n" "\t current kernel symbols.\n" ); } #endif /* defined(COMMON_3264) && defined(ONLY_32) */ #if defined(COMMON_3264) && defined(ONLY_32) #define DEPMOD_MAIN depmod_main_32 /* 32 bit version */ #elif defined(COMMON_3264) && defined(ONLY_64) #define DEPMOD_MAIN depmod_main_64 /* 64 bit version */ #else #define DEPMOD_MAIN depmod_main /* Not common 3264 code */ #endif /* For common 3264 code, add an overall depmod_main, in the 64 bit version. */ #if defined(COMMON_3264) && defined(ONLY_64) int depmod_main(int argc, char **argv) { if (arch64()) return depmod_main_64(argc, argv); else return depmod_main_32(argc, argv); } #endif /* defined(COMMON_3264) && defined(ONLY_64) */ int DEPMOD_MAIN(int argc, char **argv) { int ret = -1; int stdmode = 0; int err = 0; int nflag = 0; int o; char *conf_file = NULL; char *file_syms = NULL; char *base_dir = ""; int ignore_suffix = 0; /* Ignore genksyms suffix on resolve? */ struct option long_opts[] = { {"all", 0, 0, 'a'}, {"basedir", 1, 0, 'b'}, {"config", 1, 0, 'C'}, {"errsyms", 0, 0, 'e'}, {"filesyms", 1, 0, 'F'}, {"help", 0, 0, 'h'}, {"ignore-versions", 0, 0, 'i'}, {"show", 0, 0, 'n'}, {"system-map", 1, 0, 'm'}, /* backward compatibility, same as 'F' */ {"quiet", 0, 0, 'q'}, {"syslog", 0, 0, 's'}, {"verbose", 0, 0, 'v'}, {"version", 0, 0, 'V'}, {"root", 0, 0, 'r'}, {0, 0, 0, 0 } }; error_file = "depmod"; while ((o = getopt_long(argc, argv, "aAb:C:eF:him:nqsvVr", &long_opts[0], NULL)) != EOF) { switch (o) { case 'A': case 'a': quick = o == 'A'; stdmode = 1; /* Probe standard directory */ break; /* using the config file */ case 'e': showerror = 1; break; case 'b': base_dir = optarg; break; case '?': case 'h': depmod_usage(); return 0; break; case 'i': ignore_symbol_versions = 1; break; case 'C': conf_file = optarg; break; case 'F': case 'm': file_syms = optarg; break; case 'n': nflag = 1; break; case 'q': quiet = 1; break; case 's': setsyslog("depmod"); break; case 'v': flag_verbose = 1; break; case 'V': printf("depmod version %s\n", MODUTILS_VERSION); break; case 'r': root_check_off = 1; break; default: err = 1; break; } } if (err) { error("Aborting"); return -1; } /* else */ argc -= optind; argv += optind; ignore_suffix = file_syms != NULL; if (stdmode || argc == 0) { /* option -a is the default without arguments */ if (argc > 0) { if (config_read(0, *argv, base_dir, conf_file) == -1) { error("%s does not exist", gen_file[GEN_DEPFILE].name); return -1; } } else if (config_read(0, NULL, base_dir, conf_file) == -1) { return -1; } if ((ret = addksyms(file_syms)) != -1) { int i; GLOB_LIST *g = config_lstmod(NULL, NULL, 0); for (i = 0; g && i < g->pathc; i++) loadobj(g->pathv[i], ignore_suffix); resolve(); prtdepend(base_dir, nflag); } } else { /* not stdmode */ if ((ret = addksyms(file_syms)) != -1) { for (; argc > 0 && ret != -1; ++argv, --argc) loadobj(*argv, ignore_suffix); resolve(); prtdepend(base_dir, 1); } } return ret; } /* For common 3264 code, only compile main in the 64 bit version. */ #if defined(COMMON_3264) && defined(ONLY_32) /* Use the main in the 64 bit version */ #else int main(int argc, char **argv) { return depmod_main(argc, argv); } #endif /* defined(COMMON_3264) && defined(ONLY_32) */