]> git.hungrycats.org Git - linux/commitdiff
[PATCH] Add a MODULE_VERSION macro
authorAndrew Morton <akpm@osdl.org>
Thu, 26 Feb 2004 14:51:58 +0000 (06:51 -0800)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Thu, 26 Feb 2004 14:51:58 +0000 (06:51 -0800)
From: Rusty Russell <rusty@au1.ibm.com>

The way it works is that the .mod file contains the name of the module (as
before), but succeeding lines are the constituent parts (assumed to be .c
files, which usually works: if they use MODULE_VERSION in a file for which
this isn't true we'll get a warning).

As we postprocess modules, we look in the .modinfo section for a
"version=", which is placed by the MODULE_VERSION() macro.  This will be of
form "version=<macroarg>" "\0" [24 chars] "\0".  The 24 chars are replaced
by the md4 sum of the .c files and any files they #include using '#include
"file"' which are found in the current directory.  Whitespace is collapsed
outside strings, and comments are ignored for purposes of the sum.

The result is a .modinfo entry such as

version=1.16ac-rustytest B13E9451C4CA3B89577DEFF

At the kernel summit, various people asked for a MODULE_VERSION macro to
store module strings (for later access through sysfs).  A simple md4 is
needed to identify changes in modules which, inevitably, do not update the
version.  It skips whitespace and comments, and includes #includes which
are in the same dir.

