diff options
| author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
| commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
| tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /scripts/mod | |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'scripts/mod')
| -rw-r--r-- | scripts/mod/Makefile | 16 | ||||
| -rw-r--r-- | scripts/mod/empty.c | 1 | ||||
| -rw-r--r-- | scripts/mod/file2alias.c | 308 | ||||
| -rw-r--r-- | scripts/mod/mk_elfconfig.c | 65 | ||||
| -rw-r--r-- | scripts/mod/modpost.c | 797 | ||||
| -rw-r--r-- | scripts/mod/modpost.h | 107 | ||||
| -rw-r--r-- | scripts/mod/sumversion.c | 498 |
7 files changed, 1792 insertions, 0 deletions
diff --git a/scripts/mod/Makefile b/scripts/mod/Makefile new file mode 100644 index 000000000000..11d69c35e5b4 --- /dev/null +++ b/scripts/mod/Makefile | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | hostprogs-y := modpost mk_elfconfig | ||
| 2 | always := $(hostprogs-y) empty.o | ||
| 3 | |||
| 4 | modpost-objs := modpost.o file2alias.o sumversion.o | ||
| 5 | |||
| 6 | # dependencies on generated files need to be listed explicitly | ||
| 7 | |||
| 8 | $(obj)/modpost.o $(obj)/file2alias.o $(obj)/sumversion.o: $(obj)/elfconfig.h | ||
| 9 | |||
| 10 | quiet_cmd_elfconfig = MKELF $@ | ||
| 11 | cmd_elfconfig = $(obj)/mk_elfconfig $(ARCH) < $< > $@ | ||
| 12 | |||
| 13 | $(obj)/elfconfig.h: $(obj)/empty.o $(obj)/mk_elfconfig FORCE | ||
| 14 | $(call if_changed,elfconfig) | ||
| 15 | |||
| 16 | targets += elfconfig.h | ||
diff --git a/scripts/mod/empty.c b/scripts/mod/empty.c new file mode 100644 index 000000000000..49839cc4ff26 --- /dev/null +++ b/scripts/mod/empty.c | |||
| @@ -0,0 +1 @@ | |||
| /* empty file to figure out endianness / word size */ | |||
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c new file mode 100644 index 000000000000..d54b52d3bb6f --- /dev/null +++ b/scripts/mod/file2alias.c | |||
| @@ -0,0 +1,308 @@ | |||
| 1 | /* Simple code to turn various tables in an ELF file into alias definitions. | ||
| 2 | * This deals with kernel datastructures where they should be | ||
| 3 | * dealt with: in the kernel source. | ||
| 4 | * | ||
| 5 | * Copyright 2002-2003 Rusty Russell, IBM Corporation | ||
| 6 | * 2003 Kai Germaschewski | ||
| 7 | * | ||
| 8 | * | ||
| 9 | * This software may be used and distributed according to the terms | ||
| 10 | * of the GNU General Public License, incorporated herein by reference. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include "modpost.h" | ||
| 14 | |||
| 15 | /* We use the ELF typedefs for kernel_ulong_t but bite the bullet and | ||
| 16 | * use either stdint.h or inttypes.h for the rest. */ | ||
| 17 | #if KERNEL_ELFCLASS == ELFCLASS32 | ||
| 18 | typedef Elf32_Addr kernel_ulong_t; | ||
| 19 | #else | ||
| 20 | typedef Elf64_Addr kernel_ulong_t; | ||
| 21 | #endif | ||
| 22 | #ifdef __sun__ | ||
| 23 | #include <inttypes.h> | ||
| 24 | #else | ||
| 25 | #include <stdint.h> | ||
| 26 | #endif | ||
| 27 | |||
| 28 | typedef uint32_t __u32; | ||
| 29 | typedef uint16_t __u16; | ||
| 30 | typedef unsigned char __u8; | ||
| 31 | |||
| 32 | /* Big exception to the "don't include kernel headers into userspace, which | ||
| 33 | * even potentially has different endianness and word sizes, since | ||
| 34 | * we handle those differences explicitly below */ | ||
| 35 | #include "../../include/linux/mod_devicetable.h" | ||
| 36 | |||
| 37 | #define ADD(str, sep, cond, field) \ | ||
| 38 | do { \ | ||
| 39 | strcat(str, sep); \ | ||
| 40 | if (cond) \ | ||
| 41 | sprintf(str + strlen(str), \ | ||
| 42 | sizeof(field) == 1 ? "%02X" : \ | ||
| 43 | sizeof(field) == 2 ? "%04X" : \ | ||
| 44 | sizeof(field) == 4 ? "%08X" : "", \ | ||
| 45 | field); \ | ||
| 46 | else \ | ||
| 47 | sprintf(str + strlen(str), "*"); \ | ||
| 48 | } while(0) | ||
| 49 | |||
| 50 | /* Looks like "usb:vNpNdlNdhNdcNdscNdpNicNiscNipN" */ | ||
| 51 | static int do_usb_entry(const char *filename, | ||
| 52 | struct usb_device_id *id, char *alias) | ||
| 53 | { | ||
| 54 | id->match_flags = TO_NATIVE(id->match_flags); | ||
| 55 | id->idVendor = TO_NATIVE(id->idVendor); | ||
| 56 | id->idProduct = TO_NATIVE(id->idProduct); | ||
| 57 | id->bcdDevice_lo = TO_NATIVE(id->bcdDevice_lo); | ||
| 58 | id->bcdDevice_hi = TO_NATIVE(id->bcdDevice_hi); | ||
| 59 | |||
| 60 | /* | ||
| 61 | * Some modules (visor) have empty slots as placeholder for | ||
| 62 | * run-time specification that results in catch-all alias | ||
| 63 | */ | ||
| 64 | if (!(id->idVendor | id->bDeviceClass | id->bInterfaceClass)) | ||
| 65 | return 1; | ||
| 66 | |||
| 67 | strcpy(alias, "usb:"); | ||
| 68 | ADD(alias, "v", id->match_flags&USB_DEVICE_ID_MATCH_VENDOR, | ||
| 69 | id->idVendor); | ||
| 70 | ADD(alias, "p", id->match_flags&USB_DEVICE_ID_MATCH_PRODUCT, | ||
| 71 | id->idProduct); | ||
| 72 | ADD(alias, "dl", id->match_flags&USB_DEVICE_ID_MATCH_DEV_LO, | ||
| 73 | id->bcdDevice_lo); | ||
| 74 | ADD(alias, "dh", id->match_flags&USB_DEVICE_ID_MATCH_DEV_HI, | ||
| 75 | id->bcdDevice_hi); | ||
| 76 | ADD(alias, "dc", id->match_flags&USB_DEVICE_ID_MATCH_DEV_CLASS, | ||
| 77 | id->bDeviceClass); | ||
| 78 | ADD(alias, "dsc", | ||
| 79 | id->match_flags&USB_DEVICE_ID_MATCH_DEV_SUBCLASS, | ||
| 80 | id->bDeviceSubClass); | ||
| 81 | ADD(alias, "dp", | ||
| 82 | id->match_flags&USB_DEVICE_ID_MATCH_DEV_PROTOCOL, | ||
| 83 | id->bDeviceProtocol); | ||
| 84 | ADD(alias, "ic", | ||
| 85 | id->match_flags&USB_DEVICE_ID_MATCH_INT_CLASS, | ||
| 86 | id->bInterfaceClass); | ||
| 87 | ADD(alias, "isc", | ||
| 88 | id->match_flags&USB_DEVICE_ID_MATCH_INT_SUBCLASS, | ||
| 89 | id->bInterfaceSubClass); | ||
| 90 | ADD(alias, "ip", | ||
| 91 | id->match_flags&USB_DEVICE_ID_MATCH_INT_PROTOCOL, | ||
| 92 | id->bInterfaceProtocol); | ||
| 93 | return 1; | ||
| 94 | } | ||
| 95 | |||
| 96 | /* Looks like: ieee1394:venNmoNspNverN */ | ||
| 97 | static int do_ieee1394_entry(const char *filename, | ||
| 98 | struct ieee1394_device_id *id, char *alias) | ||
| 99 | { | ||
| 100 | id->match_flags = TO_NATIVE(id->match_flags); | ||
| 101 | id->vendor_id = TO_NATIVE(id->vendor_id); | ||
| 102 | id->model_id = TO_NATIVE(id->model_id); | ||
| 103 | id->specifier_id = TO_NATIVE(id->specifier_id); | ||
| 104 | id->version = TO_NATIVE(id->version); | ||
| 105 | |||
| 106 | strcpy(alias, "ieee1394:"); | ||
| 107 | ADD(alias, "ven", id->match_flags & IEEE1394_MATCH_VENDOR_ID, | ||
| 108 | id->vendor_id); | ||
| 109 | ADD(alias, "mo", id->match_flags & IEEE1394_MATCH_MODEL_ID, | ||
| 110 | id->model_id); | ||
| 111 | ADD(alias, "sp", id->match_flags & IEEE1394_MATCH_SPECIFIER_ID, | ||
| 112 | id->specifier_id); | ||
| 113 | ADD(alias, "ver", id->match_flags & IEEE1394_MATCH_VERSION, | ||
| 114 | id->version); | ||
| 115 | |||
| 116 | return 1; | ||
| 117 | } | ||
| 118 | |||
| 119 | /* Looks like: pci:vNdNsvNsdNbcNscNiN. */ | ||
| 120 | static int do_pci_entry(const char *filename, | ||
| 121 | struct pci_device_id *id, char *alias) | ||
| 122 | { | ||
| 123 | /* Class field can be divided into these three. */ | ||
| 124 | unsigned char baseclass, subclass, interface, | ||
| 125 | baseclass_mask, subclass_mask, interface_mask; | ||
| 126 | |||
| 127 | id->vendor = TO_NATIVE(id->vendor); | ||
| 128 | id->device = TO_NATIVE(id->device); | ||
| 129 | id->subvendor = TO_NATIVE(id->subvendor); | ||
| 130 | id->subdevice = TO_NATIVE(id->subdevice); | ||
| 131 | id->class = TO_NATIVE(id->class); | ||
| 132 | id->class_mask = TO_NATIVE(id->class_mask); | ||
| 133 | |||
| 134 | strcpy(alias, "pci:"); | ||
| 135 | ADD(alias, "v", id->vendor != PCI_ANY_ID, id->vendor); | ||
| 136 | ADD(alias, "d", id->device != PCI_ANY_ID, id->device); | ||
| 137 | ADD(alias, "sv", id->subvendor != PCI_ANY_ID, id->subvendor); | ||
| 138 | ADD(alias, "sd", id->subdevice != PCI_ANY_ID, id->subdevice); | ||
| 139 | |||
| 140 | baseclass = (id->class) >> 16; | ||
| 141 | baseclass_mask = (id->class_mask) >> 16; | ||
| 142 | subclass = (id->class) >> 8; | ||
| 143 | subclass_mask = (id->class_mask) >> 8; | ||
| 144 | interface = id->class; | ||
| 145 | interface_mask = id->class_mask; | ||
| 146 | |||
| 147 | if ((baseclass_mask != 0 && baseclass_mask != 0xFF) | ||
| 148 | || (subclass_mask != 0 && subclass_mask != 0xFF) | ||
| 149 | || (interface_mask != 0 && interface_mask != 0xFF)) { | ||
| 150 | fprintf(stderr, | ||
| 151 | "*** Warning: Can't handle masks in %s:%04X\n", | ||
| 152 | filename, id->class_mask); | ||
| 153 | return 0; | ||
| 154 | } | ||
| 155 | |||
| 156 | ADD(alias, "bc", baseclass_mask == 0xFF, baseclass); | ||
| 157 | ADD(alias, "sc", subclass_mask == 0xFF, subclass); | ||
| 158 | ADD(alias, "i", interface_mask == 0xFF, interface); | ||
| 159 | return 1; | ||
| 160 | } | ||
| 161 | |||
| 162 | /* looks like: "ccw:tNmNdtNdmN" */ | ||
| 163 | static int do_ccw_entry(const char *filename, | ||
| 164 | struct ccw_device_id *id, char *alias) | ||
| 165 | { | ||
| 166 | id->match_flags = TO_NATIVE(id->match_flags); | ||
| 167 | id->cu_type = TO_NATIVE(id->cu_type); | ||
| 168 | id->cu_model = TO_NATIVE(id->cu_model); | ||
| 169 | id->dev_type = TO_NATIVE(id->dev_type); | ||
| 170 | id->dev_model = TO_NATIVE(id->dev_model); | ||
| 171 | |||
| 172 | strcpy(alias, "ccw:"); | ||
| 173 | ADD(alias, "t", id->match_flags&CCW_DEVICE_ID_MATCH_CU_TYPE, | ||
| 174 | id->cu_type); | ||
| 175 | ADD(alias, "m", id->match_flags&CCW_DEVICE_ID_MATCH_CU_MODEL, | ||
| 176 | id->cu_model); | ||
| 177 | ADD(alias, "dt", id->match_flags&CCW_DEVICE_ID_MATCH_DEVICE_TYPE, | ||
| 178 | id->dev_type); | ||
| 179 | ADD(alias, "dm", id->match_flags&CCW_DEVICE_ID_MATCH_DEVICE_TYPE, | ||
| 180 | id->dev_model); | ||
| 181 | return 1; | ||
| 182 | } | ||
| 183 | |||
| 184 | /* Looks like: "serio:tyNprNidNexN" */ | ||
| 185 | static int do_serio_entry(const char *filename, | ||
| 186 | struct serio_device_id *id, char *alias) | ||
| 187 | { | ||
| 188 | id->type = TO_NATIVE(id->type); | ||
| 189 | id->proto = TO_NATIVE(id->proto); | ||
| 190 | id->id = TO_NATIVE(id->id); | ||
| 191 | id->extra = TO_NATIVE(id->extra); | ||
| 192 | |||
| 193 | strcpy(alias, "serio:"); | ||
| 194 | ADD(alias, "ty", id->type != SERIO_ANY, id->type); | ||
| 195 | ADD(alias, "pr", id->proto != SERIO_ANY, id->proto); | ||
| 196 | ADD(alias, "id", id->id != SERIO_ANY, id->id); | ||
| 197 | ADD(alias, "ex", id->extra != SERIO_ANY, id->extra); | ||
| 198 | |||
| 199 | return 1; | ||
| 200 | } | ||
| 201 | |||
| 202 | /* looks like: "pnp:dD" */ | ||
| 203 | static int do_pnp_entry(const char *filename, | ||
| 204 | struct pnp_device_id *id, char *alias) | ||
| 205 | { | ||
| 206 | sprintf(alias, "pnp:d%s", id->id); | ||
| 207 | return 1; | ||
| 208 | } | ||
| 209 | |||
| 210 | /* looks like: "pnp:cCdD..." */ | ||
| 211 | static int do_pnp_card_entry(const char *filename, | ||
| 212 | struct pnp_card_device_id *id, char *alias) | ||
| 213 | { | ||
| 214 | int i; | ||
| 215 | |||
| 216 | sprintf(alias, "pnp:c%s", id->id); | ||
| 217 | for (i = 0; i < PNP_MAX_DEVICES; i++) { | ||
| 218 | if (! *id->devs[i].id) | ||
| 219 | break; | ||
| 220 | sprintf(alias + strlen(alias), "d%s", id->devs[i].id); | ||
| 221 | } | ||
| 222 | return 1; | ||
| 223 | } | ||
| 224 | |||
| 225 | /* Ignore any prefix, eg. v850 prepends _ */ | ||
| 226 | static inline int sym_is(const char *symbol, const char *name) | ||
| 227 | { | ||
| 228 | const char *match; | ||
| 229 | |||
| 230 | match = strstr(symbol, name); | ||
| 231 | if (!match) | ||
| 232 | return 0; | ||
| 233 | return match[strlen(symbol)] == '\0'; | ||
| 234 | } | ||
| 235 | |||
| 236 | static void do_table(void *symval, unsigned long size, | ||
| 237 | unsigned long id_size, | ||
| 238 | void *function, | ||
| 239 | struct module *mod) | ||
| 240 | { | ||
| 241 | unsigned int i; | ||
| 242 | char alias[500]; | ||
| 243 | int (*do_entry)(const char *, void *entry, char *alias) = function; | ||
| 244 | |||
| 245 | if (size % id_size || size < id_size) { | ||
| 246 | fprintf(stderr, "*** Warning: %s ids %lu bad size " | ||
| 247 | "(each on %lu)\n", mod->name, size, id_size); | ||
| 248 | } | ||
| 249 | /* Leave last one: it's the terminator. */ | ||
| 250 | size -= id_size; | ||
| 251 | |||
| 252 | for (i = 0; i < size; i += id_size) { | ||
| 253 | if (do_entry(mod->name, symval+i, alias)) { | ||
| 254 | /* Always end in a wildcard, for future extension */ | ||
| 255 | if (alias[strlen(alias)-1] != '*') | ||
| 256 | strcat(alias, "*"); | ||
| 257 | buf_printf(&mod->dev_table_buf, | ||
| 258 | "MODULE_ALIAS(\"%s\");\n", alias); | ||
| 259 | } | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 263 | /* Create MODULE_ALIAS() statements. | ||
| 264 | * At this time, we cannot write the actual output C source yet, | ||
| 265 | * so we write into the mod->dev_table_buf buffer. */ | ||
| 266 | void handle_moddevtable(struct module *mod, struct elf_info *info, | ||
| 267 | Elf_Sym *sym, const char *symname) | ||
| 268 | { | ||
| 269 | void *symval; | ||
| 270 | |||
| 271 | /* We're looking for a section relative symbol */ | ||
| 272 | if (!sym->st_shndx || sym->st_shndx >= info->hdr->e_shnum) | ||
| 273 | return; | ||
| 274 | |||
| 275 | symval = (void *)info->hdr | ||
| 276 | + info->sechdrs[sym->st_shndx].sh_offset | ||
| 277 | + sym->st_value; | ||
| 278 | |||
| 279 | if (sym_is(symname, "__mod_pci_device_table")) | ||
| 280 | do_table(symval, sym->st_size, sizeof(struct pci_device_id), | ||
| 281 | do_pci_entry, mod); | ||
| 282 | else if (sym_is(symname, "__mod_usb_device_table")) | ||
| 283 | do_table(symval, sym->st_size, sizeof(struct usb_device_id), | ||
| 284 | do_usb_entry, mod); | ||
| 285 | else if (sym_is(symname, "__mod_ieee1394_device_table")) | ||
| 286 | do_table(symval, sym->st_size, sizeof(struct ieee1394_device_id), | ||
| 287 | do_ieee1394_entry, mod); | ||
| 288 | else if (sym_is(symname, "__mod_ccw_device_table")) | ||
| 289 | do_table(symval, sym->st_size, sizeof(struct ccw_device_id), | ||
| 290 | do_ccw_entry, mod); | ||
| 291 | else if (sym_is(symname, "__mod_serio_device_table")) | ||
| 292 | do_table(symval, sym->st_size, sizeof(struct serio_device_id), | ||
| 293 | do_serio_entry, mod); | ||
| 294 | else if (sym_is(symname, "__mod_pnp_device_table")) | ||
| 295 | do_table(symval, sym->st_size, sizeof(struct pnp_device_id), | ||
| 296 | do_pnp_entry, mod); | ||
| 297 | else if (sym_is(symname, "__mod_pnp_card_device_table")) | ||
| 298 | do_table(symval, sym->st_size, sizeof(struct pnp_card_device_id), | ||
| 299 | do_pnp_card_entry, mod); | ||
| 300 | } | ||
| 301 | |||
| 302 | /* Now add out buffered information to the generated C source */ | ||
| 303 | void add_moddevtable(struct buffer *buf, struct module *mod) | ||
| 304 | { | ||
| 305 | buf_printf(buf, "\n"); | ||
| 306 | buf_write(buf, mod->dev_table_buf.p, mod->dev_table_buf.pos); | ||
| 307 | free(mod->dev_table_buf.p); | ||
| 308 | } | ||
diff --git a/scripts/mod/mk_elfconfig.c b/scripts/mod/mk_elfconfig.c new file mode 100644 index 000000000000..de2aabf89fb3 --- /dev/null +++ b/scripts/mod/mk_elfconfig.c | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | #include <stdio.h> | ||
| 2 | #include <stdlib.h> | ||
| 3 | #include <string.h> | ||
| 4 | #include <elf.h> | ||
| 5 | |||
| 6 | int | ||
| 7 | main(int argc, char **argv) | ||
| 8 | { | ||
| 9 | unsigned char ei[EI_NIDENT]; | ||
| 10 | union { short s; char c[2]; } endian_test; | ||
| 11 | |||
| 12 | if (argc != 2) { | ||
| 13 | fprintf(stderr, "Error: no arch\n"); | ||
| 14 | } | ||
| 15 | if (fread(ei, 1, EI_NIDENT, stdin) != EI_NIDENT) { | ||
| 16 | fprintf(stderr, "Error: input truncated\n"); | ||
| 17 | return 1; | ||
| 18 | } | ||
| 19 | if (memcmp(ei, ELFMAG, SELFMAG) != 0) { | ||
| 20 | fprintf(stderr, "Error: not ELF\n"); | ||
| 21 | return 1; | ||
| 22 | } | ||
| 23 | switch (ei[EI_CLASS]) { | ||
| 24 | case ELFCLASS32: | ||
| 25 | printf("#define KERNEL_ELFCLASS ELFCLASS32\n"); | ||
| 26 | break; | ||
| 27 | case ELFCLASS64: | ||
| 28 | printf("#define KERNEL_ELFCLASS ELFCLASS64\n"); | ||
| 29 | break; | ||
| 30 | default: | ||
| 31 | abort(); | ||
| 32 | } | ||
| 33 | switch (ei[EI_DATA]) { | ||
| 34 | case ELFDATA2LSB: | ||
| 35 | printf("#define KERNEL_ELFDATA ELFDATA2LSB\n"); | ||
| 36 | break; | ||
| 37 | case ELFDATA2MSB: | ||
| 38 | printf("#define KERNEL_ELFDATA ELFDATA2MSB\n"); | ||
| 39 | break; | ||
| 40 | default: | ||
| 41 | abort(); | ||
| 42 | } | ||
| 43 | |||
| 44 | if (sizeof(unsigned long) == 4) { | ||
| 45 | printf("#define HOST_ELFCLASS ELFCLASS32\n"); | ||
| 46 | } else if (sizeof(unsigned long) == 8) { | ||
| 47 | printf("#define HOST_ELFCLASS ELFCLASS64\n"); | ||
| 48 | } | ||
| 49 | |||
| 50 | endian_test.s = 0x0102; | ||
| 51 | if (memcmp(endian_test.c, "\x01\x02", 2) == 0) | ||
| 52 | printf("#define HOST_ELFDATA ELFDATA2MSB\n"); | ||
| 53 | else if (memcmp(endian_test.c, "\x02\x01", 2) == 0) | ||
| 54 | printf("#define HOST_ELFDATA ELFDATA2LSB\n"); | ||
| 55 | else | ||
| 56 | abort(); | ||
| 57 | |||
| 58 | if ((strcmp(argv[1], "v850") == 0) || (strcmp(argv[1], "h8300") == 0)) | ||
| 59 | printf("#define MODULE_SYMBOL_PREFIX \"_\"\n"); | ||
| 60 | else | ||
| 61 | printf("#define MODULE_SYMBOL_PREFIX \"\"\n"); | ||
| 62 | |||
| 63 | return 0; | ||
| 64 | } | ||
| 65 | |||
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c new file mode 100644 index 000000000000..9b9f94c915d2 --- /dev/null +++ b/scripts/mod/modpost.c | |||
| @@ -0,0 +1,797 @@ | |||
| 1 | /* Postprocess module symbol versions | ||
| 2 | * | ||
| 3 | * Copyright 2003 Kai Germaschewski | ||
| 4 | * Copyright 2002-2004 Rusty Russell, IBM Corporation | ||
| 5 | * | ||
| 6 | * Based in part on module-init-tools/depmod.c,file2alias | ||
| 7 | * | ||
| 8 | * This software may be used and distributed according to the terms | ||
| 9 | * of the GNU General Public License, incorporated herein by reference. | ||
| 10 | * | ||
| 11 | * Usage: modpost vmlinux module1.o module2.o ... | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <ctype.h> | ||
| 15 | #include "modpost.h" | ||
| 16 | |||
| 17 | /* Are we using CONFIG_MODVERSIONS? */ | ||
| 18 | int modversions = 0; | ||
| 19 | /* Warn about undefined symbols? (do so if we have vmlinux) */ | ||
| 20 | int have_vmlinux = 0; | ||
| 21 | /* Is CONFIG_MODULE_SRCVERSION_ALL set? */ | ||
| 22 | static int all_versions = 0; | ||
| 23 | |||
| 24 | void | ||
| 25 | fatal(const char *fmt, ...) | ||
| 26 | { | ||
| 27 | va_list arglist; | ||
| 28 | |||
| 29 | fprintf(stderr, "FATAL: "); | ||
| 30 | |||
| 31 | va_start(arglist, fmt); | ||
| 32 | vfprintf(stderr, fmt, arglist); | ||
| 33 | va_end(arglist); | ||
| 34 | |||
| 35 | exit(1); | ||
| 36 | } | ||
| 37 | |||
| 38 | void | ||
| 39 | warn(const char *fmt, ...) | ||
| 40 | { | ||
| 41 | va_list arglist; | ||
| 42 | |||
| 43 | fprintf(stderr, "WARNING: "); | ||
| 44 | |||
| 45 | va_start(arglist, fmt); | ||
| 46 | vfprintf(stderr, fmt, arglist); | ||
| 47 | va_end(arglist); | ||
| 48 | } | ||
| 49 | |||
| 50 | void *do_nofail(void *ptr, const char *expr) | ||
| 51 | { | ||
| 52 | if (!ptr) { | ||
| 53 | fatal("modpost: Memory allocation failure: %s.\n", expr); | ||
| 54 | } | ||
| 55 | return ptr; | ||
| 56 | } | ||
| 57 | |||
| 58 | /* A list of all modules we processed */ | ||
| 59 | |||
| 60 | static struct module *modules; | ||
| 61 | |||
| 62 | struct module * | ||
| 63 | find_module(char *modname) | ||
| 64 | { | ||
| 65 | struct module *mod; | ||
| 66 | |||
| 67 | for (mod = modules; mod; mod = mod->next) | ||
| 68 | if (strcmp(mod->name, modname) == 0) | ||
| 69 | break; | ||
| 70 | return mod; | ||
| 71 | } | ||
| 72 | |||
| 73 | struct module * | ||
| 74 | new_module(char *modname) | ||
| 75 | { | ||
| 76 | struct module *mod; | ||
| 77 | char *p, *s; | ||
| 78 | |||
| 79 | mod = NOFAIL(malloc(sizeof(*mod))); | ||
| 80 | memset(mod, 0, sizeof(*mod)); | ||
| 81 | p = NOFAIL(strdup(modname)); | ||
| 82 | |||
| 83 | /* strip trailing .o */ | ||
| 84 | if ((s = strrchr(p, '.')) != NULL) | ||
| 85 | if (strcmp(s, ".o") == 0) | ||
| 86 | *s = '\0'; | ||
| 87 | |||
| 88 | /* add to list */ | ||
| 89 | mod->name = p; | ||
| 90 | mod->next = modules; | ||
| 91 | modules = mod; | ||
| 92 | |||
| 93 | return mod; | ||
| 94 | } | ||
| 95 | |||
| 96 | /* A hash of all exported symbols, | ||
| 97 | * struct symbol is also used for lists of unresolved symbols */ | ||
| 98 | |||
| 99 | #define SYMBOL_HASH_SIZE 1024 | ||
| 100 | |||
| 101 | struct symbol { | ||
| 102 | struct symbol *next; | ||
| 103 | struct module *module; | ||
| 104 | unsigned int crc; | ||
| 105 | int crc_valid; | ||
| 106 | unsigned int weak:1; | ||
| 107 | char name[0]; | ||
| 108 | }; | ||
| 109 | |||
| 110 | static struct symbol *symbolhash[SYMBOL_HASH_SIZE]; | ||
| 111 | |||
| 112 | /* This is based on the hash agorithm from gdbm, via tdb */ | ||
| 113 | static inline unsigned int tdb_hash(const char *name) | ||
| 114 | { | ||
| 115 | unsigned value; /* Used to compute the hash value. */ | ||
| 116 | unsigned i; /* Used to cycle through random values. */ | ||
| 117 | |||
| 118 | /* Set the initial value from the key size. */ | ||
| 119 | for (value = 0x238F13AF * strlen(name), i=0; name[i]; i++) | ||
| 120 | value = (value + (((unsigned char *)name)[i] << (i*5 % 24))); | ||
| 121 | |||
| 122 | return (1103515243 * value + 12345); | ||
| 123 | } | ||
| 124 | |||
| 125 | /* Allocate a new symbols for use in the hash of exported symbols or | ||
| 126 | * the list of unresolved symbols per module */ | ||
| 127 | |||
| 128 | struct symbol * | ||
| 129 | alloc_symbol(const char *name, unsigned int weak, struct symbol *next) | ||
| 130 | { | ||
| 131 | struct symbol *s = NOFAIL(malloc(sizeof(*s) + strlen(name) + 1)); | ||
| 132 | |||
| 133 | memset(s, 0, sizeof(*s)); | ||
| 134 | strcpy(s->name, name); | ||
| 135 | s->weak = weak; | ||
| 136 | s->next = next; | ||
| 137 | return s; | ||
| 138 | } | ||
| 139 | |||
| 140 | /* For the hash of exported symbols */ | ||
| 141 | |||
| 142 | void | ||
| 143 | new_symbol(const char *name, struct module *module, unsigned int *crc) | ||
| 144 | { | ||
| 145 | unsigned int hash; | ||
| 146 | struct symbol *new; | ||
| 147 | |||
| 148 | hash = tdb_hash(name) % SYMBOL_HASH_SIZE; | ||
| 149 | new = symbolhash[hash] = alloc_symbol(name, 0, symbolhash[hash]); | ||
| 150 | new->module = module; | ||
| 151 | if (crc) { | ||
| 152 | new->crc = *crc; | ||
| 153 | new->crc_valid = 1; | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | struct symbol * | ||
| 158 | find_symbol(const char *name) | ||
| 159 | { | ||
| 160 | struct symbol *s; | ||
| 161 | |||
| 162 | /* For our purposes, .foo matches foo. PPC64 needs this. */ | ||
| 163 | if (name[0] == '.') | ||
| 164 | name++; | ||
| 165 | |||
| 166 | for (s = symbolhash[tdb_hash(name) % SYMBOL_HASH_SIZE]; s; s=s->next) { | ||
| 167 | if (strcmp(s->name, name) == 0) | ||
| 168 | return s; | ||
| 169 | } | ||
| 170 | return NULL; | ||
| 171 | } | ||
| 172 | |||
| 173 | /* Add an exported symbol - it may have already been added without a | ||
| 174 | * CRC, in this case just update the CRC */ | ||
| 175 | void | ||
| 176 | add_exported_symbol(const char *name, struct module *module, unsigned int *crc) | ||
| 177 | { | ||
| 178 | struct symbol *s = find_symbol(name); | ||
| 179 | |||
| 180 | if (!s) { | ||
| 181 | new_symbol(name, module, crc); | ||
| 182 | return; | ||
| 183 | } | ||
| 184 | if (crc) { | ||
| 185 | s->crc = *crc; | ||
| 186 | s->crc_valid = 1; | ||
| 187 | } | ||
| 188 | } | ||
| 189 | |||
| 190 | void * | ||
| 191 | grab_file(const char *filename, unsigned long *size) | ||
| 192 | { | ||
| 193 | struct stat st; | ||
| 194 | void *map; | ||
| 195 | int fd; | ||
| 196 | |||
| 197 | fd = open(filename, O_RDONLY); | ||
| 198 | if (fd < 0 || fstat(fd, &st) != 0) | ||
| 199 | return NULL; | ||
| 200 | |||
| 201 | *size = st.st_size; | ||
| 202 | map = mmap(NULL, *size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); | ||
| 203 | close(fd); | ||
| 204 | |||
| 205 | if (map == MAP_FAILED) | ||
| 206 | return NULL; | ||
| 207 | return map; | ||
| 208 | } | ||
| 209 | |||
| 210 | /* | ||
| 211 | Return a copy of the next line in a mmap'ed file. | ||
| 212 | spaces in the beginning of the line is trimmed away. | ||
| 213 | Return a pointer to a static buffer. | ||
| 214 | */ | ||
| 215 | char* | ||
| 216 | get_next_line(unsigned long *pos, void *file, unsigned long size) | ||
| 217 | { | ||
| 218 | static char line[4096]; | ||
| 219 | int skip = 1; | ||
| 220 | size_t len = 0; | ||
| 221 | signed char *p = (signed char *)file + *pos; | ||
| 222 | char *s = line; | ||
| 223 | |||
| 224 | for (; *pos < size ; (*pos)++) | ||
| 225 | { | ||
| 226 | if (skip && isspace(*p)) { | ||
| 227 | p++; | ||
| 228 | continue; | ||
| 229 | } | ||
| 230 | skip = 0; | ||
| 231 | if (*p != '\n' && (*pos < size)) { | ||
| 232 | len++; | ||
| 233 | *s++ = *p++; | ||
| 234 | if (len > 4095) | ||
| 235 | break; /* Too long, stop */ | ||
| 236 | } else { | ||
| 237 | /* End of string */ | ||
| 238 | *s = '\0'; | ||
| 239 | return line; | ||
| 240 | } | ||
| 241 | } | ||
| 242 | /* End of buffer */ | ||
| 243 | return NULL; | ||
| 244 | } | ||
| 245 | |||
| 246 | void | ||
| 247 | release_file(void *file, unsigned long size) | ||
| 248 | { | ||
| 249 | munmap(file, size); | ||
| 250 | } | ||
| 251 | |||
| 252 | void | ||
| 253 | parse_elf(struct elf_info *info, const char *filename) | ||
| 254 | { | ||
| 255 | unsigned int i; | ||
| 256 | Elf_Ehdr *hdr = info->hdr; | ||
| 257 | Elf_Shdr *sechdrs; | ||
| 258 | Elf_Sym *sym; | ||
| 259 | |||
| 260 | hdr = grab_file(filename, &info->size); | ||
| 261 | if (!hdr) { | ||
| 262 | perror(filename); | ||
| 263 | abort(); | ||
| 264 | } | ||
| 265 | info->hdr = hdr; | ||
| 266 | if (info->size < sizeof(*hdr)) | ||
| 267 | goto truncated; | ||
| 268 | |||
| 269 | /* Fix endianness in ELF header */ | ||
| 270 | hdr->e_shoff = TO_NATIVE(hdr->e_shoff); | ||
| 271 | hdr->e_shstrndx = TO_NATIVE(hdr->e_shstrndx); | ||
| 272 | hdr->e_shnum = TO_NATIVE(hdr->e_shnum); | ||
| 273 | hdr->e_machine = TO_NATIVE(hdr->e_machine); | ||
| 274 | sechdrs = (void *)hdr + hdr->e_shoff; | ||
| 275 | info->sechdrs = sechdrs; | ||
| 276 | |||
| 277 | /* Fix endianness in section headers */ | ||
| 278 | for (i = 0; i < hdr->e_shnum; i++) { | ||
| 279 | sechdrs[i].sh_type = TO_NATIVE(sechdrs[i].sh_type); | ||
| 280 | sechdrs[i].sh_offset = TO_NATIVE(sechdrs[i].sh_offset); | ||
| 281 | sechdrs[i].sh_size = TO_NATIVE(sechdrs[i].sh_size); | ||
| 282 | sechdrs[i].sh_link = TO_NATIVE(sechdrs[i].sh_link); | ||
| 283 | sechdrs[i].sh_name = TO_NATIVE(sechdrs[i].sh_name); | ||
| 284 | } | ||
| 285 | /* Find symbol table. */ | ||
| 286 | for (i = 1; i < hdr->e_shnum; i++) { | ||
| 287 | const char *secstrings | ||
| 288 | = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; | ||
| 289 | |||
| 290 | if (sechdrs[i].sh_offset > info->size) | ||
| 291 | goto truncated; | ||
| 292 | if (strcmp(secstrings+sechdrs[i].sh_name, ".modinfo") == 0) { | ||
| 293 | info->modinfo = (void *)hdr + sechdrs[i].sh_offset; | ||
| 294 | info->modinfo_len = sechdrs[i].sh_size; | ||
| 295 | } | ||
| 296 | if (sechdrs[i].sh_type != SHT_SYMTAB) | ||
| 297 | continue; | ||
| 298 | |||
| 299 | info->symtab_start = (void *)hdr + sechdrs[i].sh_offset; | ||
| 300 | info->symtab_stop = (void *)hdr + sechdrs[i].sh_offset | ||
| 301 | + sechdrs[i].sh_size; | ||
| 302 | info->strtab = (void *)hdr + | ||
| 303 | sechdrs[sechdrs[i].sh_link].sh_offset; | ||
| 304 | } | ||
| 305 | if (!info->symtab_start) { | ||
| 306 | fprintf(stderr, "modpost: %s no symtab?\n", filename); | ||
| 307 | abort(); | ||
| 308 | } | ||
| 309 | /* Fix endianness in symbols */ | ||
| 310 | for (sym = info->symtab_start; sym < info->symtab_stop; sym++) { | ||
| 311 | sym->st_shndx = TO_NATIVE(sym->st_shndx); | ||
| 312 | sym->st_name = TO_NATIVE(sym->st_name); | ||
| 313 | sym->st_value = TO_NATIVE(sym->st_value); | ||
| 314 | sym->st_size = TO_NATIVE(sym->st_size); | ||
| 315 | } | ||
| 316 | return; | ||
| 317 | |||
| 318 | truncated: | ||
| 319 | fprintf(stderr, "modpost: %s is truncated.\n", filename); | ||
| 320 | abort(); | ||
| 321 | } | ||
| 322 | |||
| 323 | void | ||
| 324 | parse_elf_finish(struct elf_info *info) | ||
| 325 | { | ||
| 326 | release_file(info->hdr, info->size); | ||
| 327 | } | ||
| 328 | |||
| 329 | #define CRC_PFX MODULE_SYMBOL_PREFIX "__crc_" | ||
| 330 | #define KSYMTAB_PFX MODULE_SYMBOL_PREFIX "__ksymtab_" | ||
| 331 | |||
| 332 | void | ||
| 333 | handle_modversions(struct module *mod, struct elf_info *info, | ||
| 334 | Elf_Sym *sym, const char *symname) | ||
| 335 | { | ||
| 336 | unsigned int crc; | ||
| 337 | |||
| 338 | switch (sym->st_shndx) { | ||
| 339 | case SHN_COMMON: | ||
| 340 | fprintf(stderr, "*** Warning: \"%s\" [%s] is COMMON symbol\n", | ||
| 341 | symname, mod->name); | ||
| 342 | break; | ||
| 343 | case SHN_ABS: | ||
| 344 | /* CRC'd symbol */ | ||
| 345 | if (memcmp(symname, CRC_PFX, strlen(CRC_PFX)) == 0) { | ||
| 346 | crc = (unsigned int) sym->st_value; | ||
| 347 | add_exported_symbol(symname + strlen(CRC_PFX), | ||
| 348 | mod, &crc); | ||
| 349 | } | ||
| 350 | break; | ||
| 351 | case SHN_UNDEF: | ||
| 352 | /* undefined symbol */ | ||
| 353 | if (ELF_ST_BIND(sym->st_info) != STB_GLOBAL && | ||
| 354 | ELF_ST_BIND(sym->st_info) != STB_WEAK) | ||
| 355 | break; | ||
| 356 | /* ignore global offset table */ | ||
| 357 | if (strcmp(symname, "_GLOBAL_OFFSET_TABLE_") == 0) | ||
| 358 | break; | ||
| 359 | /* ignore __this_module, it will be resolved shortly */ | ||
| 360 | if (strcmp(symname, MODULE_SYMBOL_PREFIX "__this_module") == 0) | ||
| 361 | break; | ||
| 362 | #ifdef STT_REGISTER | ||
| 363 | if (info->hdr->e_machine == EM_SPARC || | ||
| 364 | info->hdr->e_machine == EM_SPARCV9) { | ||
| 365 | /* Ignore register directives. */ | ||
| 366 | if (ELF_ST_TYPE(sym->st_info) == STT_REGISTER) | ||
| 367 | break; | ||
| 368 | } | ||
| 369 | #endif | ||
| 370 | |||
| 371 | if (memcmp(symname, MODULE_SYMBOL_PREFIX, | ||
| 372 | strlen(MODULE_SYMBOL_PREFIX)) == 0) | ||
| 373 | mod->unres = alloc_symbol(symname + | ||
| 374 | strlen(MODULE_SYMBOL_PREFIX), | ||
| 375 | ELF_ST_BIND(sym->st_info) == STB_WEAK, | ||
| 376 | mod->unres); | ||
| 377 | break; | ||
| 378 | default: | ||
| 379 | /* All exported symbols */ | ||
| 380 | if (memcmp(symname, KSYMTAB_PFX, strlen(KSYMTAB_PFX)) == 0) { | ||
| 381 | add_exported_symbol(symname + strlen(KSYMTAB_PFX), | ||
| 382 | mod, NULL); | ||
| 383 | } | ||
| 384 | if (strcmp(symname, MODULE_SYMBOL_PREFIX "init_module") == 0) | ||
| 385 | mod->has_init = 1; | ||
| 386 | if (strcmp(symname, MODULE_SYMBOL_PREFIX "cleanup_module") == 0) | ||
| 387 | mod->has_cleanup = 1; | ||
| 388 | break; | ||
| 389 | } | ||
| 390 | } | ||
| 391 | |||
| 392 | int | ||
| 393 | is_vmlinux(const char *modname) | ||
| 394 | { | ||
| 395 | const char *myname; | ||
| 396 | |||
| 397 | if ((myname = strrchr(modname, '/'))) | ||
| 398 | myname++; | ||
| 399 | else | ||
| 400 | myname = modname; | ||
| 401 | |||
| 402 | return strcmp(myname, "vmlinux") == 0; | ||
| 403 | } | ||
| 404 | |||
| 405 | /* Parse tag=value strings from .modinfo section */ | ||
| 406 | static char *next_string(char *string, unsigned long *secsize) | ||
| 407 | { | ||
| 408 | /* Skip non-zero chars */ | ||
| 409 | while (string[0]) { | ||
| 410 | string++; | ||
| 411 | if ((*secsize)-- <= 1) | ||
| 412 | return NULL; | ||
| 413 | } | ||
| 414 | |||
| 415 | /* Skip any zero padding. */ | ||
| 416 | while (!string[0]) { | ||
| 417 | string++; | ||
| 418 | if ((*secsize)-- <= 1) | ||
| 419 | return NULL; | ||
| 420 | } | ||
| 421 | return string; | ||
| 422 | } | ||
| 423 | |||
| 424 | static char *get_modinfo(void *modinfo, unsigned long modinfo_len, | ||
| 425 | const char *tag) | ||
| 426 | { | ||
| 427 | char *p; | ||
| 428 | unsigned int taglen = strlen(tag); | ||
| 429 | unsigned long size = modinfo_len; | ||
| 430 | |||
| 431 | for (p = modinfo; p; p = next_string(p, &size)) { | ||
| 432 | if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=') | ||
| 433 | return p + taglen + 1; | ||
| 434 | } | ||
| 435 | return NULL; | ||
| 436 | } | ||
| 437 | |||
| 438 | void | ||
| 439 | read_symbols(char *modname) | ||
| 440 | { | ||
| 441 | const char *symname; | ||
| 442 | char *version; | ||
| 443 | struct module *mod; | ||
| 444 | struct elf_info info = { }; | ||
| 445 | Elf_Sym *sym; | ||
| 446 | |||
| 447 | parse_elf(&info, modname); | ||
| 448 | |||
| 449 | mod = new_module(modname); | ||
| 450 | |||
| 451 | /* When there's no vmlinux, don't print warnings about | ||
| 452 | * unresolved symbols (since there'll be too many ;) */ | ||
| 453 | if (is_vmlinux(modname)) { | ||
| 454 | unsigned int fake_crc = 0; | ||
| 455 | have_vmlinux = 1; | ||
| 456 | add_exported_symbol("struct_module", mod, &fake_crc); | ||
| 457 | mod->skip = 1; | ||
| 458 | } | ||
| 459 | |||
| 460 | for (sym = info.symtab_start; sym < info.symtab_stop; sym++) { | ||
| 461 | symname = info.strtab + sym->st_name; | ||
| 462 | |||
| 463 | handle_modversions(mod, &info, sym, symname); | ||
| 464 | handle_moddevtable(mod, &info, sym, symname); | ||
| 465 | } | ||
| 466 | |||
| 467 | version = get_modinfo(info.modinfo, info.modinfo_len, "version"); | ||
| 468 | if (version) | ||
| 469 | maybe_frob_rcs_version(modname, version, info.modinfo, | ||
| 470 | version - (char *)info.hdr); | ||
| 471 | if (version || (all_versions && !is_vmlinux(modname))) | ||
| 472 | get_src_version(modname, mod->srcversion, | ||
| 473 | sizeof(mod->srcversion)-1); | ||
| 474 | |||
| 475 | parse_elf_finish(&info); | ||
| 476 | |||
| 477 | /* Our trick to get versioning for struct_module - it's | ||
| 478 | * never passed as an argument to an exported function, so | ||
| 479 | * the automatic versioning doesn't pick it up, but it's really | ||
| 480 | * important anyhow */ | ||
| 481 | if (modversions) | ||
| 482 | mod->unres = alloc_symbol("struct_module", 0, mod->unres); | ||
| 483 | } | ||
| 484 | |||
| 485 | #define SZ 500 | ||
| 486 | |||
| 487 | /* We first write the generated file into memory using the | ||
| 488 | * following helper, then compare to the file on disk and | ||
| 489 | * only update the later if anything changed */ | ||
| 490 | |||
| 491 | void __attribute__((format(printf, 2, 3))) | ||
| 492 | buf_printf(struct buffer *buf, const char *fmt, ...) | ||
| 493 | { | ||
| 494 | char tmp[SZ]; | ||
| 495 | int len; | ||
| 496 | va_list ap; | ||
| 497 | |||
| 498 | va_start(ap, fmt); | ||
| 499 | len = vsnprintf(tmp, SZ, fmt, ap); | ||
| 500 | if (buf->size - buf->pos < len + 1) { | ||
| 501 | buf->size += 128; | ||
| 502 | buf->p = realloc(buf->p, buf->size); | ||
| 503 | } | ||
| 504 | strncpy(buf->p + buf->pos, tmp, len + 1); | ||
| 505 | buf->pos += len; | ||
| 506 | va_end(ap); | ||
| 507 | } | ||
| 508 | |||
| 509 | void | ||
| 510 | buf_write(struct buffer *buf, const char *s, int len) | ||
| 511 | { | ||
| 512 | if (buf->size - buf->pos < len) { | ||
| 513 | buf->size += len; | ||
| 514 | buf->p = realloc(buf->p, buf->size); | ||
| 515 | } | ||
| 516 | strncpy(buf->p + buf->pos, s, len); | ||
| 517 | buf->pos += len; | ||
| 518 | } | ||
| 519 | |||
| 520 | /* Header for the generated file */ | ||
| 521 | |||
| 522 | void | ||
| 523 | add_header(struct buffer *b, struct module *mod) | ||
| 524 | { | ||
| 525 | buf_printf(b, "#include <linux/module.h>\n"); | ||
| 526 | buf_printf(b, "#include <linux/vermagic.h>\n"); | ||
| 527 | buf_printf(b, "#include <linux/compiler.h>\n"); | ||
| 528 | buf_printf(b, "\n"); | ||
| 529 | buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n"); | ||
| 530 | buf_printf(b, "\n"); | ||
| 531 | buf_printf(b, "#undef unix\n"); /* We have a module called "unix" */ | ||
| 532 | buf_printf(b, "struct module __this_module\n"); | ||
| 533 | buf_printf(b, "__attribute__((section(\".gnu.linkonce.this_module\"))) = {\n"); | ||
| 534 | buf_printf(b, " .name = __stringify(KBUILD_MODNAME),\n"); | ||
| 535 | if (mod->has_init) | ||
| 536 | buf_printf(b, " .init = init_module,\n"); | ||
| 537 | if (mod->has_cleanup) | ||
| 538 | buf_printf(b, "#ifdef CONFIG_MODULE_UNLOAD\n" | ||
| 539 | " .exit = cleanup_module,\n" | ||
| 540 | "#endif\n"); | ||
| 541 | buf_printf(b, "};\n"); | ||
| 542 | } | ||
| 543 | |||
| 544 | /* Record CRCs for unresolved symbols */ | ||
| 545 | |||
| 546 | void | ||
| 547 | add_versions(struct buffer *b, struct module *mod) | ||
| 548 | { | ||
| 549 | struct symbol *s, *exp; | ||
| 550 | |||
| 551 | for (s = mod->unres; s; s = s->next) { | ||
| 552 | exp = find_symbol(s->name); | ||
| 553 | if (!exp || exp->module == mod) { | ||
| 554 | if (have_vmlinux && !s->weak) | ||
| 555 | fprintf(stderr, "*** Warning: \"%s\" [%s.ko] " | ||
| 556 | "undefined!\n", s->name, mod->name); | ||
| 557 | continue; | ||
| 558 | } | ||
| 559 | s->module = exp->module; | ||
| 560 | s->crc_valid = exp->crc_valid; | ||
| 561 | s->crc = exp->crc; | ||
| 562 | } | ||
| 563 | |||
| 564 | if (!modversions) | ||
| 565 | return; | ||
| 566 | |||
| 567 | buf_printf(b, "\n"); | ||
| 568 | buf_printf(b, "static const struct modversion_info ____versions[]\n"); | ||
| 569 | buf_printf(b, "__attribute_used__\n"); | ||
| 570 | buf_printf(b, "__attribute__((section(\"__versions\"))) = {\n"); | ||
| 571 | |||
| 572 | for (s = mod->unres; s; s = s->next) { | ||
| 573 | if (!s->module) { | ||
| 574 | continue; | ||
| 575 | } | ||
| 576 | if (!s->crc_valid) { | ||
| 577 | fprintf(stderr, "*** Warning: \"%s\" [%s.ko] " | ||
| 578 | "has no CRC!\n", | ||
| 579 | s->name, mod->name); | ||
| 580 | continue; | ||
| 581 | } | ||
| 582 | buf_printf(b, "\t{ %#8x, \"%s\" },\n", s->crc, s->name); | ||
| 583 | } | ||
| 584 | |||
| 585 | buf_printf(b, "};\n"); | ||
| 586 | } | ||
| 587 | |||
| 588 | void | ||
| 589 | add_depends(struct buffer *b, struct module *mod, struct module *modules) | ||
| 590 | { | ||
| 591 | struct symbol *s; | ||
| 592 | struct module *m; | ||
| 593 | int first = 1; | ||
| 594 | |||
| 595 | for (m = modules; m; m = m->next) { | ||
| 596 | m->seen = is_vmlinux(m->name); | ||
| 597 | } | ||
| 598 | |||
| 599 | buf_printf(b, "\n"); | ||
| 600 | buf_printf(b, "static const char __module_depends[]\n"); | ||
| 601 | buf_printf(b, "__attribute_used__\n"); | ||
| 602 | buf_printf(b, "__attribute__((section(\".modinfo\"))) =\n"); | ||
| 603 | buf_printf(b, "\"depends="); | ||
| 604 | for (s = mod->unres; s; s = s->next) { | ||
| 605 | if (!s->module) | ||
| 606 | continue; | ||
| 607 | |||
| 608 | if (s->module->seen) | ||
| 609 | continue; | ||
| 610 | |||
| 611 | s->module->seen = 1; | ||
| 612 | buf_printf(b, "%s%s", first ? "" : ",", | ||
| 613 | strrchr(s->module->name, '/') + 1); | ||
| 614 | first = 0; | ||
| 615 | } | ||
| 616 | buf_printf(b, "\";\n"); | ||
| 617 | } | ||
| 618 | |||
| 619 | void | ||
| 620 | add_srcversion(struct buffer *b, struct module *mod) | ||
| 621 | { | ||
| 622 | if (mod->srcversion[0]) { | ||
| 623 | buf_printf(b, "\n"); | ||
| 624 | buf_printf(b, "MODULE_INFO(srcversion, \"%s\");\n", | ||
| 625 | mod->srcversion); | ||
| 626 | } | ||
| 627 | } | ||
| 628 | |||
| 629 | void | ||
| 630 | write_if_changed(struct buffer *b, const char *fname) | ||
| 631 | { | ||
| 632 | char *tmp; | ||
| 633 | FILE *file; | ||
| 634 | struct stat st; | ||
| 635 | |||
| 636 | file = fopen(fname, "r"); | ||
| 637 | if (!file) | ||
| 638 | goto write; | ||
| 639 | |||
| 640 | if (fstat(fileno(file), &st) < 0) | ||
| 641 | goto close_write; | ||
| 642 | |||
| 643 | if (st.st_size != b->pos) | ||
| 644 | goto close_write; | ||
| 645 | |||
| 646 | tmp = NOFAIL(malloc(b->pos)); | ||
| 647 | if (fread(tmp, 1, b->pos, file) != b->pos) | ||
| 648 | goto free_write; | ||
| 649 | |||
| 650 | if (memcmp(tmp, b->p, b->pos) != 0) | ||
| 651 | goto free_write; | ||
| 652 | |||
| 653 | free(tmp); | ||
| 654 | fclose(file); | ||
| 655 | return; | ||
| 656 | |||
| 657 | free_write: | ||
| 658 | free(tmp); | ||
| 659 | close_write: | ||
| 660 | fclose(file); | ||
| 661 | write: | ||
| 662 | file = fopen(fname, "w"); | ||
| 663 | if (!file) { | ||
| 664 | perror(fname); | ||
| 665 | exit(1); | ||
| 666 | } | ||
| 667 | if (fwrite(b->p, 1, b->pos, file) != b->pos) { | ||
| 668 | perror(fname); | ||
| 669 | exit(1); | ||
| 670 | } | ||
| 671 | fclose(file); | ||
| 672 | } | ||
| 673 | |||
| 674 | void | ||
| 675 | read_dump(const char *fname) | ||
| 676 | { | ||
| 677 | unsigned long size, pos = 0; | ||
| 678 | void *file = grab_file(fname, &size); | ||
| 679 | char *line; | ||
| 680 | |||
| 681 | if (!file) | ||
| 682 | /* No symbol versions, silently ignore */ | ||
| 683 | return; | ||
| 684 | |||
| 685 | while ((line = get_next_line(&pos, file, size))) { | ||
| 686 | char *symname, *modname, *d; | ||
| 687 | unsigned int crc; | ||
| 688 | struct module *mod; | ||
| 689 | |||
| 690 | if (!(symname = strchr(line, '\t'))) | ||
| 691 | goto fail; | ||
| 692 | *symname++ = '\0'; | ||
| 693 | if (!(modname = strchr(symname, '\t'))) | ||
| 694 | goto fail; | ||
| 695 | *modname++ = '\0'; | ||
| 696 | if (strchr(modname, '\t')) | ||
| 697 | goto fail; | ||
| 698 | crc = strtoul(line, &d, 16); | ||
| 699 | if (*symname == '\0' || *modname == '\0' || *d != '\0') | ||
| 700 | goto fail; | ||
| 701 | |||
| 702 | if (!(mod = find_module(modname))) { | ||
| 703 | if (is_vmlinux(modname)) { | ||
| 704 | have_vmlinux = 1; | ||
| 705 | } | ||
| 706 | mod = new_module(NOFAIL(strdup(modname))); | ||
| 707 | mod->skip = 1; | ||
| 708 | } | ||
| 709 | add_exported_symbol(symname, mod, &crc); | ||
| 710 | } | ||
| 711 | return; | ||
| 712 | fail: | ||
| 713 | fatal("parse error in symbol dump file\n"); | ||
| 714 | } | ||
| 715 | |||
| 716 | void | ||
| 717 | write_dump(const char *fname) | ||
| 718 | { | ||
| 719 | struct buffer buf = { }; | ||
| 720 | struct symbol *symbol; | ||
| 721 | int n; | ||
| 722 | |||
| 723 | for (n = 0; n < SYMBOL_HASH_SIZE ; n++) { | ||
| 724 | symbol = symbolhash[n]; | ||
| 725 | while (symbol) { | ||
| 726 | symbol = symbol->next; | ||
| 727 | } | ||
| 728 | } | ||
| 729 | |||
| 730 | for (n = 0; n < SYMBOL_HASH_SIZE ; n++) { | ||
| 731 | symbol = symbolhash[n]; | ||
| 732 | while (symbol) { | ||
| 733 | buf_printf(&buf, "0x%08x\t%s\t%s\n", symbol->crc, | ||
| 734 | symbol->name, symbol->module->name); | ||
| 735 | symbol = symbol->next; | ||
| 736 | } | ||
| 737 | } | ||
| 738 | write_if_changed(&buf, fname); | ||
| 739 | } | ||
| 740 | |||
| 741 | int | ||
| 742 | main(int argc, char **argv) | ||
| 743 | { | ||
| 744 | struct module *mod; | ||
| 745 | struct buffer buf = { }; | ||
| 746 | char fname[SZ]; | ||
| 747 | char *dump_read = NULL, *dump_write = NULL; | ||
| 748 | int opt; | ||
| 749 | |||
| 750 | while ((opt = getopt(argc, argv, "i:mo:a")) != -1) { | ||
| 751 | switch(opt) { | ||
| 752 | case 'i': | ||
| 753 | dump_read = optarg; | ||
| 754 | break; | ||
| 755 | case 'm': | ||
| 756 | modversions = 1; | ||
| 757 | break; | ||
| 758 | case 'o': | ||
| 759 | dump_write = optarg; | ||
| 760 | break; | ||
| 761 | case 'a': | ||
| 762 | all_versions = 1; | ||
| 763 | break; | ||
| 764 | default: | ||
| 765 | exit(1); | ||
| 766 | } | ||
| 767 | } | ||
| 768 | |||
| 769 | if (dump_read) | ||
| 770 | read_dump(dump_read); | ||
| 771 | |||
| 772 | while (optind < argc) { | ||
| 773 | read_symbols(argv[optind++]); | ||
| 774 | } | ||
| 775 | |||
| 776 | for (mod = modules; mod; mod = mod->next) { | ||
| 777 | if (mod->skip) | ||
| 778 | continue; | ||
| 779 | |||
| 780 | buf.pos = 0; | ||
| 781 | |||
| 782 | add_header(&buf, mod); | ||
| 783 | add_versions(&buf, mod); | ||
| 784 | add_depends(&buf, mod, modules); | ||
| 785 | add_moddevtable(&buf, mod); | ||
| 786 | add_srcversion(&buf, mod); | ||
| 787 | |||
| 788 | sprintf(fname, "%s.mod.c", mod->name); | ||
| 789 | write_if_changed(&buf, fname); | ||
| 790 | } | ||
| 791 | |||
| 792 | if (dump_write) | ||
| 793 | write_dump(dump_write); | ||
| 794 | |||
| 795 | return 0; | ||
| 796 | } | ||
| 797 | |||
diff --git a/scripts/mod/modpost.h b/scripts/mod/modpost.h new file mode 100644 index 000000000000..7334d839145d --- /dev/null +++ b/scripts/mod/modpost.h | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | #include <stdio.h> | ||
| 2 | #include <stdlib.h> | ||
| 3 | #include <stdarg.h> | ||
| 4 | #include <string.h> | ||
| 5 | #include <sys/types.h> | ||
| 6 | #include <sys/stat.h> | ||
| 7 | #include <sys/mman.h> | ||
| 8 | #include <fcntl.h> | ||
| 9 | #include <unistd.h> | ||
| 10 | #include <elf.h> | ||
| 11 | |||
| 12 | #include "elfconfig.h" | ||
| 13 | |||
| 14 | #if KERNEL_ELFCLASS == ELFCLASS32 | ||
| 15 | |||
| 16 | #define Elf_Ehdr Elf32_Ehdr | ||
| 17 | #define Elf_Shdr Elf32_Shdr | ||
| 18 | #define Elf_Sym Elf32_Sym | ||
| 19 | #define ELF_ST_BIND ELF32_ST_BIND | ||
| 20 | #define ELF_ST_TYPE ELF32_ST_TYPE | ||
| 21 | |||
| 22 | #else | ||
| 23 | |||
| 24 | #define Elf_Ehdr Elf64_Ehdr | ||
| 25 | #define Elf_Shdr Elf64_Shdr | ||
| 26 | #define Elf_Sym Elf64_Sym | ||
| 27 | #define ELF_ST_BIND ELF64_ST_BIND | ||
| 28 | #define ELF_ST_TYPE ELF64_ST_TYPE | ||
| 29 | |||
| 30 | #endif | ||
| 31 | |||
| 32 | #if KERNEL_ELFDATA != HOST_ELFDATA | ||
| 33 | |||
| 34 | static inline void __endian(const void *src, void *dest, unsigned int size) | ||
| 35 | { | ||
| 36 | unsigned int i; | ||
| 37 | for (i = 0; i < size; i++) | ||
| 38 | ((unsigned char*)dest)[i] = ((unsigned char*)src)[size - i-1]; | ||
| 39 | } | ||
| 40 | |||
| 41 | |||
| 42 | |||
| 43 | #define TO_NATIVE(x) \ | ||
| 44 | ({ \ | ||
| 45 | typeof(x) __x; \ | ||
| 46 | __endian(&(x), &(__x), sizeof(__x)); \ | ||
| 47 | __x; \ | ||
| 48 | }) | ||
| 49 | |||
| 50 | #else /* endianness matches */ | ||
| 51 | |||
| 52 | #define TO_NATIVE(x) (x) | ||
| 53 | |||
| 54 | #endif | ||
| 55 | |||
| 56 | #define NOFAIL(ptr) do_nofail((ptr), #ptr) | ||
| 57 | void *do_nofail(void *ptr, const char *expr); | ||
| 58 | |||
| 59 | struct buffer { | ||
| 60 | char *p; | ||
| 61 | int pos; | ||
| 62 | int size; | ||
| 63 | }; | ||
| 64 | |||
| 65 | void __attribute__((format(printf, 2, 3))) | ||
| 66 | buf_printf(struct buffer *buf, const char *fmt, ...); | ||
| 67 | |||
| 68 | void | ||
| 69 | buf_write(struct buffer *buf, const char *s, int len); | ||
| 70 | |||
| 71 | struct module { | ||
| 72 | struct module *next; | ||
| 73 | const char *name; | ||
| 74 | struct symbol *unres; | ||
| 75 | int seen; | ||
| 76 | int skip; | ||
| 77 | int has_init; | ||
| 78 | int has_cleanup; | ||
| 79 | struct buffer dev_table_buf; | ||
| 80 | char srcversion[25]; | ||
| 81 | }; | ||
| 82 | |||
| 83 | struct elf_info { | ||
| 84 | unsigned long size; | ||
| 85 | Elf_Ehdr *hdr; | ||
| 86 | Elf_Shdr *sechdrs; | ||
| 87 | Elf_Sym *symtab_start; | ||
| 88 | Elf_Sym *symtab_stop; | ||
| 89 | const char *strtab; | ||
| 90 | char *modinfo; | ||
| 91 | unsigned int modinfo_len; | ||
| 92 | }; | ||
| 93 | |||
| 94 | void handle_moddevtable(struct module *mod, struct elf_info *info, | ||
| 95 | Elf_Sym *sym, const char *symname); | ||
| 96 | |||
| 97 | void add_moddevtable(struct buffer *buf, struct module *mod); | ||
| 98 | |||
| 99 | void maybe_frob_rcs_version(const char *modfilename, | ||
| 100 | char *version, | ||
| 101 | void *modinfo, | ||
| 102 | unsigned long modinfo_offset); | ||
| 103 | void get_src_version(const char *modname, char sum[], unsigned sumlen); | ||
| 104 | |||
| 105 | void *grab_file(const char *filename, unsigned long *size); | ||
| 106 | char* get_next_line(unsigned long *pos, void *file, unsigned long size); | ||
| 107 | void release_file(void *file, unsigned long size); | ||
diff --git a/scripts/mod/sumversion.c b/scripts/mod/sumversion.c new file mode 100644 index 000000000000..1112347245c0 --- /dev/null +++ b/scripts/mod/sumversion.c | |||
| @@ -0,0 +1,498 @@ | |||
| 1 | #include <netinet/in.h> | ||
| 2 | #ifdef __sun__ | ||
| 3 | #include <inttypes.h> | ||
| 4 | #else | ||
| 5 | #include <stdint.h> | ||
| 6 | #endif | ||
| 7 | #include <ctype.h> | ||
| 8 | #include <errno.h> | ||
| 9 | #include <string.h> | ||
| 10 | #include "modpost.h" | ||
| 11 | |||
| 12 | /* | ||
| 13 | * Stolen form Cryptographic API. | ||
| 14 | * | ||
| 15 | * MD4 Message Digest Algorithm (RFC1320). | ||
| 16 | * | ||
| 17 | * Implementation derived from Andrew Tridgell and Steve French's | ||
| 18 | * CIFS MD4 implementation, and the cryptoapi implementation | ||
| 19 | * originally based on the public domain implementation written | ||
| 20 | * by Colin Plumb in 1993. | ||
| 21 | * | ||
| 22 | * Copyright (c) Andrew Tridgell 1997-1998. | ||
| 23 | * Modified by Steve French (sfrench@us.ibm.com) 2002 | ||
| 24 | * Copyright (c) Cryptoapi developers. | ||
| 25 | * Copyright (c) 2002 David S. Miller (davem@redhat.com) | ||
| 26 | * Copyright (c) 2002 James Morris <jmorris@intercode.com.au> | ||
| 27 | * | ||
| 28 | * This program is free software; you can redistribute it and/or modify | ||
| 29 | * it under the terms of the GNU General Public License as published by | ||
| 30 | * the Free Software Foundation; either version 2 of the License, or | ||
| 31 | * (at your option) any later version. | ||
| 32 | * | ||
| 33 | */ | ||
| 34 | #define MD4_DIGEST_SIZE 16 | ||
| 35 | #define MD4_HMAC_BLOCK_SIZE 64 | ||
| 36 | #define MD4_BLOCK_WORDS 16 | ||
| 37 | #define MD4_HASH_WORDS 4 | ||
| 38 | |||
| 39 | struct md4_ctx { | ||
| 40 | uint32_t hash[MD4_HASH_WORDS]; | ||
| 41 | uint32_t block[MD4_BLOCK_WORDS]; | ||
| 42 | uint64_t byte_count; | ||
| 43 | }; | ||
| 44 | |||
| 45 | static inline uint32_t lshift(uint32_t x, unsigned int s) | ||
| 46 | { | ||
| 47 | x &= 0xFFFFFFFF; | ||
| 48 | return ((x << s) & 0xFFFFFFFF) | (x >> (32 - s)); | ||
| 49 | } | ||
| 50 | |||
| 51 | static inline uint32_t F(uint32_t x, uint32_t y, uint32_t z) | ||
| 52 | { | ||
| 53 | return (x & y) | ((~x) & z); | ||
| 54 | } | ||
| 55 | |||
| 56 | static inline uint32_t G(uint32_t x, uint32_t y, uint32_t z) | ||
| 57 | { | ||
| 58 | return (x & y) | (x & z) | (y & z); | ||
| 59 | } | ||
| 60 | |||
| 61 | static inline uint32_t H(uint32_t x, uint32_t y, uint32_t z) | ||
| 62 | { | ||
| 63 | return x ^ y ^ z; | ||
| 64 | } | ||
| 65 | |||
| 66 | #define ROUND1(a,b,c,d,k,s) (a = lshift(a + F(b,c,d) + k, s)) | ||
| 67 | #define ROUND2(a,b,c,d,k,s) (a = lshift(a + G(b,c,d) + k + (uint32_t)0x5A827999,s)) | ||
| 68 | #define ROUND3(a,b,c,d,k,s) (a = lshift(a + H(b,c,d) + k + (uint32_t)0x6ED9EBA1,s)) | ||
| 69 | |||
| 70 | /* XXX: this stuff can be optimized */ | ||
| 71 | static inline void le32_to_cpu_array(uint32_t *buf, unsigned int words) | ||
| 72 | { | ||
| 73 | while (words--) { | ||
| 74 | *buf = ntohl(*buf); | ||
| 75 | buf++; | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | static inline void cpu_to_le32_array(uint32_t *buf, unsigned int words) | ||
| 80 | { | ||
| 81 | while (words--) { | ||
| 82 | *buf = htonl(*buf); | ||
| 83 | buf++; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | static void md4_transform(uint32_t *hash, uint32_t const *in) | ||
| 88 | { | ||
| 89 | uint32_t a, b, c, d; | ||
| 90 | |||
| 91 | a = hash[0]; | ||
| 92 | b = hash[1]; | ||
| 93 | c = hash[2]; | ||
| 94 | d = hash[3]; | ||
| 95 | |||
| 96 | ROUND1(a, b, c, d, in[0], 3); | ||
| 97 | ROUND1(d, a, b, c, in[1], 7); | ||
| 98 | ROUND1(c, d, a, b, in[2], 11); | ||
| 99 | ROUND1(b, c, d, a, in[3], 19); | ||
| 100 | ROUND1(a, b, c, d, in[4], 3); | ||
| 101 | ROUND1(d, a, b, c, in[5], 7); | ||
| 102 | ROUND1(c, d, a, b, in[6], 11); | ||
| 103 | ROUND1(b, c, d, a, in[7], 19); | ||
| 104 | ROUND1(a, b, c, d, in[8], 3); | ||
| 105 | ROUND1(d, a, b, c, in[9], 7); | ||
| 106 | ROUND1(c, d, a, b, in[10], 11); | ||
| 107 | ROUND1(b, c, d, a, in[11], 19); | ||
| 108 | ROUND1(a, b, c, d, in[12], 3); | ||
| 109 | ROUND1(d, a, b, c, in[13], 7); | ||
| 110 | ROUND1(c, d, a, b, in[14], 11); | ||
| 111 | ROUND1(b, c, d, a, in[15], 19); | ||
| 112 | |||
| 113 | ROUND2(a, b, c, d,in[ 0], 3); | ||
| 114 | ROUND2(d, a, b, c, in[4], 5); | ||
| 115 | ROUND2(c, d, a, b, in[8], 9); | ||
| 116 | ROUND2(b, c, d, a, in[12], 13); | ||
| 117 | ROUND2(a, b, c, d, in[1], 3); | ||
| 118 | ROUND2(d, a, b, c, in[5], 5); | ||
| 119 | ROUND2(c, d, a, b, in[9], 9); | ||
| 120 | ROUND2(b, c, d, a, in[13], 13); | ||
| 121 | ROUND2(a, b, c, d, in[2], 3); | ||
| 122 | ROUND2(d, a, b, c, in[6], 5); | ||
| 123 | ROUND2(c, d, a, b, in[10], 9); | ||
| 124 | ROUND2(b, c, d, a, in[14], 13); | ||
| 125 | ROUND2(a, b, c, d, in[3], 3); | ||
| 126 | ROUND2(d, a, b, c, in[7], 5); | ||
| 127 | ROUND2(c, d, a, b, in[11], 9); | ||
| 128 | ROUND2(b, c, d, a, in[15], 13); | ||
| 129 | |||
| 130 | ROUND3(a, b, c, d,in[ 0], 3); | ||
| 131 | ROUND3(d, a, b, c, in[8], 9); | ||
| 132 | ROUND3(c, d, a, b, in[4], 11); | ||
| 133 | ROUND3(b, c, d, a, in[12], 15); | ||
| 134 | ROUND3(a, b, c, d, in[2], 3); | ||
| 135 | ROUND3(d, a, b, c, in[10], 9); | ||
| 136 | ROUND3(c, d, a, b, in[6], 11); | ||
| 137 | ROUND3(b, c, d, a, in[14], 15); | ||
| 138 | ROUND3(a, b, c, d, in[1], 3); | ||
| 139 | ROUND3(d, a, b, c, in[9], 9); | ||
| 140 | ROUND3(c, d, a, b, in[5], 11); | ||
| 141 | ROUND3(b, c, d, a, in[13], 15); | ||
| 142 | ROUND3(a, b, c, d, in[3], 3); | ||
| 143 | ROUND3(d, a, b, c, in[11], 9); | ||
| 144 | ROUND3(c, d, a, b, in[7], 11); | ||
| 145 | ROUND3(b, c, d, a, in[15], 15); | ||
| 146 | |||
| 147 | hash[0] += a; | ||
| 148 | hash[1] += b; | ||
| 149 | hash[2] += c; | ||
| 150 | hash[3] += d; | ||
| 151 | } | ||
| 152 | |||
| 153 | static inline void md4_transform_helper(struct md4_ctx *ctx) | ||
| 154 | { | ||
| 155 | le32_to_cpu_array(ctx->block, sizeof(ctx->block) / sizeof(uint32_t)); | ||
| 156 | md4_transform(ctx->hash, ctx->block); | ||
| 157 | } | ||
| 158 | |||
| 159 | static void md4_init(struct md4_ctx *mctx) | ||
| 160 | { | ||
| 161 | mctx->hash[0] = 0x67452301; | ||
| 162 | mctx->hash[1] = 0xefcdab89; | ||
| 163 | mctx->hash[2] = 0x98badcfe; | ||
| 164 | mctx->hash[3] = 0x10325476; | ||
| 165 | mctx->byte_count = 0; | ||
| 166 | } | ||
| 167 | |||
| 168 | static void md4_update(struct md4_ctx *mctx, | ||
| 169 | const unsigned char *data, unsigned int len) | ||
| 170 | { | ||
| 171 | const uint32_t avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f); | ||
| 172 | |||
| 173 | mctx->byte_count += len; | ||
| 174 | |||
| 175 | if (avail > len) { | ||
| 176 | memcpy((char *)mctx->block + (sizeof(mctx->block) - avail), | ||
| 177 | data, len); | ||
| 178 | return; | ||
| 179 | } | ||
| 180 | |||
| 181 | memcpy((char *)mctx->block + (sizeof(mctx->block) - avail), | ||
| 182 | data, avail); | ||
| 183 | |||
| 184 | md4_transform_helper(mctx); | ||
| 185 | data += avail; | ||
| 186 | len -= avail; | ||
| 187 | |||
| 188 | while (len >= sizeof(mctx->block)) { | ||
| 189 | memcpy(mctx->block, data, sizeof(mctx->block)); | ||
| 190 | md4_transform_helper(mctx); | ||
| 191 | data += sizeof(mctx->block); | ||
| 192 | len -= sizeof(mctx->block); | ||
| 193 | } | ||
| 194 | |||
| 195 | memcpy(mctx->block, data, len); | ||
| 196 | } | ||
| 197 | |||
| 198 | static void md4_final_ascii(struct md4_ctx *mctx, char *out, unsigned int len) | ||
| 199 | { | ||
| 200 | const unsigned int offset = mctx->byte_count & 0x3f; | ||
| 201 | char *p = (char *)mctx->block + offset; | ||
| 202 | int padding = 56 - (offset + 1); | ||
| 203 | |||
| 204 | *p++ = 0x80; | ||
| 205 | if (padding < 0) { | ||
| 206 | memset(p, 0x00, padding + sizeof (uint64_t)); | ||
| 207 | md4_transform_helper(mctx); | ||
| 208 | p = (char *)mctx->block; | ||
| 209 | padding = 56; | ||
| 210 | } | ||
| 211 | |||
| 212 | memset(p, 0, padding); | ||
| 213 | mctx->block[14] = mctx->byte_count << 3; | ||
| 214 | mctx->block[15] = mctx->byte_count >> 29; | ||
| 215 | le32_to_cpu_array(mctx->block, (sizeof(mctx->block) - | ||
| 216 | sizeof(uint64_t)) / sizeof(uint32_t)); | ||
| 217 | md4_transform(mctx->hash, mctx->block); | ||
| 218 | cpu_to_le32_array(mctx->hash, sizeof(mctx->hash) / sizeof(uint32_t)); | ||
| 219 | |||
| 220 | snprintf(out, len, "%08X%08X%08X%08X", | ||
| 221 | mctx->hash[0], mctx->hash[1], mctx->hash[2], mctx->hash[3]); | ||
| 222 | } | ||
| 223 | |||
| 224 | static inline void add_char(unsigned char c, struct md4_ctx *md) | ||
| 225 | { | ||
| 226 | md4_update(md, &c, 1); | ||
| 227 | } | ||
| 228 | |||
| 229 | static int parse_string(const char *file, unsigned long len, | ||
| 230 | struct md4_ctx *md) | ||
| 231 | { | ||
| 232 | unsigned long i; | ||
| 233 | |||
| 234 | add_char(file[0], md); | ||
| 235 | for (i = 1; i < len; i++) { | ||
| 236 | add_char(file[i], md); | ||
| 237 | if (file[i] == '"' && file[i-1] != '\\') | ||
| 238 | break; | ||
| 239 | } | ||
| 240 | return i; | ||
| 241 | } | ||
| 242 | |||
| 243 | static int parse_comment(const char *file, unsigned long len) | ||
| 244 | { | ||
| 245 | unsigned long i; | ||
| 246 | |||
| 247 | for (i = 2; i < len; i++) { | ||
| 248 | if (file[i-1] == '*' && file[i] == '/') | ||
| 249 | break; | ||
| 250 | } | ||
| 251 | return i; | ||
| 252 | } | ||
| 253 | |||
| 254 | /* FIXME: Handle .s files differently (eg. # starts comments) --RR */ | ||
| 255 | static int parse_file(const signed char *fname, struct md4_ctx *md) | ||
| 256 | { | ||
| 257 | signed char *file; | ||
| 258 | unsigned long i, len; | ||
| 259 | |||
| 260 | file = grab_file(fname, &len); | ||
| 261 | if (!file) | ||
| 262 | return 0; | ||
| 263 | |||
| 264 | for (i = 0; i < len; i++) { | ||
| 265 | /* Collapse and ignore \ and CR. */ | ||
| 266 | if (file[i] == '\\' && (i+1 < len) && file[i+1] == '\n') { | ||
| 267 | i++; | ||
| 268 | continue; | ||
| 269 | } | ||
| 270 | |||
| 271 | /* Ignore whitespace */ | ||
| 272 | if (isspace(file[i])) | ||
| 273 | continue; | ||
| 274 | |||
| 275 | /* Handle strings as whole units */ | ||
| 276 | if (file[i] == '"') { | ||
| 277 | i += parse_string(file+i, len - i, md); | ||
| 278 | continue; | ||
| 279 | } | ||
| 280 | |||
| 281 | /* Comments: ignore */ | ||
| 282 | if (file[i] == '/' && file[i+1] == '*') { | ||
| 283 | i += parse_comment(file+i, len - i); | ||
| 284 | continue; | ||
| 285 | } | ||
| 286 | |||
| 287 | add_char(file[i], md); | ||
| 288 | } | ||
| 289 | release_file(file, len); | ||
| 290 | return 1; | ||
| 291 | } | ||
| 292 | |||
| 293 | /* We have dir/file.o. Open dir/.file.o.cmd, look for deps_ line to | ||
| 294 | * figure out source file. */ | ||
| 295 | static int parse_source_files(const char *objfile, struct md4_ctx *md) | ||
| 296 | { | ||
| 297 | char *cmd, *file, *line, *dir; | ||
| 298 | const char *base; | ||
| 299 | unsigned long flen, pos = 0; | ||
| 300 | int dirlen, ret = 0, check_files = 0; | ||
| 301 | |||
| 302 | cmd = NOFAIL(malloc(strlen(objfile) + sizeof("..cmd"))); | ||
| 303 | |||
| 304 | base = strrchr(objfile, '/'); | ||
| 305 | if (base) { | ||
| 306 | base++; | ||
| 307 | dirlen = base - objfile; | ||
| 308 | sprintf(cmd, "%.*s.%s.cmd", dirlen, objfile, base); | ||
| 309 | } else { | ||
| 310 | dirlen = 0; | ||
| 311 | sprintf(cmd, ".%s.cmd", objfile); | ||
| 312 | } | ||
| 313 | dir = NOFAIL(malloc(dirlen + 1)); | ||
| 314 | strncpy(dir, objfile, dirlen); | ||
| 315 | dir[dirlen] = '\0'; | ||
| 316 | |||
| 317 | file = grab_file(cmd, &flen); | ||
| 318 | if (!file) { | ||
| 319 | fprintf(stderr, "Warning: could not find %s for %s\n", | ||
| 320 | cmd, objfile); | ||
| 321 | goto out; | ||
| 322 | } | ||
| 323 | |||
| 324 | /* There will be a line like so: | ||
| 325 | deps_drivers/net/dummy.o := \ | ||
| 326 | drivers/net/dummy.c \ | ||
| 327 | $(wildcard include/config/net/fastroute.h) \ | ||
| 328 | include/linux/config.h \ | ||
| 329 | $(wildcard include/config/h.h) \ | ||
| 330 | include/linux/module.h \ | ||
| 331 | |||
| 332 | Sum all files in the same dir or subdirs. | ||
| 333 | */ | ||
| 334 | while ((line = get_next_line(&pos, file, flen)) != NULL) { | ||
| 335 | signed char* p = line; | ||
| 336 | if (strncmp(line, "deps_", sizeof("deps_")-1) == 0) { | ||
| 337 | check_files = 1; | ||
| 338 | continue; | ||
| 339 | } | ||
| 340 | if (!check_files) | ||
| 341 | continue; | ||
| 342 | |||
| 343 | /* Continue until line does not end with '\' */ | ||
| 344 | if ( *(p + strlen(p)-1) != '\\') | ||
| 345 | break; | ||
| 346 | /* Terminate line at first space, to get rid of final ' \' */ | ||
| 347 | while (*p) { | ||
| 348 | if (isspace(*p)) { | ||
| 349 | *p = '\0'; | ||
| 350 | break; | ||
| 351 | } | ||
| 352 | p++; | ||
| 353 | } | ||
| 354 | |||
| 355 | /* Check if this file is in same dir as objfile */ | ||
| 356 | if ((strstr(line, dir)+strlen(dir)-1) == strrchr(line, '/')) { | ||
| 357 | if (!parse_file(line, md)) { | ||
| 358 | fprintf(stderr, | ||
| 359 | "Warning: could not open %s: %s\n", | ||
| 360 | line, strerror(errno)); | ||
| 361 | goto out_file; | ||
| 362 | } | ||
| 363 | |||
| 364 | } | ||
| 365 | |||
| 366 | } | ||
| 367 | |||
| 368 | /* Everyone parsed OK */ | ||
| 369 | ret = 1; | ||
| 370 | out_file: | ||
| 371 | release_file(file, flen); | ||
| 372 | out: | ||
| 373 | free(dir); | ||
| 374 | free(cmd); | ||
| 375 | return ret; | ||
| 376 | } | ||
| 377 | |||
| 378 | /* Calc and record src checksum. */ | ||
| 379 | void get_src_version(const char *modname, char sum[], unsigned sumlen) | ||
| 380 | { | ||
| 381 | void *file; | ||
| 382 | unsigned long len; | ||
| 383 | struct md4_ctx md; | ||
| 384 | char *sources, *end, *fname; | ||
| 385 | const char *basename; | ||
| 386 | char filelist[strlen(getenv("MODVERDIR")) + strlen("/") + | ||
| 387 | strlen(modname) - strlen(".o") + strlen(".mod") + 1 ]; | ||
| 388 | |||
| 389 | /* Source files for module are in .tmp_versions/modname.mod, | ||
| 390 | after the first line. */ | ||
| 391 | if (strrchr(modname, '/')) | ||
| 392 | basename = strrchr(modname, '/') + 1; | ||
| 393 | else | ||
| 394 | basename = modname; | ||
| 395 | sprintf(filelist, "%s/%.*s.mod", getenv("MODVERDIR"), | ||
| 396 | (int) strlen(basename) - 2, basename); | ||
| 397 | |||
| 398 | file = grab_file(filelist, &len); | ||
| 399 | if (!file) { | ||
| 400 | fprintf(stderr, "Warning: could not find versions for %s\n", | ||
| 401 | filelist); | ||
| 402 | return; | ||
| 403 | } | ||
| 404 | |||
| 405 | sources = strchr(file, '\n'); | ||
| 406 | if (!sources) { | ||
| 407 | fprintf(stderr, "Warning: malformed versions file for %s\n", | ||
| 408 | modname); | ||
| 409 | goto release; | ||
| 410 | } | ||
| 411 | |||
| 412 | sources++; | ||
| 413 | end = strchr(sources, '\n'); | ||
| 414 | if (!end) { | ||
| 415 | fprintf(stderr, "Warning: bad ending versions file for %s\n", | ||
| 416 | modname); | ||
| 417 | goto release; | ||
| 418 | } | ||
| 419 | *end = '\0'; | ||
| 420 | |||
| 421 | md4_init(&md); | ||
| 422 | while ((fname = strsep(&sources, " ")) != NULL) { | ||
| 423 | if (!*fname) | ||
| 424 | continue; | ||
| 425 | if (!parse_source_files(fname, &md)) | ||
| 426 | goto release; | ||
| 427 | } | ||
| 428 | |||
| 429 | md4_final_ascii(&md, sum, sumlen); | ||
| 430 | release: | ||
| 431 | release_file(file, len); | ||
| 432 | } | ||
| 433 | |||
| 434 | static void write_version(const char *filename, const char *sum, | ||
| 435 | unsigned long offset) | ||
| 436 | { | ||
| 437 | int fd; | ||
| 438 | |||
| 439 | fd = open(filename, O_RDWR); | ||
| 440 | if (fd < 0) { | ||
| 441 | fprintf(stderr, "Warning: changing sum in %s failed: %s\n", | ||
| 442 | filename, strerror(errno)); | ||
| 443 | return; | ||
| 444 | } | ||
| 445 | |||
| 446 | if (lseek(fd, offset, SEEK_SET) == (off_t)-1) { | ||
| 447 | fprintf(stderr, "Warning: changing sum in %s:%lu failed: %s\n", | ||
| 448 | filename, offset, strerror(errno)); | ||
| 449 | goto out; | ||
| 450 | } | ||
| 451 | |||
| 452 | if (write(fd, sum, strlen(sum)+1) != strlen(sum)+1) { | ||
| 453 | fprintf(stderr, "Warning: writing sum in %s failed: %s\n", | ||
| 454 | filename, strerror(errno)); | ||
| 455 | goto out; | ||
| 456 | } | ||
| 457 | out: | ||
| 458 | close(fd); | ||
| 459 | } | ||
| 460 | |||
| 461 | static int strip_rcs_crap(signed char *version) | ||
| 462 | { | ||
| 463 | unsigned int len, full_len; | ||
| 464 | |||
| 465 | if (strncmp(version, "$Revision", strlen("$Revision")) != 0) | ||
| 466 | return 0; | ||
| 467 | |||
| 468 | /* Space for version string follows. */ | ||
| 469 | full_len = strlen(version) + strlen(version + strlen(version) + 1) + 2; | ||
| 470 | |||
| 471 | /* Move string to start with version number: prefix will be | ||
| 472 | * $Revision$ or $Revision: */ | ||
| 473 | len = strlen("$Revision"); | ||
| 474 | if (version[len] == ':' || version[len] == '$') | ||
| 475 | len++; | ||
| 476 | while (isspace(version[len])) | ||
| 477 | len++; | ||
| 478 | memmove(version, version+len, full_len-len); | ||
| 479 | full_len -= len; | ||
| 480 | |||
| 481 | /* Preserve up to next whitespace. */ | ||
| 482 | len = 0; | ||
| 483 | while (version[len] && !isspace(version[len])) | ||
| 484 | len++; | ||
| 485 | memmove(version + len, version + strlen(version), | ||
| 486 | full_len - strlen(version)); | ||
| 487 | return 1; | ||
| 488 | } | ||
| 489 | |||
| 490 | /* Clean up RCS-style version numbers. */ | ||
| 491 | void maybe_frob_rcs_version(const char *modfilename, | ||
| 492 | char *version, | ||
| 493 | void *modinfo, | ||
| 494 | unsigned long version_offset) | ||
| 495 | { | ||
| 496 | if (strip_rcs_crap(version)) | ||
| 497 | write_version(modfilename, version, version_offset); | ||
| 498 | } | ||
