diff options
| author | Rusty Russell <rusty@rustcorp.com.au> | 2003-02-14 08:35:21 -0600 |
|---|---|---|
| committer | Kai Germaschewski <kai@tp1.ruhr-uni-bochum.de> | 2003-02-14 08:35:21 -0600 |
| commit | 0e5064f80d41d75abbb10e040bbd7c600e08299c (patch) | |
| tree | 6eb8bdc574cf01754670b2f34396b1a152092b1c /scripts | |
| parent | 0571739cd43b80de3bfa08f55fbcf410878cbe75 (diff) | |
kbuild: Module alias and device table support
Introduces "MODULE_ALIAS" which modules can use to embed their own
aliases for modprobe to use. Also adds a "finishing" step to modules to
supplement their aliases based on MODULE_TABLE declarations, eg.
'usb:v0506p4601dl*dh*dc*dsc*dp*ic*isc*ip*' for drivers/usb/net/pegasus.o
Diffstat (limited to 'scripts')
| -rw-r--r-- | scripts/Makefile | 3 | ||||
| -rw-r--r-- | scripts/Makefile.modver | 19 | ||||
| -rw-r--r-- | scripts/file2alias.c | 259 | ||||
| -rw-r--r-- | scripts/file2alias_inc.c | 79 |
4 files changed, 357 insertions, 3 deletions
diff --git a/scripts/Makefile b/scripts/Makefile index 3927e5eab97f..cf5a1862ea1d 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -8,7 +8,8 @@ # docproc: Preprocess .tmpl file in order to generate .sgml documentation # conmakehash: Create arrays for initializing the kernel console tables -host-progs := fixdep split-include conmakehash docproc kallsyms modpost +host-progs := fixdep split-include conmakehash docproc kallsyms modpost \ + file2alias build-targets := $(host-progs) # Let clean descend into subdirs diff --git a/scripts/Makefile.modver b/scripts/Makefile.modver index 252dc42f7203..6d1a3cb32232 100644 --- a/scripts/Makefile.modver +++ b/scripts/Makefile.modver @@ -27,7 +27,7 @@ quiet_cmd_ld_ko_o = LD [M] $@ cmd_ld_ko_o = $(LD) $(LDFLAGS) $(LDFLAGS_MODULE) -o $@ \ $(filter-out FORCE,$^) -$(modules): %.ko :%.o %.ver.o FORCE +$(modules): %.ko :%.o %.ver.o $(if CONFIG_HOTPLUG,%.aliases.o) FORCE $(call if_changed,ld_ko_o) targets += $(modules) @@ -35,7 +35,7 @@ targets += $(modules) # Compile version info for unresolved symbols quiet_cmd_cc_o_c = CC $@ - cmd_cc_o_c = $(CC) $(CFLAGS) -c -o $@ $< + cmd_cc_o_c = $(CC) $(CFLAGS) $(CFLAGS_MODULE) -c -o $@ $< # We have a fake dependency on compile.h to make sure that we # notice if the compiler version changes under us. @@ -45,6 +45,21 @@ $(modules:.ko=.ver.o): %.ver.o: %.ver.c include/linux/compile.h FORCE targets += $(modules:.ko=.ver.o) +$(modules:.ko=.aliases.o): %.aliases.o: %.aliases.c FORCE + $(call if_changed,cc_o_c) + +targets += $(modules:.ko=.aliases.o) + +# Generate aliases + +quiet_cmd_file2alias = ALIAS $@ + cmd_file2alias = scripts/file2alias $< > $@ + +$(modules:.ko=.aliases.c): %.aliases.c: %.o FORCE + $(call if_changed,file2alias) + +targets += $(modules:.ko=.aliases.c) + # All the .ver.c files are generated using the helper "modpost" .PHONY: __modpost diff --git a/scripts/file2alias.c b/scripts/file2alias.c new file mode 100644 index 000000000000..525a766bab3e --- /dev/null +++ b/scripts/file2alias.c @@ -0,0 +1,259 @@ +/* Simple code to turn various tables in an ELF file into alias definitions. + This deals with kernel datastructures where they should be + dealt with: in the kernel source. + (C) 2002 Rusty Russell IBM Corporation. +*/ +#include <stdio.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <elf.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> + +/* 32 bits: if it turns out to be 64, we add explicitly (see EXTRA_SIZE). */ +typedef int kernel_ulong_t; +#include "../include/linux/types.h" +#include "../include/linux/mod_devicetable.h" + +static int switch_endian; + +static void __endian(const void *src, void *dest, unsigned int size) +{ + unsigned int i; + for (i = 0; i < size; i++) + ((unsigned char*)dest)[i] = ((unsigned char*)src)[size - i-1]; +} + +#define TO_NATIVE(x) \ +({ \ + typeof(x) __x; \ + if (switch_endian) __endian(&(x), &(__x), sizeof(__x)); \ + else __x = x; \ + __x; \ +}) + +#define ADD(str, sep, cond, field) \ +do { \ + strcat(str, sep); \ + if (cond) \ + sprintf(str + strlen(str), \ + sizeof(field) == 1 ? "%02X" : \ + sizeof(field) == 2 ? "%04X" : \ + sizeof(field) == 4 ? "%08X" : "", \ + field); \ + else \ + sprintf(str + strlen(str), "*"); \ +} while(0) + +/* Looks like "usb:vNpNdlNdhNdcNdscNdpNicNiscNipN" */ +static int do_usb_entry(const char *filename, + struct usb_device_id *id, char *alias) +{ + id->match_flags = TO_NATIVE(id->match_flags); + id->idVendor = TO_NATIVE(id->idVendor); + id->idProduct = TO_NATIVE(id->idProduct); + id->bcdDevice_lo = TO_NATIVE(id->bcdDevice_lo); + id->bcdDevice_hi = TO_NATIVE(id->bcdDevice_hi); + + strcpy(alias, "usb:"); + ADD(alias, "v", id->match_flags&USB_DEVICE_ID_MATCH_VENDOR, + id->idVendor); + ADD(alias, "p", id->match_flags&USB_DEVICE_ID_MATCH_PRODUCT, + id->idProduct); + ADD(alias, "dl", id->match_flags&USB_DEVICE_ID_MATCH_DEV_LO, + id->bcdDevice_lo); + ADD(alias, "dh", id->match_flags&USB_DEVICE_ID_MATCH_DEV_HI, + id->bcdDevice_hi); + ADD(alias, "dc", id->match_flags&USB_DEVICE_ID_MATCH_DEV_CLASS, + id->bDeviceClass); + ADD(alias, "dsc", + id->match_flags&USB_DEVICE_ID_MATCH_DEV_SUBCLASS, + id->bDeviceSubClass); + ADD(alias, "dp", + id->match_flags&USB_DEVICE_ID_MATCH_DEV_PROTOCOL, + id->bDeviceProtocol); + ADD(alias, "ic", + id->match_flags&USB_DEVICE_ID_MATCH_INT_CLASS, + id->bInterfaceClass); + ADD(alias, "isc", + id->match_flags&USB_DEVICE_ID_MATCH_INT_SUBCLASS, + id->bInterfaceSubClass); + ADD(alias, "ip", + id->match_flags&USB_DEVICE_ID_MATCH_INT_PROTOCOL, + id->bInterfaceProtocol); + return 1; +} + +/* Looks like: pci:vNdNsvNsdNcN. */ +static int do_pci_entry(const char *filename, + struct pci_device_id *id, char *alias) +{ + id->vendor = TO_NATIVE(id->vendor); + id->device = TO_NATIVE(id->device); + id->subvendor = TO_NATIVE(id->subvendor); + id->subdevice = TO_NATIVE(id->subdevice); + id->class = TO_NATIVE(id->class); + id->class_mask = TO_NATIVE(id->class_mask); + + strcpy(alias, "pci:"); + ADD(alias, "v", id->vendor != PCI_ANY_ID, id->vendor); + ADD(alias, "d", id->device != PCI_ANY_ID, id->device); + ADD(alias, "sv", id->subvendor != PCI_ANY_ID, id->subvendor); + ADD(alias, "sd", id->subdevice != PCI_ANY_ID, id->subdevice); + if (id->class_mask != 0 && id->class_mask != ~0) { + fprintf(stderr, + "file2alias: Can't handle class_mask in %s:%04X\n", + filename, id->class_mask); + return 0; + } + ADD(alias, "c", id->class_mask == ~0, id->class); + return 1; +} + +/* Ignore any prefix, eg. v850 prepends _ */ +static inline int sym_is(const char *symbol, const char *name) +{ + const char *match; + + match = strstr(symbol, name); + if (!match) + return 0; + return match[strlen(symbol)] == '\0'; +} + +/* Returns 1 if we output anything. */ +static int do_table(void *symval, unsigned long size, + unsigned long id_size, + void *function, + const char *filename, int *first) +{ + unsigned int i; + char alias[500]; + int (*do_entry)(const char *, void *entry, char *alias) = function; + int wrote = 0; + + if (size % id_size || size < id_size) { + fprintf(stderr, "WARNING: %s ids %lu bad size (each on %lu)\n", + filename, size, id_size); + return 0; + } + /* Leave last one: it's the terminator. */ + size -= id_size; + + for (i = 0; i < size; i += id_size) { + if (do_entry(filename, symval+i, alias)) { + /* Always end in a wildcard, for future extension */ + if (alias[strlen(alias)-1] != '*') + strcat(alias, "*"); + if (*first) { + printf("#include <linux/module.h>\n\n"); + *first = 0; + } + printf("MODULE_ALIAS(\"%s\");\n", alias); + wrote = 1; + } + } + return wrote; +} + +/* This is the best way of doing this without making a complete mess + of the code. */ +#undef analyse_file +#undef Elf_Ehdr +#undef Elf_Shdr +#undef Elf_Sym +#undef EXTRA_SIZE +#define analyse_file analyze_file32 +#define Elf_Ehdr Elf32_Ehdr +#define Elf_Shdr Elf32_Shdr +#define Elf_Sym Elf32_Sym +#define EXTRA_SIZE 0 +#include "file2alias_inc.c" +#undef analyse_file +#undef Elf_Ehdr +#undef Elf_Shdr +#undef Elf_Sym +#undef EXTRA_SIZE +#define analyse_file analyze_file64 +#define Elf_Ehdr Elf64_Ehdr +#define Elf_Shdr Elf64_Shdr +#define Elf_Sym Elf64_Sym +#define EXTRA_SIZE 4 +#include "file2alias_inc.c" + +static void *grab_file(const char *filename, unsigned long *size) +{ + struct stat st; + void *map; + int fd; + + fd = open(filename, O_RDONLY); + if (fd < 0) + return NULL; + if (fstat(fd, &st) != 0) { + close(fd); + return NULL; + } + *size = st.st_size; + map = mmap(NULL, *size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); + if (mmap == MAP_FAILED) { + close(fd); + return NULL; + } + close(fd); + return map; +} + +/* Look through files for __mod_*_device_table: emit alias definitions + for compiling in. */ +int main(int argc, char *argv[]) +{ + void *file; + unsigned long size; + int endian; + union { short s; char c[2]; } endian_test; + + endian_test.s = 1; + if (endian_test.c[1] == 1) endian = ELFDATA2MSB; + else if (endian_test.c[0] == 1) endian = ELFDATA2LSB; + else + abort(); + + for (; argv[1]; argv++) { + file = grab_file(argv[1], &size); + if (!file) { + fprintf(stderr, "file2alias: opening %s: %s\n", + argv[1], strerror(errno)); + continue; + } + + if (size < SELFMAG || memcmp(file, ELFMAG, SELFMAG) != 0) + goto bad_elf; + + if (((unsigned char *)file)[EI_DATA] != endian) + switch_endian = 1; + + switch (((unsigned char *)file)[EI_CLASS]) { + case ELFCLASS32: + analyze_file32(file, size, argv[1]); + break; + case ELFCLASS64: + analyze_file64(file, size, argv[1]); + break; + default: + goto bad_elf; + } + munmap(file, size); + continue; + + bad_elf: + fprintf(stderr, "file2alias: %s is not elf\n", argv[1]); + return 1; + } + return 0; +} diff --git a/scripts/file2alias_inc.c b/scripts/file2alias_inc.c new file mode 100644 index 000000000000..3fcf52e0e3d4 --- /dev/null +++ b/scripts/file2alias_inc.c @@ -0,0 +1,79 @@ +/* This contains the cookie-cutter code for ELF handling (32 v 64). + Return true if anything output. */ +static void analyse_file(Elf_Ehdr *hdr, + unsigned int size, + const char *filename) +{ + unsigned int i, num_syms = 0; + Elf_Shdr *sechdrs; + Elf_Sym *syms = NULL; + char *secstrings, *strtab = NULL; + int first = 1; + + if (size < sizeof(*hdr)) + goto truncated; + + sechdrs = (void *)hdr + TO_NATIVE(hdr->e_shoff); + if (switch_endian) { + hdr->e_shoff = TO_NATIVE(hdr->e_shoff); + hdr->e_shstrndx = TO_NATIVE(hdr->e_shstrndx); + hdr->e_shnum = TO_NATIVE(hdr->e_shnum); + for (i = 0; i < hdr->e_shnum; i++) { + sechdrs[i].sh_type = TO_NATIVE(sechdrs[i].sh_type); + sechdrs[i].sh_offset = TO_NATIVE(sechdrs[i].sh_offset); + sechdrs[i].sh_size = TO_NATIVE(sechdrs[i].sh_size); + sechdrs[i].sh_link = TO_NATIVE(sechdrs[i].sh_link); + } + } + + /* Find symbol table. */ + secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; + + for (i = 1; i < hdr->e_shnum; i++) { + if (sechdrs[i].sh_offset > size) + goto truncated; + if (sechdrs[i].sh_type == SHT_SYMTAB) { + syms = (void *)hdr + sechdrs[i].sh_offset; + num_syms = sechdrs[i].sh_size / sizeof(syms[0]); + } else if (sechdrs[i].sh_type == SHT_STRTAB) + strtab = (void *)hdr + sechdrs[i].sh_offset; + } + + if (!strtab || !syms) { + fprintf(stderr, "table2alias: %s no symtab?\n", filename); + return; + } + + for (i = 0; i < num_syms; i++) { + const char *symname; + void *symval; + + if (switch_endian) { + syms[i].st_shndx = TO_NATIVE(syms[i].st_shndx); + syms[i].st_name = TO_NATIVE(syms[i].st_name); + syms[i].st_value = TO_NATIVE(syms[i].st_value); + syms[i].st_size = TO_NATIVE(syms[i].st_size); + } + + if (!syms[i].st_shndx || syms[i].st_shndx >= hdr->e_shnum) + continue; + + symname = strtab + syms[i].st_name; + symval = (void *)hdr + + sechdrs[syms[i].st_shndx].sh_offset + + syms[i].st_value; + if (sym_is(symname, "__mod_pci_device_table")) + do_table(symval, syms[i].st_size, + sizeof(struct pci_device_id) + EXTRA_SIZE * 1, + do_pci_entry, filename, &first); + else if (sym_is(symname, "__mod_usb_device_table")) + do_table(symval, syms[i].st_size, + sizeof(struct usb_device_id) + EXTRA_SIZE * 1, + do_usb_entry, filename, &first); + } + return; + + truncated: + fprintf(stderr, "table2alias: %s is truncated.\n", filename); + return; +} |