The module versions should be set according to this definition, based on
the RPM one, or CVS Revision tags.  Violators will be shot.

 [<epoch>`:']<version>[`-'<extraversion>]
 <epoch>: A (small) unsigned integer which allows you to start versions
          anew. If not mentioned, it's zero.  eg. "2:1.0" is after
     "1:2.0".
 <version>: The <version> may contain only alphanumerics.
 <extraversion>: Like <version>, but inserted for local
          customizations, eg "rh3" or "rusty1".

Comparison of two versions (assuming same epoch):

Split each into all-digit and all-alphabetical parts.  Compare each one one
at a time: digit parts numerically, alphabetical in ASCII order.  So 0.10
comes after 0.9.

include/linux/module.h
scripts/Makefile
scripts/Makefile.build
scripts/Makefile.modinst
scripts/Makefile.modpost
scripts/modpost.c
scripts/modpost.h
scripts/sumversion.c [new file with mode: 0644]

index e01784890022ce4bfbbba17e316fad068446be30..4c9b53d5d51c1a125f0fc095b3cf4c32db923703 100644 (file)
@@ -127,6 +127,24 @@ extern const struct gtype##_id __mod_##gtype##_table               \
 #define MODULE_DEVICE_TABLE(type,name)         \
   MODULE_GENERIC_TABLE(type##_device,name)
 
+/* Version of form [<epoch>:]<version>[-<extra-version>].
+   Or for CVS/RCS ID version, everything but the number is stripped.
+  <epoch>: A (small) unsigned integer which allows you to start versions
+           anew. If not mentioned, it's zero.  eg. "2:1.0" is after
+          "1:2.0".
+  <version>: The <version> may contain only alphanumerics and the
+           character `.'.  Ordered by numeric sort for numeric parts,
+          ascii sort for ascii parts (as per RPM or DEB algorithm).
+  <extraversion>: Like <version>, but inserted for local
+           customizations, eg "rh3" or "rusty1".
+
+  Using this automatically adds a checksum of the .c files and the
+  local headers to the end.  Use MODULE_VERSION("") if you want just
+  this.  Macro includes room for this.
+*/
+#define MODULE_VERSION(_version) \
+  MODULE_INFO(version, _version "\0xxxxxxxxxxxxxxxxxxxxxxxx")
+
 /* Given an address, look for it in the exception tables */
 const struct exception_table_entry *search_exception_tables(unsigned long add);
 
index c03384ce37ae01640d3e4c0245f8bd158db60895..044afd55a77d0d930525cc9135627f8e6ea57346 100644 (file)
@@ -12,7 +12,7 @@ host-progs    := fixdep split-include conmakehash docproc kallsyms modpost \
                   mk_elfconfig pnmtologo bin2c
 always         := $(host-progs) empty.o
 
-modpost-objs   := modpost.o file2alias.o
+modpost-objs   := modpost.o file2alias.o sumversion.o
 
 subdir-$(CONFIG_MODVERSIONS)   += genksyms
 
@@ -24,7 +24,7 @@ $(addprefix $(obj)/,$(filter-out fixdep,$(always)) $(subdir-y)): $(obj)/fixdep
 
 # dependencies on generated files need to be listed explicitly
 
-$(obj)/modpost.o $(obj)/file2alias.o: $(obj)/elfconfig.h
+$(obj)/modpost.o $(obj)/file2alias.o $(obj)/sumversion.o: $(obj)/elfconfig.h
 
 quiet_cmd_elfconfig = MKELF   $@
       cmd_elfconfig = $(obj)/mk_elfconfig $(ARCH) < $< > $@
index 5b45fb382f642871ce3d6d35426b4c121b1c9a62..5451610fff0d7eccd45ee6a96bcd8dc247241210 100644 (file)
@@ -64,8 +64,6 @@ endif
 
 # We keep a list of all modules in $(MODVERDIR)
 
-touch-module = @echo $(@:.o=.ko) > $(MODVERDIR)/$(@F:.o=.mod)
-
 __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
         $(if $(KBUILD_MODULES),$(obj-m)) \
         $(subdir-ym) $(always)
@@ -178,7 +176,7 @@ endef
 
 $(single-used-m): %.o: %.c FORCE
        $(call if_changed_rule,cc_o_c)
-       $(touch-module)
+       @{ echo $(@:.o=.ko); echo $@; } > $(MODVERDIR)/$(@F:.o=.mod)
 
 quiet_cmd_cc_lst_c = MKLST   $@
       cmd_cc_lst_c = $(CC) $(c_flags) -g -c -o $*.o $< && \
@@ -273,7 +271,7 @@ $(multi-used-y) : %.o: $(multi-objs-y) FORCE
 
 $(multi-used-m) : %.o: $(multi-objs-m) FORCE
        $(call if_changed,link_multi-m)
-       $(touch-module)
+       @{ echo $(@:.o=.ko); echo $(link_multi_deps); } > $(MODVERDIR)/$(@F:.o=.mod)
 
 targets += $(multi-used-y) $(multi-used-m)
 
index 6fd619221b935e680b438e4ed5a8a9187868b049..8f974ef5cbb36eec0d0a45af843029c7a611b456 100644 (file)
@@ -9,7 +9,7 @@ include scripts/Makefile.lib
 
 #
 
-__modules := $(shell cat /dev/null $(wildcard $(MODVERDIR)/*.mod))
+__modules := $(shell head -q -n1 /dev/null $(wildcard $(MODVERDIR)/*.mod))
 modules := $(patsubst %.o,%.ko,$(wildcard $(__modules:.ko=.o)))
 
 ifneq ($(filter-out $(modules),$(__modules)),)
index f1867d3a7118788920d66bf320e38e5946db0a53..66288a64f0420606ab4ae841edf424a237632691 100644 (file)
@@ -10,10 +10,11 @@ include scripts/Makefile.lib
 
 #
 
-__modules := $(shell cat /dev/null $(wildcard $(MODVERDIR)/*.mod))
+__modules := $(shell head -q -n1 /dev/null $(wildcard $(MODVERDIR)/*.mod))
 modules := $(patsubst %.o,%.ko,$(wildcard $(__modules:.ko=.o)))
 
 ifneq ($(filter-out $(modules),$(__modules)),)
+  $(warning Trouble: $(__modules) )
   $(warning *** Uh-oh, you have stale module entries. You messed with SUBDIRS,)
   $(warning     do not complain if something goes wrong.)
 endif
index 1707171810e5ed5009d361b6f955c5ed2f80f03f..3ee9b4d507fa03df923abaf4d66e67909d2b520f 100644 (file)
@@ -65,15 +65,15 @@ new_module(char *modname)
        struct module *mod;
        char *p;
        
+       mod = NOFAIL(malloc(sizeof(*mod)));
+       memset(mod, 0, sizeof(*mod));
+       mod->name = NOFAIL(strdup(modname));
+
        /* strip trailing .o */
-       p = strstr(modname, ".o");
+       p = strstr(mod->name, ".o");
        if (p)
                *p = 0;
 
-       mod = NOFAIL(malloc(sizeof(*mod)));
-       memset(mod, 0, sizeof(*mod));
-       mod->name = modname;
-
        /* add to list */
        mod->next = modules;
        modules = mod;
@@ -194,25 +194,24 @@ grab_file(const char *filename, unsigned long *size)
        int fd;
 
        fd = open(filename, O_RDONLY);
-       if (fd < 0) {
-               perror(filename);
-               abort();
-       }
-       if (fstat(fd, &st) != 0) {
-               perror(filename);
-               abort();
-       }
+       if (fstat(fd, &st) != 0)
+               return NULL;
 
        *size = st.st_size;
        map = mmap(NULL, *size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
-       if (map == MAP_FAILED) {
-               perror(filename);
-               abort();
-       }
        close(fd);
+
+       if (map == MAP_FAILED)
+               return NULL;
        return map;
 }
 
+void
+release_file(void *file, unsigned long size)
+{
+       munmap(file, size);
+}
+
 void
 parse_elf(struct elf_info *info, const char *filename)
 {
@@ -222,6 +221,10 @@ parse_elf(struct elf_info *info, const char *filename)
        Elf_Sym  *sym;
 
        hdr = grab_file(filename, &info->size);
+       if (!hdr) {
+               perror(filename);
+               abort();
+       }
        info->hdr = hdr;
        if (info->size < sizeof(*hdr))
                goto truncated;
@@ -239,11 +242,19 @@ parse_elf(struct elf_info *info, const char *filename)
                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);
+               sechdrs[i].sh_name   = TO_NATIVE(sechdrs[i].sh_name);
        }
        /* Find symbol table. */
        for (i = 1; i < hdr->e_shnum; i++) {
+               const char *secstrings
+                       = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
+
                if (sechdrs[i].sh_offset > info->size)
                        goto truncated;
+               if (strcmp(secstrings+sechdrs[i].sh_name, ".modinfo") == 0) {
+                       info->modinfo = (void *)hdr + sechdrs[i].sh_offset;
+                       info->modinfo_len = sechdrs[i].sh_size;
+               }
                if (sechdrs[i].sh_type != SHT_SYMTAB)
                        continue;
 
@@ -274,7 +285,7 @@ parse_elf(struct elf_info *info, const char *filename)
 void
 parse_elf_finish(struct elf_info *info)
 {
-       munmap(info->hdr, info->size);
+       release_file(info->hdr, info->size);
 }
 
 #define CRC_PFX     MODULE_SYMBOL_PREFIX "__crc_"
@@ -372,6 +383,8 @@ read_symbols(char *modname)
                handle_modversions(mod, &info, sym, symname);
                handle_moddevtable(mod, &info, sym, symname);
        }
+       maybe_frob_version(modname, info.modinfo, info.modinfo_len,
+                          (void *)info.modinfo - (void *)info.hdr);
        parse_elf_finish(&info);
 
        /* Our trick to get versioning for struct_module - it's
index 58610817978099d4a6d3eb5c701804eb40510b24..13eafa4e43ccf9da741307b78f29ac1c24819ff6 100644 (file)
@@ -80,9 +80,19 @@ struct elf_info {
        Elf_Sym      *symtab_start;
        Elf_Sym      *symtab_stop;
        const char   *strtab;
+       char         *modinfo;
+       unsigned int modinfo_len;
 };
 
 void handle_moddevtable(struct module *mod, struct elf_info *info,
                        Elf_Sym *sym, const char *symname);
 
 void add_moddevtable(struct buffer *buf, struct module *mod);
+
+void maybe_frob_version(const char *modfilename,
+                       void *modinfo,
+                       unsigned long modinfo_len,
+                       unsigned long modinfo_offset);
+
+void *grab_file(const char *filename, unsigned long *size);
+void release_file(void *file, unsigned long size);
diff --git a/scripts/sumversion.c b/scripts/sumversion.c
new file mode 100644 (file)
index 0000000..fb022cc
--- /dev/null
@@ -0,0 +1,550 @@
+#include <netinet/in.h>
+#include <stdint.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+#include "modpost.h"
+
+/* Parse tag=value strings from .modinfo section */
+static char *next_string(char *string, unsigned long *secsize)
+{
+       /* Skip non-zero chars */
+       while (string[0]) {
+               string++;
+               if ((*secsize)-- <= 1)
+                       return NULL;
+       }
+
+       /* Skip any zero padding. */
+       while (!string[0]) {
+               string++;
+               if ((*secsize)-- <= 1)
+                       return NULL;
+       }
+       return string;
+}
+
+static char *get_modinfo(void *modinfo, unsigned long modinfo_len,
+                        const char *tag)
+{
+       char *p;
+       unsigned int taglen = strlen(tag);
+       unsigned long size = modinfo_len;
+
+       for (p = modinfo; p; p = next_string(p, &size)) {
+               if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=')
+                       return p + taglen + 1;
+       }
+       return NULL;
+}
+
+/*
+ * Stolen form Cryptographic API.
+ *
+ * MD4 Message Digest Algorithm (RFC1320).
+ *
+ * Implementation derived from Andrew Tridgell and Steve French's
+ * CIFS MD4 implementation, and the cryptoapi implementation
+ * originally based on the public domain implementation written
+ * by Colin Plumb in 1993.
+ *
+ * Copyright (c) Andrew Tridgell 1997-1998.
+ * Modified by Steve French (sfrench@us.ibm.com) 2002
+ * Copyright (c) Cryptoapi developers.
+ * Copyright (c) 2002 David S. Miller (davem@redhat.com)
+ * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
+ *
+ * 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.
+ *
+ */
+#define MD4_DIGEST_SIZE                16
+#define MD4_HMAC_BLOCK_SIZE    64
+#define MD4_BLOCK_WORDS                16
+#define MD4_HASH_WORDS         4
+
+struct md4_ctx {
+       uint32_t hash[MD4_HASH_WORDS];
+       uint32_t block[MD4_BLOCK_WORDS];
+       uint64_t byte_count;
+};
+
+static inline uint32_t lshift(uint32_t x, unsigned int s)
+{
+       x &= 0xFFFFFFFF;
+       return ((x << s) & 0xFFFFFFFF) | (x >> (32 - s));
+}
+
+static inline uint32_t F(uint32_t x, uint32_t y, uint32_t z)
+{
+       return (x & y) | ((~x) & z);
+}
+
+static inline uint32_t G(uint32_t x, uint32_t y, uint32_t z)
+{
+       return (x & y) | (x & z) | (y & z);
+}
+
+static inline uint32_t H(uint32_t x, uint32_t y, uint32_t z)
+{
+       return x ^ y ^ z;
+}
+
+#define ROUND1(a,b,c,d,k,s) (a = lshift(a + F(b,c,d) + k, s))
+#define ROUND2(a,b,c,d,k,s) (a = lshift(a + G(b,c,d) + k + (uint32_t)0x5A827999,s))
+#define ROUND3(a,b,c,d,k,s) (a = lshift(a + H(b,c,d) + k + (uint32_t)0x6ED9EBA1,s))
+
+/* XXX: this stuff can be optimized */
+static inline void le32_to_cpu_array(uint32_t *buf, unsigned int words)
+{
+       while (words--) {
+               *buf = ntohl(*buf);
+               buf++;
+       }
+}
+
+static inline void cpu_to_le32_array(uint32_t *buf, unsigned int words)
+{
+       while (words--) {
+               *buf = htonl(*buf);
+               buf++;
+       }
+}
+
+static void md4_transform(uint32_t *hash, uint32_t const *in)
+{
+       uint32_t a, b, c, d;
+
+       a = hash[0];
+       b = hash[1];
+       c = hash[2];
+       d = hash[3];
+
+       ROUND1(a, b, c, d, in[0], 3);
+       ROUND1(d, a, b, c, in[1], 7);
+       ROUND1(c, d, a, b, in[2], 11);
+       ROUND1(b, c, d, a, in[3], 19);
+       ROUND1(a, b, c, d, in[4], 3);
+       ROUND1(d, a, b, c, in[5], 7);
+       ROUND1(c, d, a, b, in[6], 11);
+       ROUND1(b, c, d, a, in[7], 19);
+       ROUND1(a, b, c, d, in[8], 3);
+       ROUND1(d, a, b, c, in[9], 7);
+       ROUND1(c, d, a, b, in[10], 11);
+       ROUND1(b, c, d, a, in[11], 19);
+       ROUND1(a, b, c, d, in[12], 3);
+       ROUND1(d, a, b, c, in[13], 7);
+       ROUND1(c, d, a, b, in[14], 11);
+       ROUND1(b, c, d, a, in[15], 19);
+
+       ROUND2(a, b, c, d,in[ 0], 3);
+       ROUND2(d, a, b, c, in[4], 5);
+       ROUND2(c, d, a, b, in[8], 9);
+       ROUND2(b, c, d, a, in[12], 13);
+       ROUND2(a, b, c, d, in[1], 3);
+       ROUND2(d, a, b, c, in[5], 5);
+       ROUND2(c, d, a, b, in[9], 9);
+       ROUND2(b, c, d, a, in[13], 13);
+       ROUND2(a, b, c, d, in[2], 3);
+       ROUND2(d, a, b, c, in[6], 5);
+       ROUND2(c, d, a, b, in[10], 9);
+       ROUND2(b, c, d, a, in[14], 13);
+       ROUND2(a, b, c, d, in[3], 3);
+       ROUND2(d, a, b, c, in[7], 5);
+       ROUND2(c, d, a, b, in[11], 9);
+       ROUND2(b, c, d, a, in[15], 13);
+
+       ROUND3(a, b, c, d,in[ 0], 3);
+       ROUND3(d, a, b, c, in[8], 9);
+       ROUND3(c, d, a, b, in[4], 11);
+       ROUND3(b, c, d, a, in[12], 15);
+       ROUND3(a, b, c, d, in[2], 3);
+       ROUND3(d, a, b, c, in[10], 9);
+       ROUND3(c, d, a, b, in[6], 11);
+       ROUND3(b, c, d, a, in[14], 15);
+       ROUND3(a, b, c, d, in[1], 3);
+       ROUND3(d, a, b, c, in[9], 9);
+       ROUND3(c, d, a, b, in[5], 11);
+       ROUND3(b, c, d, a, in[13], 15);
+       ROUND3(a, b, c, d, in[3], 3);
+       ROUND3(d, a, b, c, in[11], 9);
+       ROUND3(c, d, a, b, in[7], 11);
+       ROUND3(b, c, d, a, in[15], 15);
+
+       hash[0] += a;
+       hash[1] += b;
+       hash[2] += c;
+       hash[3] += d;
+}
+
+static inline void md4_transform_helper(struct md4_ctx *ctx)
+{
+       le32_to_cpu_array(ctx->block, sizeof(ctx->block) / sizeof(uint32_t));
+       md4_transform(ctx->hash, ctx->block);
+}
+
+static void md4_init(struct md4_ctx *mctx)
+{
+       mctx->hash[0] = 0x67452301;
+       mctx->hash[1] = 0xefcdab89;
+       mctx->hash[2] = 0x98badcfe;
+       mctx->hash[3] = 0x10325476;
+       mctx->byte_count = 0;
+}
+
+static void md4_update(struct md4_ctx *mctx,
+                      const unsigned char *data, unsigned int len)
+{
+       const uint32_t avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f);
+
+       mctx->byte_count += len;
+
+       if (avail > len) {
+               memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
+                      data, len);
+               return;
+       }
+
+       memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
+              data, avail);
+
+       md4_transform_helper(mctx);
+       data += avail;
+       len -= avail;
+
+       while (len >= sizeof(mctx->block)) {
+               memcpy(mctx->block, data, sizeof(mctx->block));
+               md4_transform_helper(mctx);
+               data += sizeof(mctx->block);
+               len -= sizeof(mctx->block);
+       }
+
+       memcpy(mctx->block, data, len);
+}
+
+static void md4_final_ascii(struct md4_ctx *mctx, char *out, unsigned int len)
+{
+       const unsigned int offset = mctx->byte_count & 0x3f;
+       char *p = (char *)mctx->block + offset;
+       int padding = 56 - (offset + 1);
+
+       *p++ = 0x80;
+       if (padding < 0) {
+               memset(p, 0x00, padding + sizeof (uint64_t));
+               md4_transform_helper(mctx);
+               p = (char *)mctx->block;
+               padding = 56;
+       }
+
+       memset(p, 0, padding);
+       mctx->block[14] = mctx->byte_count << 3;
+       mctx->block[15] = mctx->byte_count >> 29;
+       le32_to_cpu_array(mctx->block, (sizeof(mctx->block) -
+                         sizeof(uint64_t)) / sizeof(uint32_t));
+       md4_transform(mctx->hash, mctx->block);
+       cpu_to_le32_array(mctx->hash, sizeof(mctx->hash) / sizeof(uint32_t));
+
+       snprintf(out, len, "%08X%08X%08X%08X",
+                mctx->hash[0], mctx->hash[1], mctx->hash[2], mctx->hash[3]);
+}
+
+static inline void add_char(unsigned char c, struct md4_ctx *md)
+{
+       md4_update(md, &c, 1);
+}
+
+static int parse_string(const char *file, unsigned long len,
+                       struct md4_ctx *md)
+{
+       unsigned long i;
+
+       add_char(file[0], md);
+       for (i = 1; i < len; i++) {
+               add_char(file[i], md);
+               if (file[i] == '"' && file[i-1] != '\\')
+                       break;
+       }
+       return i;
+}
+
+static int parse_comment(const char *file, unsigned long len)
+{
+       unsigned long i;
+
+       for (i = 2; i < len; i++) {
+               if (file[i-1] == '*' && file[i] == '/')
+                       break;
+       }
+       return i;
+}
+
+/* FIXME: Handle .s files differently (eg. # starts comments) --RR */
+static int parse_file(const char *fname, struct md4_ctx *md)
+{
+       char *file;
+       unsigned long i, len;
+
+       file = grab_file(fname, &len);
+       if (!file)
+               return 0;
+
+       for (i = 0; i < len; i++) {
+               /* Collapse and ignore \ and CR. */
+               if (file[i] == '\\' && (i+1 < len) && file[i+1] == '\n') {
+                       i++;
+                       continue;
+               }
+
+               /* Ignore whitespace */
+               if (isspace(file[i]))
+                       continue;
+
+               /* Handle strings as whole units */
+               if (file[i] == '"') {
+                       i += parse_string(file+i, len - i, md);
+                       continue;
+               }
+
+               /* Comments: ignore */
+               if (file[i] == '/' && file[i+1] == '*') {
+                       i += parse_comment(file+i, len - i);
+                       continue;
+               }
+
+               add_char(file[i], md);
+       }
+       release_file(file, len);
+       return 1;
+}
+
+/* We have dir/file.o.  Open dir/.file.o.cmd, look for deps_ line to
+ * figure out source file. */
+static int parse_source_files(const char *objfile, struct md4_ctx *md)
+{
+       char *cmd, *file, *p, *end;
+       const char *base;
+       unsigned long flen;
+       int dirlen, ret = 0;
+
+       cmd = malloc(strlen(objfile) + sizeof("..cmd"));
+
+       base = strrchr(objfile, '/');
+       if (base) {
+               base++;
+               dirlen = base - objfile;
+               sprintf(cmd, "%.*s.%s.cmd", dirlen, objfile, base);
+       } else {
+               dirlen = 0;
+               sprintf(cmd, ".%s.cmd", objfile);
+       }
+
+       file = grab_file(cmd, &flen);
+       if (!file) {
+               fprintf(stderr, "Warning: could not find %s for %s\n",
+                       cmd, objfile);
+               goto out;
+       }
+
+       /* There will be a line like so:
+               deps_drivers/net/dummy.o := \
+                 drivers/net/dummy.c \
+                   $(wildcard include/config/net/fastroute.h) \
+                 include/linux/config.h \
+                   $(wildcard include/config/h.h) \
+                 include/linux/module.h \
+
+          Sum all files in the same dir or subdirs.
+       */
+       /* Strictly illegal: file is not nul terminated. */
+       p = strstr(file, "\ndeps_");
+       if (!p) {
+               fprintf(stderr, "Warning: could not find deps_ line in %s\n",
+                       cmd);
+               goto out_file;
+       }
+       p = strstr(p, ":=");
+       if (!p) {
+               fprintf(stderr, "Warning: could not find := line in %s\n",
+                       cmd);
+               goto out_file;
+       }
+       p += strlen(":=");
+       p += strspn(p, " \\\n");
+
+       end = strstr(p, "\n\n");
+       if (!end) {
+               fprintf(stderr, "Warning: could not find end line in %s\n",
+                       cmd);
+               goto out_file;
+       }
+
+       while (p < end) {
+               unsigned int len;
+
+               len = strcspn(p, " \\\n");
+               if (memcmp(objfile, p, dirlen) == 0) {
+                       char source[len + 1];
+
+                       memcpy(source, p, len);
+                       source[len] = '\0';
+                       printf("parsing %s\n", source);
+                       if (!parse_file(source, md)) {
+                               fprintf(stderr,
+                                       "Warning: could not open %s: %s\n",
+                                       source, strerror(errno));
+                               goto out_file;
+                       }
+               }
+               p += len;
+               p += strspn(p, " \\\n");
+       }
+
+       /* Everyone parsed OK */
+       ret = 1;
+out_file:
+       release_file(file, flen);
+out:
+       free(cmd);
+       return ret;
+}
+
+static int get_version(const char *modname, char sum[])
+{
+       void *file;
+       unsigned long len;
+       int ret = 0;
+       struct md4_ctx md;
+       char *sources, *end, *fname;
+       const char *basename;
+       char filelist[sizeof(".tmp_versions/%s.mod") + strlen(modname)];
+
+       /* Source files for module are in .tmp_versions/modname.mod,
+          after the first line. */
+       if (strrchr(modname, '/'))
+               basename = strrchr(modname, '/') + 1;
+       else
+               basename = modname;
+       sprintf(filelist, ".tmp_versions/%s", basename);
+       /* Truncate .o, add .mod */
+       strcpy(filelist + strlen(filelist)-2, ".mod");
+
+       file = grab_file(filelist, &len);
+       if (!file) {
+               fprintf(stderr, "Warning: could not find versions for %s\n",
+                       filelist);
+               return 0;
+       }
+
+       sources = strchr(file, '\n');
+       if (!sources) {
+               fprintf(stderr, "Warning: malformed versions file for %s\n",
+                       modname);
+               goto release;
+       }
+
+       sources++;
+       end = strchr(sources, '\n');
+       if (!end) {
+               fprintf(stderr, "Warning: bad ending versions file for %s\n",
+                       modname);
+               goto release;
+       }
+       *end = '\0';
+
+       md4_init(&md);
+       for (fname = strtok(sources, " "); fname; fname = strtok(NULL, " ")) {
+               if (!parse_source_files(fname, &md))
+                       goto release;
+       }
+
+       /* sum is of form \0<padding>. */
+       md4_final_ascii(&md, sum, 1 + strlen(sum+1));
+       ret = 1;
+release:
+       release_file(file, len);
+       return ret;
+}
+
+static void write_version(const char *filename, const char *sum,
+                         unsigned long offset)
+{
+       int fd;
+
+       fd = open(filename, O_RDWR);
+       if (fd < 0) {
+               fprintf(stderr, "Warning: changing sum in %s failed: %s\n",
+                       filename, strerror(errno));
+               return;
+       }
+
+       if (lseek(fd, offset, SEEK_SET) == (off_t)-1) {
+               fprintf(stderr, "Warning: changing sum in %s:%lu failed: %s\n",
+                       filename, offset, strerror(errno));
+               goto out;
+       }
+
+       if (write(fd, sum, strlen(sum)+1) != strlen(sum)+1) {
+               fprintf(stderr, "Warning: writing sum in %s failed: %s\n",
+                       filename, strerror(errno));
+               goto out;
+       }
+out:
+       close(fd);
+}
+
+void strip_rcs_crap(char *version)
+{
+       unsigned int len, full_len;
+
+       if (strncmp(version, "$Revision", strlen("$Revision")) != 0)
+               return;
+
+       /* Space for version string follows. */
+       full_len = strlen(version) + strlen(version + strlen(version) + 1) + 2;
+
+       /* Move string to start with version number: prefix will be
+        * $Revision$ or $Revision: */
+       len = strlen("$Revision");
+       if (version[len] == ':' || version[len] == '$')
+               len++;
+       while (isspace(version[len]))
+               len++;
+       memmove(version, version+len, full_len-len);
+       full_len -= len;
+
+       /* Preserve up to next whitespace. */
+       len = 0;
+       while (version[len] && !isspace(version[len]))
+               len++;
+       memmove(version + len, version + strlen(version),
+               full_len - strlen(version));
+}
+
+/* If the modinfo contains a "version" value, then set this. */
+void maybe_frob_version(const char *modfilename,
+                       void *modinfo,
+                       unsigned long modinfo_len,
+                       unsigned long modinfo_offset)
+{
+       char *version, *csum;
+
+       version = get_modinfo(modinfo, modinfo_len, "version");
+       if (!version)
+               return;
+
+       /* RCS $Revision gets stripped out. */
+       strip_rcs_crap(version);
+
+       /* Check against double sumversion */
+       if (strchr(version, ' '))
+               return;
+
+       /* Version contains embedded NUL: second half has space for checksum */
+       csum = version + strlen(version);
+       *(csum++) = ' ';
+       if (get_version(modfilename, csum))
+               write_version(modfilename, version,
+                             modinfo_offset + (version - (char *)modinfo));
+}