add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

4
kernel/scripts/mod/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
elfconfig.h
mk_elfconfig
modpost
ksymhash

View File

@@ -0,0 +1,25 @@
hostprogs-y := modpost mk_elfconfig
hostprogs-$(CONFIG_LKM_ELF_HASH) += ksymhash
always := $(hostprogs-y) empty.o
modpost-objs := modpost.o file2alias.o sumversion.o elflib.o
ksymhash-objs := ksymhash.o elflib.o
ifdef CONFIG_LKM_ELF_HASH
modpost-objs += ktablehash.o
HOSTCFLAGS_ktablehash.o += -DCONFIG_LKM_ELF_HASH
HOSTCFLAGS_modpost.o += -DCONFIG_LKM_ELF_HASH
endif
# dependencies on generated files need to be listed explicitly
$(obj)/modpost.o $(obj)/file2alias.o $(obj)/sumversion.o: $(obj)/elfconfig.h
$(obj)/ksymhash.o $(obj)/ktablehash.o $(obj)/elflib.o: $(obj)/elfconfig.h
quiet_cmd_elfconfig = MKELF $@
cmd_elfconfig = $(obj)/mk_elfconfig $(ARCH) < $< > $@
$(obj)/elfconfig.h: $(obj)/empty.o $(obj)/mk_elfconfig FORCE
$(call if_changed,elfconfig)
targets += elfconfig.h

267
kernel/scripts/mod/elflib.c Normal file
View File

@@ -0,0 +1,267 @@
#include "elflib.h"
#define PRINTF __attribute__ ((format (printf, 1, 2)))
PRINTF void fatal(const char *fmt, ...)
{
va_list arglist;
fprintf(stderr, "FATAL: ");
va_start(arglist, fmt);
vfprintf(stderr, fmt, arglist);
va_end(arglist);
exit(1);
}
PRINTF void warn(const char *fmt, ...)
{
va_list arglist;
fprintf(stderr, "WARNING: ");
va_start(arglist, fmt);
vfprintf(stderr, fmt, arglist);
va_end(arglist);
}
PRINTF void merror(const char *fmt, ...)
{
va_list arglist;
fprintf(stderr, "ERROR: ");
va_start(arglist, fmt);
vfprintf(stderr, fmt, arglist);
va_end(arglist);
}
static void *grab_file_rw(const char *filename, unsigned long *size,
unsigned char write)
{
struct stat st;
void *map;
int fd, open_flag, map_flag;
if (write) {
open_flag = O_RDWR;
map_flag = MAP_SHARED;
} else {
open_flag = O_RDONLY;
map_flag = MAP_PRIVATE;
}
fd = open(filename, open_flag);
if (fd < 0 || fstat(fd, &st) != 0)
return NULL;
*size = st.st_size;
map = mmap(NULL, *size, PROT_READ|PROT_WRITE, map_flag, fd, 0);
close(fd);
if (map == MAP_FAILED)
return NULL;
return map;
}
static void set_ksymtable(struct elf_info *info, enum ksymtab_type type,
Elf_Ehdr *hdr, Elf_Shdr *sechdrs, unsigned int secidx,
const char *secname)
{
info->ksym_tables[type].start = (struct kernel_symbol *) \
((void *) hdr + sechdrs[secidx].sh_offset);
info->ksym_tables[type].stop = (struct kernel_symbol *) \
((void *) hdr + sechdrs[secidx].sh_offset + sechdrs[secidx].sh_size);
info->ksym_tables[type].name = strdup(secname);
info->ksym_tables[type].sec = secidx;
}
static int parse_elf_rw(struct elf_info *info, const char *filename,
unsigned char write)
{
unsigned int i;
Elf_Ehdr *hdr;
Elf_Shdr *sechdrs;
Elf_Sym *sym;
char *lkm_suffix;
hdr = grab_file_rw(filename, &info->size, write);
if (!hdr) {
perror(filename);
exit(1);
}
info->hdr = hdr;
if (info->size < sizeof(*hdr)) {
/* file too small, assume this is an empty .o file */
return 0;
}
/* Is this a valid ELF file? */
if ((hdr->e_ident[EI_MAG0] != ELFMAG0) ||
(hdr->e_ident[EI_MAG1] != ELFMAG1) ||
(hdr->e_ident[EI_MAG2] != ELFMAG2) ||
(hdr->e_ident[EI_MAG3] != ELFMAG3)) {
/* Not an ELF file - silently ignore it */
return 0;
}
/* Fix endianness in ELF header */
hdr->e_type = TO_NATIVE(hdr->e_type);
hdr->e_machine = TO_NATIVE(hdr->e_machine);
hdr->e_version = TO_NATIVE(hdr->e_version);
hdr->e_entry = TO_NATIVE(hdr->e_entry);
hdr->e_phoff = TO_NATIVE(hdr->e_phoff);
hdr->e_shoff = TO_NATIVE(hdr->e_shoff);
hdr->e_flags = TO_NATIVE(hdr->e_flags);
hdr->e_ehsize = TO_NATIVE(hdr->e_ehsize);
hdr->e_phentsize = TO_NATIVE(hdr->e_phentsize);
hdr->e_phnum = TO_NATIVE(hdr->e_phnum);
hdr->e_shentsize = TO_NATIVE(hdr->e_shentsize);
hdr->e_shnum = TO_NATIVE(hdr->e_shnum);
hdr->e_shstrndx = TO_NATIVE(hdr->e_shstrndx);
sechdrs = (void *)hdr + hdr->e_shoff;
info->sechdrs = sechdrs;
/* Check if file offset is correct */
if (hdr->e_shoff > info->size) {
fatal("section header offset=%lu in file '%s' is bigger than "
"filesize=%lu\n", (unsigned long)hdr->e_shoff,
filename, info->size);
return 0;
}
/* Fix endianness in section headers */
for (i = 0; i < hdr->e_shnum; i++) {
sechdrs[i].sh_name = TO_NATIVE(sechdrs[i].sh_name);
sechdrs[i].sh_type = TO_NATIVE(sechdrs[i].sh_type);
sechdrs[i].sh_flags = TO_NATIVE(sechdrs[i].sh_flags);
sechdrs[i].sh_addr = TO_NATIVE(sechdrs[i].sh_addr);
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_info = TO_NATIVE(sechdrs[i].sh_info);
sechdrs[i].sh_addralign = TO_NATIVE(sechdrs[i].sh_addralign);
sechdrs[i].sh_entsize = TO_NATIVE(sechdrs[i].sh_entsize);
}
/* Find symbol tables and text section. */
for (i = 1; i < hdr->e_shnum; i++) {
const char *secstrings
= (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
const char *secname;
int nobits = sechdrs[i].sh_type == SHT_NOBITS;
if (!nobits && sechdrs[i].sh_offset > info->size) {
fatal("%s is truncated. sechdrs[i].sh_offset=%lu > "
"sizeof(*hrd)=%zu\n", filename,
(unsigned long)sechdrs[i].sh_offset,
sizeof(*hdr));
return 0;
}
secname = secstrings + sechdrs[i].sh_name;
if (strcmp(secname, ".text") == 0)
info->base_addr = sechdrs[i].sh_addr -
sechdrs[i].sh_offset;
else if (strcmp(secname, ".modinfo") == 0) {
if (nobits)
fatal("%s has NOBITS .modinfo\n", filename);
info->modinfo = (void *)hdr + sechdrs[i].sh_offset;
info->modinfo_len = sechdrs[i].sh_size;
} else if (strcmp(secname, "__ksymtab") == 0)
set_ksymtable(info, KSYMTAB, hdr, sechdrs, i, secname);
else if (strcmp(secname, "__ksymtab_unused") == 0)
set_ksymtable(info, KSYMTAB_UNUSED, hdr, sechdrs, i,
secname);
else if (strcmp(secname, "__ksymtab_gpl") == 0)
set_ksymtable(info, KSYMTAB_GPL, hdr, sechdrs, i,
secname);
else if (strcmp(secname, "__ksymtab_unused_gpl") == 0)
set_ksymtable(info, KSYMTAB_UNUSED_GPL, hdr, sechdrs, i,
secname);
else if (strcmp(secname, "__ksymtab_gpl_future") == 0)
set_ksymtable(info, KSYMTAB_GPL_FUTURE, hdr, sechdrs, i,
secname);
else if (strcmp(secname, "__ksymtab_strings") == 0)
info->kstrings = (void *)hdr + sechdrs[i].sh_offset;
else if (strcmp(secname, "__markers_strings") == 0)
info->markers_strings_sec = i;
else if (strcmp(secname, ".undef.hash") == 0) {
info->undef_hash.start = (void *)
hdr + sechdrs[i].sh_offset;
info->undef_hash.stop = (void *)
hdr + sechdrs[i].sh_offset + sechdrs[i].sh_size;
}
if (sechdrs[i].sh_type != SHT_SYMTAB)
continue;
info->symtab_start = (void *)hdr + sechdrs[i].sh_offset;
info->symtab_stop = (void *)hdr + sechdrs[i].sh_offset
+ sechdrs[i].sh_size;
info->strtab = (void *)hdr +
sechdrs[sechdrs[i].sh_link].sh_offset;
}
if (!info->symtab_start)
fatal("%s has no symtab?\n", filename);
/* Check if it is the vmlinux or lkm */
lkm_suffix = strstr(filename, ".ko");
if (lkm_suffix && (strlen(lkm_suffix) == 3))
/* Likely this is a lkm */
/*
* ksym->name is an offset with respect to the start of the
* __ksymtab_strings
*/
info->kstr_offset = (long) info->kstrings;
else
/*
* In this case, ksym->name is the absolute value of the string
* into the __ksymtab_strings
*/
info->kstr_offset = (long)info->hdr - (long)info->base_addr;
/* Fix endianness in symbols */
for (sym = info->symtab_start; sym < info->symtab_stop; sym++) {
sym->st_shndx = TO_NATIVE(sym->st_shndx);
sym->st_name = TO_NATIVE(sym->st_name);
sym->st_value = TO_NATIVE(sym->st_value);
sym->st_size = TO_NATIVE(sym->st_size);
}
return 1;
}
ksym_hash_t gnu_hash(const unsigned char *name)
{
ksym_hash_t h = 5381;
unsigned char c;
for (c = *name; c != '\0'; c = *++name)
h = h * 33 + c;
return h & 0xffffffff;
}
void *grab_file(const char *filename, unsigned long *size)
{
/* Read-only */
return grab_file_rw(filename, size, 0);
}
void release_file(void *file, unsigned long size)
{
munmap(file, size);
}
int parse_elf(struct elf_info *info, const char *filename)
{
/* Read-only */
return parse_elf_rw(info, filename, 0);
}
int parse_writable_elf(struct elf_info *info, const char *filename)
{
return parse_elf_rw(info, filename, 1);
}
void parse_elf_finish(struct elf_info *info)
{
release_file(info->hdr, info->size);
}

180
kernel/scripts/mod/elflib.h Normal file
View File

@@ -0,0 +1,180 @@
#include <sys/stat.h>
#include <elf.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "elfconfig.h"
#if KERNEL_ELFCLASS == ELFCLASS32
#define Elf_Ehdr Elf32_Ehdr
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define Elf_Addr Elf32_Addr
#define Elf_Section Elf32_Section
#define ELF_ST_BIND ELF32_ST_BIND
#define ELF_ST_TYPE ELF32_ST_TYPE
#define Elf_Rel Elf32_Rel
#define Elf_Rela Elf32_Rela
#define ELF_R_SYM ELF32_R_SYM
#define ELF_R_TYPE ELF32_R_TYPE
/* It needs to match sizeof within kernel
* as defined in include/linux/module.h
*/
#define ksym_t uint32_t
#define kstr_t uint32_t
#define ksym_hash_t uint32_t
#else
#define Elf_Ehdr Elf64_Ehdr
#define Elf_Shdr Elf64_Shdr
#define Elf_Sym Elf64_Sym
#define Elf_Addr Elf64_Addr
#define Elf_Section Elf64_Section
#define ELF_ST_BIND ELF64_ST_BIND
#define ELF_ST_TYPE ELF64_ST_TYPE
#define Elf_Rel Elf64_Rel
#define Elf_Rela Elf64_Rela
#define ELF_R_SYM ELF64_R_SYM
#define ELF_R_TYPE ELF64_R_TYPE
/* It needs to match sizeof within kernel
* as defined in include/linux/module.h
*/
#define ksym_t uint64_t
#define kstr_t uint64_t
#define ksym_hash_t uint64_t
#endif
/* The 64-bit MIPS ELF ABI uses an unusual reloc format. */
typedef struct
{
Elf32_Word r_sym; /* Symbol index */
unsigned char r_ssym; /* Special symbol for 2nd relocation */
unsigned char r_type3; /* 3rd relocation type */
unsigned char r_type2; /* 2nd relocation type */
unsigned char r_type1; /* 1st relocation type */
} _Elf64_Mips_R_Info;
typedef union
{
Elf64_Xword r_info_number;
_Elf64_Mips_R_Info r_info_fields;
} _Elf64_Mips_R_Info_union;
#define ELF64_MIPS_R_SYM(i) \
((__extension__ (_Elf64_Mips_R_Info_union)(i)).r_info_fields.r_sym)
#define ELF64_MIPS_R_TYPE(i) \
((__extension__ (_Elf64_Mips_R_Info_union)(i)).r_info_fields.r_type1)
#if KERNEL_ELFDATA != HOST_ELFDATA
static inline void __endian(const void *src, void *dest, unsigned int size)
{
unsigned int i;
for (i = 0; i < size; i++)
((unsigned char *)dest)[i] =
((unsigned char *)src)[size - i - 1];
}
#define TO_NATIVE(x) \
({ \
typeof(x) __x; \
__endian(&(x), &(__x), sizeof(__x)); \
__x; \
})
#else /* endianness matches */
#define TO_NATIVE(x) (x)
#endif
#define GET_KSTRING(__ksym, __offset) (unsigned char *)(__ksym->name + __offset)
/* We have no more than 5 kernel symbol tables
__ksymtab
__ksymtab_gpl
__ksymtab_unused
__ksymtab_unused_gpl
__ksymtab_gpl_future
*/
enum ksymtab_type {
KSYMTAB = 0,
KSYMTAB_GPL,
KSYMTAB_UNUSED,
KSYMTAB_UNUSED_GPL,
KSYMTAB_GPL_FUTURE,
KSYMTAB_ALL,
};
/*
* This maps the ELF hash table
* The entries in the .hash table always have a size of 32 bits.
*/
struct elf_htable {
uint32_t nbucket;
uint32_t nchain;
uint32_t *elf_buckets;
uint32_t *chains;
};
/* as defined in include/linux/module.h */
struct kernel_symbol {
ksym_t value;
kstr_t name;
ksym_hash_t hash_value;
};
struct kernel_symtab {
const char *name;
struct kernel_symbol *start;
struct kernel_symbol *stop;
Elf_Section sec;
};
struct elf_info {
unsigned long size;
Elf_Ehdr *hdr;
Elf_Shdr *sechdrs;
Elf_Sym *symtab_start;
Elf_Sym *symtab_stop;
long kstr_offset;
unsigned long base_addr;
struct {
ksym_hash_t *start;
ksym_hash_t *stop;
} undef_hash;
struct kernel_symtab ksym_tables[KSYMTAB_ALL];
Elf_Section markers_strings_sec;
const char *strtab;
const char *kstrings;
char *modinfo;
unsigned int modinfo_len;
};
/* from elflib.c */
void fatal(const char *fmt, ...);
void warn(const char *fmt, ...);
void merror(const char *fmt, ...);
ksym_hash_t gnu_hash(const unsigned char *name);
void *grab_file(const char *filename, unsigned long *size);
void release_file(void *file, unsigned long size);
int parse_elf(struct elf_info *info, const char *filename);
int parse_writable_elf(struct elf_info *info, const char *filename);
void parse_elf_finish(struct elf_info *info);

View File

@@ -0,0 +1 @@
/* empty file to figure out endianness / word size */

View File

@@ -0,0 +1,886 @@
/* Simple code to turn various tables in an ELF file into alias definitions.
* This deals with kernel datastructures where they should be
* dealt with: in the kernel source.
*
* Copyright 2002-2003 Rusty Russell, IBM Corporation
* 2003 Kai Germaschewski
*
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*/
#include "modpost.h"
/* We use the ELF typedefs for kernel_ulong_t but bite the bullet and
* use either stdint.h or inttypes.h for the rest. */
#if KERNEL_ELFCLASS == ELFCLASS32
typedef Elf32_Addr kernel_ulong_t;
#define BITS_PER_LONG 32
#else
typedef Elf64_Addr kernel_ulong_t;
#define BITS_PER_LONG 64
#endif
#ifdef __sun__
#include <inttypes.h>
#else
#include <stdint.h>
#endif
#include <ctype.h>
typedef uint32_t __u32;
typedef uint16_t __u16;
typedef unsigned char __u8;
/* Big exception to the "don't include kernel headers into userspace, which
* even potentially has different endianness and word sizes, since
* we handle those differences explicitly below */
#include "../../include/linux/mod_devicetable.h"
#define ADD(str, sep, cond, field) \
do { \
strcat(str, sep); \
if (cond) \
sprintf(str + strlen(str), \
sizeof(field) == 1 ? "%02X" : \
sizeof(field) == 2 ? "%04X" : \
sizeof(field) == 4 ? "%08X" : "", \
field); \
else \
sprintf(str + strlen(str), "*"); \
} while(0)
/* Always end in a wildcard, for future extension */
static inline void add_wildcard(char *str)
{
int len = strlen(str);
if (str[len - 1] != '*')
strcat(str + len, "*");
}
unsigned int cross_build = 0;
/**
* Check that sizeof(device_id type) are consistent with size of section
* in .o file. If in-consistent then userspace and kernel does not agree
* on actual size which is a bug.
* Also verify that the final entry in the table is all zeros.
* Ignore both checks if build host differ from target host and size differs.
**/
static void device_id_check(const char *modname, const char *device_id,
unsigned long size, unsigned long id_size,
void *symval)
{
int i;
if (size % id_size || size < id_size) {
if (cross_build != 0)
return;
fatal("%s: sizeof(struct %s_device_id)=%lu is not a modulo "
"of the size of section __mod_%s_device_table=%lu.\n"
"Fix definition of struct %s_device_id "
"in mod_devicetable.h\n",
modname, device_id, id_size, device_id, size, device_id);
}
/* Verify last one is a terminator */
for (i = 0; i < id_size; i++ ) {
if (*(uint8_t*)(symval+size-id_size+i)) {
fprintf(stderr,"%s: struct %s_device_id is %lu bytes. "
"The last of %lu is:\n",
modname, device_id, id_size, size / id_size);
for (i = 0; i < id_size; i++ )
fprintf(stderr,"0x%02x ",
*(uint8_t*)(symval+size-id_size+i) );
fprintf(stderr,"\n");
fatal("%s: struct %s_device_id is not terminated "
"with a NULL entry!\n", modname, device_id);
}
}
}
/* USB is special because the bcdDevice can be matched against a numeric range */
/* Looks like "usb:vNpNdNdcNdscNdpNicNiscNipN" */
static void do_usb_entry(struct usb_device_id *id,
unsigned int bcdDevice_initial, int bcdDevice_initial_digits,
unsigned char range_lo, unsigned char range_hi,
struct module *mod)
{
char alias[500];
strcpy(alias, "usb:");
ADD(alias, "v", id->match_flags&USB_DEVICE_ID_MATCH_VENDOR,
id->idVendor);
ADD(alias, "p", id->match_flags&USB_DEVICE_ID_MATCH_PRODUCT,
id->idProduct);
strcat(alias, "d");
if (bcdDevice_initial_digits)
sprintf(alias + strlen(alias), "%0*X",
bcdDevice_initial_digits, bcdDevice_initial);
if (range_lo == range_hi)
sprintf(alias + strlen(alias), "%u", range_lo);
else if (range_lo > 0 || range_hi < 9)
sprintf(alias + strlen(alias), "[%u-%u]", range_lo, range_hi);
if (bcdDevice_initial_digits < (sizeof(id->bcdDevice_lo) * 2 - 1))
strcat(alias, "*");
ADD(alias, "dc", id->match_flags&USB_DEVICE_ID_MATCH_DEV_CLASS,
id->bDeviceClass);
ADD(alias, "dsc",
id->match_flags&USB_DEVICE_ID_MATCH_DEV_SUBCLASS,
id->bDeviceSubClass);
ADD(alias, "dp",
id->match_flags&USB_DEVICE_ID_MATCH_DEV_PROTOCOL,
id->bDeviceProtocol);
ADD(alias, "ic",
id->match_flags&USB_DEVICE_ID_MATCH_INT_CLASS,
id->bInterfaceClass);
ADD(alias, "isc",
id->match_flags&USB_DEVICE_ID_MATCH_INT_SUBCLASS,
id->bInterfaceSubClass);
ADD(alias, "ip",
id->match_flags&USB_DEVICE_ID_MATCH_INT_PROTOCOL,
id->bInterfaceProtocol);
add_wildcard(alias);
buf_printf(&mod->dev_table_buf,
"MODULE_ALIAS(\"%s\");\n", alias);
}
static void do_usb_entry_multi(struct usb_device_id *id, struct module *mod)
{
unsigned int devlo, devhi;
unsigned char chi, clo;
int ndigits;
id->match_flags = TO_NATIVE(id->match_flags);
id->idVendor = TO_NATIVE(id->idVendor);
id->idProduct = TO_NATIVE(id->idProduct);
devlo = id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO ?
TO_NATIVE(id->bcdDevice_lo) : 0x0U;
devhi = id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI ?
TO_NATIVE(id->bcdDevice_hi) : ~0x0U;
/*
* Some modules (visor) have empty slots as placeholder for
* run-time specification that results in catch-all alias
*/
if (!(id->idVendor | id->idProduct | id->bDeviceClass | id->bInterfaceClass))
return;
/* Convert numeric bcdDevice range into fnmatch-able pattern(s) */
for (ndigits = sizeof(id->bcdDevice_lo) * 2 - 1; devlo <= devhi; ndigits--) {
clo = devlo & 0xf;
chi = devhi & 0xf;
if (chi > 9) /* it's bcd not hex */
chi = 9;
devlo >>= 4;
devhi >>= 4;
if (devlo == devhi || !ndigits) {
do_usb_entry(id, devlo, ndigits, clo, chi, mod);
break;
}
if (clo > 0)
do_usb_entry(id, devlo++, ndigits, clo, 9, mod);
if (chi < 9)
do_usb_entry(id, devhi--, ndigits, 0, chi, mod);
}
}
static void do_usb_table(void *symval, unsigned long size,
struct module *mod)
{
unsigned int i;
const unsigned long id_size = sizeof(struct usb_device_id);
device_id_check(mod->name, "usb", size, id_size, symval);
/* Leave last one: it's the terminator. */
size -= id_size;
for (i = 0; i < size; i += id_size)
do_usb_entry_multi(symval + i, mod);
}
/* Looks like: hid:bNvNpN */
static int do_hid_entry(const char *filename,
struct hid_device_id *id, char *alias)
{
id->bus = TO_NATIVE(id->bus);
id->vendor = TO_NATIVE(id->vendor);
id->product = TO_NATIVE(id->product);
sprintf(alias, "hid:b%04X", id->bus);
ADD(alias, "v", id->vendor != HID_ANY_ID, id->vendor);
ADD(alias, "p", id->product != HID_ANY_ID, id->product);
return 1;
}
/* Looks like: ieee1394:venNmoNspNverN */
static int do_ieee1394_entry(const char *filename,
struct ieee1394_device_id *id, char *alias)
{
id->match_flags = TO_NATIVE(id->match_flags);
id->vendor_id = TO_NATIVE(id->vendor_id);
id->model_id = TO_NATIVE(id->model_id);
id->specifier_id = TO_NATIVE(id->specifier_id);
id->version = TO_NATIVE(id->version);
strcpy(alias, "ieee1394:");
ADD(alias, "ven", id->match_flags & IEEE1394_MATCH_VENDOR_ID,
id->vendor_id);
ADD(alias, "mo", id->match_flags & IEEE1394_MATCH_MODEL_ID,
id->model_id);
ADD(alias, "sp", id->match_flags & IEEE1394_MATCH_SPECIFIER_ID,
id->specifier_id);
ADD(alias, "ver", id->match_flags & IEEE1394_MATCH_VERSION,
id->version);
add_wildcard(alias);
return 1;
}
/* Looks like: pci:vNdNsvNsdNbcNscNiN. */
static int do_pci_entry(const char *filename,
struct pci_device_id *id, char *alias)
{
/* Class field can be divided into these three. */
unsigned char baseclass, subclass, interface,
baseclass_mask, subclass_mask, interface_mask;
id->vendor = TO_NATIVE(id->vendor);
id->device = TO_NATIVE(id->device);
id->subvendor = TO_NATIVE(id->subvendor);
id->subdevice = TO_NATIVE(id->subdevice);
id->class = TO_NATIVE(id->class);
id->class_mask = TO_NATIVE(id->class_mask);
strcpy(alias, "pci:");
ADD(alias, "v", id->vendor != PCI_ANY_ID, id->vendor);
ADD(alias, "d", id->device != PCI_ANY_ID, id->device);
ADD(alias, "sv", id->subvendor != PCI_ANY_ID, id->subvendor);
ADD(alias, "sd", id->subdevice != PCI_ANY_ID, id->subdevice);
baseclass = (id->class) >> 16;
baseclass_mask = (id->class_mask) >> 16;
subclass = (id->class) >> 8;
subclass_mask = (id->class_mask) >> 8;
interface = id->class;
interface_mask = id->class_mask;
if ((baseclass_mask != 0 && baseclass_mask != 0xFF)
|| (subclass_mask != 0 && subclass_mask != 0xFF)
|| (interface_mask != 0 && interface_mask != 0xFF)) {
warn("Can't handle masks in %s:%04X\n",
filename, id->class_mask);
return 0;
}
ADD(alias, "bc", baseclass_mask == 0xFF, baseclass);
ADD(alias, "sc", subclass_mask == 0xFF, subclass);
ADD(alias, "i", interface_mask == 0xFF, interface);
add_wildcard(alias);
return 1;
}
/* looks like: "ccw:tNmNdtNdmN" */
static int do_ccw_entry(const char *filename,
struct ccw_device_id *id, char *alias)
{
id->match_flags = TO_NATIVE(id->match_flags);
id->cu_type = TO_NATIVE(id->cu_type);
id->cu_model = TO_NATIVE(id->cu_model);
id->dev_type = TO_NATIVE(id->dev_type);
id->dev_model = TO_NATIVE(id->dev_model);
strcpy(alias, "ccw:");
ADD(alias, "t", id->match_flags&CCW_DEVICE_ID_MATCH_CU_TYPE,
id->cu_type);
ADD(alias, "m", id->match_flags&CCW_DEVICE_ID_MATCH_CU_MODEL,
id->cu_model);
ADD(alias, "dt", id->match_flags&CCW_DEVICE_ID_MATCH_DEVICE_TYPE,
id->dev_type);
ADD(alias, "dm", id->match_flags&CCW_DEVICE_ID_MATCH_DEVICE_MODEL,
id->dev_model);
add_wildcard(alias);
return 1;
}
/* looks like: "ap:tN" */
static int do_ap_entry(const char *filename,
struct ap_device_id *id, char *alias)
{
sprintf(alias, "ap:t%02X*", id->dev_type);
return 1;
}
/* looks like: "css:tN" */
static int do_css_entry(const char *filename,
struct css_device_id *id, char *alias)
{
sprintf(alias, "css:t%01X", id->type);
return 1;
}
/* Looks like: "serio:tyNprNidNexN" */
static int do_serio_entry(const char *filename,
struct serio_device_id *id, char *alias)
{
id->type = TO_NATIVE(id->type);
id->proto = TO_NATIVE(id->proto);
id->id = TO_NATIVE(id->id);
id->extra = TO_NATIVE(id->extra);
strcpy(alias, "serio:");
ADD(alias, "ty", id->type != SERIO_ANY, id->type);
ADD(alias, "pr", id->proto != SERIO_ANY, id->proto);
ADD(alias, "id", id->id != SERIO_ANY, id->id);
ADD(alias, "ex", id->extra != SERIO_ANY, id->extra);
add_wildcard(alias);
return 1;
}
/* looks like: "acpi:ACPI0003 or acpi:PNP0C0B" or "acpi:LNXVIDEO" */
static int do_acpi_entry(const char *filename,
struct acpi_device_id *id, char *alias)
{
sprintf(alias, "acpi*:%s:*", id->id);
return 1;
}
/* looks like: "pnp:dD" */
static void do_pnp_device_entry(void *symval, unsigned long size,
struct module *mod)
{
const unsigned long id_size = sizeof(struct pnp_device_id);
const unsigned int count = (size / id_size)-1;
const struct pnp_device_id *devs = symval;
unsigned int i;
device_id_check(mod->name, "pnp", size, id_size, symval);
for (i = 0; i < count; i++) {
const char *id = (char *)devs[i].id;
char acpi_id[sizeof(devs[0].id)];
int j;
buf_printf(&mod->dev_table_buf,
"MODULE_ALIAS(\"pnp:d%s*\");\n", id);
/* fix broken pnp bus lowercasing */
for (j = 0; j < sizeof(acpi_id); j++)
acpi_id[j] = toupper(id[j]);
buf_printf(&mod->dev_table_buf,
"MODULE_ALIAS(\"acpi*:%s:*\");\n", acpi_id);
}
}
/* looks like: "pnp:dD" for every device of the card */
static void do_pnp_card_entries(void *symval, unsigned long size,
struct module *mod)
{
const unsigned long id_size = sizeof(struct pnp_card_device_id);
const unsigned int count = (size / id_size)-1;
const struct pnp_card_device_id *cards = symval;
unsigned int i;
device_id_check(mod->name, "pnp", size, id_size, symval);
for (i = 0; i < count; i++) {
unsigned int j;
const struct pnp_card_device_id *card = &cards[i];
for (j = 0; j < PNP_MAX_DEVICES; j++) {
const char *id = (char *)card->devs[j].id;
int i2, j2;
int dup = 0;
if (!id[0])
break;
/* find duplicate, already added value */
for (i2 = 0; i2 < i && !dup; i2++) {
const struct pnp_card_device_id *card2 = &cards[i2];
for (j2 = 0; j2 < PNP_MAX_DEVICES; j2++) {
const char *id2 = (char *)card2->devs[j2].id;
if (!id2[0])
break;
if (!strcmp(id, id2)) {
dup = 1;
break;
}
}
}
/* add an individual alias for every device entry */
if (!dup) {
char acpi_id[sizeof(card->devs[0].id)];
int k;
buf_printf(&mod->dev_table_buf,
"MODULE_ALIAS(\"pnp:d%s*\");\n", id);
/* fix broken pnp bus lowercasing */
for (k = 0; k < sizeof(acpi_id); k++)
acpi_id[k] = toupper(id[k]);
buf_printf(&mod->dev_table_buf,
"MODULE_ALIAS(\"acpi*:%s:*\");\n", acpi_id);
}
}
}
}
/* Looks like: pcmcia:mNcNfNfnNpfnNvaNvbNvcNvdN. */
static int do_pcmcia_entry(const char *filename,
struct pcmcia_device_id *id, char *alias)
{
unsigned int i;
id->match_flags = TO_NATIVE(id->match_flags);
id->manf_id = TO_NATIVE(id->manf_id);
id->card_id = TO_NATIVE(id->card_id);
id->func_id = TO_NATIVE(id->func_id);
id->function = TO_NATIVE(id->function);
id->device_no = TO_NATIVE(id->device_no);
for (i=0; i<4; i++) {
id->prod_id_hash[i] = TO_NATIVE(id->prod_id_hash[i]);
}
strcpy(alias, "pcmcia:");
ADD(alias, "m", id->match_flags & PCMCIA_DEV_ID_MATCH_MANF_ID,
id->manf_id);
ADD(alias, "c", id->match_flags & PCMCIA_DEV_ID_MATCH_CARD_ID,
id->card_id);
ADD(alias, "f", id->match_flags & PCMCIA_DEV_ID_MATCH_FUNC_ID,
id->func_id);
ADD(alias, "fn", id->match_flags & PCMCIA_DEV_ID_MATCH_FUNCTION,
id->function);
ADD(alias, "pfn", id->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO,
id->device_no);
ADD(alias, "pa", id->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID1, id->prod_id_hash[0]);
ADD(alias, "pb", id->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID2, id->prod_id_hash[1]);
ADD(alias, "pc", id->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID3, id->prod_id_hash[2]);
ADD(alias, "pd", id->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID4, id->prod_id_hash[3]);
add_wildcard(alias);
return 1;
}
static int do_of_entry (const char *filename, struct of_device_id *of, char *alias)
{
int len;
char *tmp;
len = sprintf (alias, "of:N%sT%s",
of->name[0] ? of->name : "*",
of->type[0] ? of->type : "*");
if (of->compatible[0])
sprintf (&alias[len], "%sC%s",
of->type[0] ? "*" : "",
of->compatible);
/* Replace all whitespace with underscores */
for (tmp = alias; tmp && *tmp; tmp++)
if (isspace (*tmp))
*tmp = '_';
add_wildcard(alias);
return 1;
}
static int do_vio_entry(const char *filename, struct vio_device_id *vio,
char *alias)
{
char *tmp;
sprintf(alias, "vio:T%sS%s", vio->type[0] ? vio->type : "*",
vio->compat[0] ? vio->compat : "*");
/* Replace all whitespace with underscores */
for (tmp = alias; tmp && *tmp; tmp++)
if (isspace (*tmp))
*tmp = '_';
add_wildcard(alias);
return 1;
}
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
static void do_input(char *alias,
kernel_ulong_t *arr, unsigned int min, unsigned int max)
{
unsigned int i;
for (i = min; i < max; i++)
if (arr[i / BITS_PER_LONG] & (1L << (i%BITS_PER_LONG)))
sprintf(alias + strlen(alias), "%X,*", i);
}
/* input:b0v0p0e0-eXkXrXaXmXlXsXfXwX where X is comma-separated %02X. */
static int do_input_entry(const char *filename, struct input_device_id *id,
char *alias)
{
sprintf(alias, "input:");
ADD(alias, "b", id->flags & INPUT_DEVICE_ID_MATCH_BUS, id->bustype);
ADD(alias, "v", id->flags & INPUT_DEVICE_ID_MATCH_VENDOR, id->vendor);
ADD(alias, "p", id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT, id->product);
ADD(alias, "e", id->flags & INPUT_DEVICE_ID_MATCH_VERSION, id->version);
sprintf(alias + strlen(alias), "-e*");
if (id->flags & INPUT_DEVICE_ID_MATCH_EVBIT)
do_input(alias, id->evbit, 0, INPUT_DEVICE_ID_EV_MAX);
sprintf(alias + strlen(alias), "k*");
if (id->flags & INPUT_DEVICE_ID_MATCH_KEYBIT)
do_input(alias, id->keybit,
INPUT_DEVICE_ID_KEY_MIN_INTERESTING,
INPUT_DEVICE_ID_KEY_MAX);
sprintf(alias + strlen(alias), "r*");
if (id->flags & INPUT_DEVICE_ID_MATCH_RELBIT)
do_input(alias, id->relbit, 0, INPUT_DEVICE_ID_REL_MAX);
sprintf(alias + strlen(alias), "a*");
if (id->flags & INPUT_DEVICE_ID_MATCH_ABSBIT)
do_input(alias, id->absbit, 0, INPUT_DEVICE_ID_ABS_MAX);
sprintf(alias + strlen(alias), "m*");
if (id->flags & INPUT_DEVICE_ID_MATCH_MSCIT)
do_input(alias, id->mscbit, 0, INPUT_DEVICE_ID_MSC_MAX);
sprintf(alias + strlen(alias), "l*");
if (id->flags & INPUT_DEVICE_ID_MATCH_LEDBIT)
do_input(alias, id->ledbit, 0, INPUT_DEVICE_ID_LED_MAX);
sprintf(alias + strlen(alias), "s*");
if (id->flags & INPUT_DEVICE_ID_MATCH_SNDBIT)
do_input(alias, id->sndbit, 0, INPUT_DEVICE_ID_SND_MAX);
sprintf(alias + strlen(alias), "f*");
if (id->flags & INPUT_DEVICE_ID_MATCH_FFBIT)
do_input(alias, id->ffbit, 0, INPUT_DEVICE_ID_FF_MAX);
sprintf(alias + strlen(alias), "w*");
if (id->flags & INPUT_DEVICE_ID_MATCH_SWBIT)
do_input(alias, id->swbit, 0, INPUT_DEVICE_ID_SW_MAX);
return 1;
}
static int do_eisa_entry(const char *filename, struct eisa_device_id *eisa,
char *alias)
{
if (eisa->sig[0])
sprintf(alias, EISA_DEVICE_MODALIAS_FMT "*", eisa->sig);
else
strcat(alias, "*");
return 1;
}
/* Looks like: parisc:tNhvNrevNsvN */
static int do_parisc_entry(const char *filename, struct parisc_device_id *id,
char *alias)
{
id->hw_type = TO_NATIVE(id->hw_type);
id->hversion = TO_NATIVE(id->hversion);
id->hversion_rev = TO_NATIVE(id->hversion_rev);
id->sversion = TO_NATIVE(id->sversion);
strcpy(alias, "parisc:");
ADD(alias, "t", id->hw_type != PA_HWTYPE_ANY_ID, id->hw_type);
ADD(alias, "hv", id->hversion != PA_HVERSION_ANY_ID, id->hversion);
ADD(alias, "rev", id->hversion_rev != PA_HVERSION_REV_ANY_ID, id->hversion_rev);
ADD(alias, "sv", id->sversion != PA_SVERSION_ANY_ID, id->sversion);
add_wildcard(alias);
return 1;
}
/* Looks like: sdio:cNvNdN. */
static int do_sdio_entry(const char *filename,
struct sdio_device_id *id, char *alias)
{
id->class = TO_NATIVE(id->class);
id->vendor = TO_NATIVE(id->vendor);
id->device = TO_NATIVE(id->device);
strcpy(alias, "sdio:");
ADD(alias, "c", id->class != (__u8)SDIO_ANY_ID, id->class);
ADD(alias, "v", id->vendor != (__u16)SDIO_ANY_ID, id->vendor);
ADD(alias, "d", id->device != (__u16)SDIO_ANY_ID, id->device);
add_wildcard(alias);
return 1;
}
/* Looks like: ssb:vNidNrevN. */
static int do_ssb_entry(const char *filename,
struct ssb_device_id *id, char *alias)
{
id->vendor = TO_NATIVE(id->vendor);
id->coreid = TO_NATIVE(id->coreid);
id->revision = TO_NATIVE(id->revision);
strcpy(alias, "ssb:");
ADD(alias, "v", id->vendor != SSB_ANY_VENDOR, id->vendor);
ADD(alias, "id", id->coreid != SSB_ANY_ID, id->coreid);
ADD(alias, "rev", id->revision != SSB_ANY_REV, id->revision);
add_wildcard(alias);
return 1;
}
/* Looks like: virtio:dNvN */
static int do_virtio_entry(const char *filename, struct virtio_device_id *id,
char *alias)
{
id->device = TO_NATIVE(id->device);
id->vendor = TO_NATIVE(id->vendor);
strcpy(alias, "virtio:");
ADD(alias, "d", id->device != VIRTIO_DEV_ANY_ID, id->device);
ADD(alias, "v", id->vendor != VIRTIO_DEV_ANY_ID, id->vendor);
add_wildcard(alias);
return 1;
}
/* Looks like: i2c:S */
static int do_i2c_entry(const char *filename, struct i2c_device_id *id,
char *alias)
{
sprintf(alias, I2C_MODULE_PREFIX "%s", id->name);
return 1;
}
/* Looks like: spi:S */
static int do_spi_entry(const char *filename, struct spi_device_id *id,
char *alias)
{
sprintf(alias, SPI_MODULE_PREFIX "%s", id->name);
return 1;
}
static const struct dmifield {
const char *prefix;
int field;
} dmi_fields[] = {
{ "bvn", DMI_BIOS_VENDOR },
{ "bvr", DMI_BIOS_VERSION },
{ "bd", DMI_BIOS_DATE },
{ "svn", DMI_SYS_VENDOR },
{ "pn", DMI_PRODUCT_NAME },
{ "pvr", DMI_PRODUCT_VERSION },
{ "rvn", DMI_BOARD_VENDOR },
{ "rn", DMI_BOARD_NAME },
{ "rvr", DMI_BOARD_VERSION },
{ "cvn", DMI_CHASSIS_VENDOR },
{ "ct", DMI_CHASSIS_TYPE },
{ "cvr", DMI_CHASSIS_VERSION },
{ NULL, DMI_NONE }
};
static void dmi_ascii_filter(char *d, const char *s)
{
/* Filter out characters we don't want to see in the modalias string */
for (; *s; s++)
if (*s > ' ' && *s < 127 && *s != ':')
*(d++) = *s;
*d = 0;
}
static int do_dmi_entry(const char *filename, struct dmi_system_id *id,
char *alias)
{
int i, j;
sprintf(alias, "dmi*");
for (i = 0; i < ARRAY_SIZE(dmi_fields); i++) {
for (j = 0; j < 4; j++) {
if (id->matches[j].slot &&
id->matches[j].slot == dmi_fields[i].field) {
sprintf(alias + strlen(alias), ":%s*",
dmi_fields[i].prefix);
dmi_ascii_filter(alias + strlen(alias),
id->matches[j].substr);
strcat(alias, "*");
}
}
}
strcat(alias, ":");
return 1;
}
static int do_platform_entry(const char *filename,
struct platform_device_id *id, char *alias)
{
sprintf(alias, PLATFORM_MODULE_PREFIX "%s", id->name);
return 1;
}
/* Ignore any prefix, eg. some architectures prepend _ */
static inline int sym_is(const char *symbol, const char *name)
{
const char *match;
match = strstr(symbol, name);
if (!match)
return 0;
return match[strlen(symbol)] == '\0';
}
static void do_table(void *symval, unsigned long size,
unsigned long id_size,
const char *device_id,
void *function,
struct module *mod)
{
unsigned int i;
char alias[500];
int (*do_entry)(const char *, void *entry, char *alias) = function;
device_id_check(mod->name, device_id, size, id_size, symval);
/* Leave last one: it's the terminator. */
size -= id_size;
for (i = 0; i < size; i += id_size) {
if (do_entry(mod->name, symval+i, alias)) {
buf_printf(&mod->dev_table_buf,
"MODULE_ALIAS(\"%s\");\n", alias);
}
}
}
/* Create MODULE_ALIAS() statements.
* At this time, we cannot write the actual output C source yet,
* so we write into the mod->dev_table_buf buffer. */
void handle_moddevtable(struct module *mod, struct elf_info *info,
Elf_Sym *sym, const char *symname)
{
void *symval;
char *zeros = NULL;
/* We're looking for a section relative symbol */
if (!sym->st_shndx || sym->st_shndx >= info->hdr->e_shnum)
return;
/* Handle all-NULL symbols allocated into .bss */
if (info->sechdrs[sym->st_shndx].sh_type & SHT_NOBITS) {
zeros = calloc(1, sym->st_size);
symval = zeros;
} else {
symval = (void *)info->hdr
+ info->sechdrs[sym->st_shndx].sh_offset
+ sym->st_value;
}
if (sym_is(symname, "__mod_pci_device_table"))
do_table(symval, sym->st_size,
sizeof(struct pci_device_id), "pci",
do_pci_entry, mod);
else if (sym_is(symname, "__mod_usb_device_table"))
/* special case to handle bcdDevice ranges */
do_usb_table(symval, sym->st_size, mod);
else if (sym_is(symname, "__mod_hid_device_table"))
do_table(symval, sym->st_size,
sizeof(struct hid_device_id), "hid",
do_hid_entry, mod);
else if (sym_is(symname, "__mod_ieee1394_device_table"))
do_table(symval, sym->st_size,
sizeof(struct ieee1394_device_id), "ieee1394",
do_ieee1394_entry, mod);
else if (sym_is(symname, "__mod_ccw_device_table"))
do_table(symval, sym->st_size,
sizeof(struct ccw_device_id), "ccw",
do_ccw_entry, mod);
else if (sym_is(symname, "__mod_ap_device_table"))
do_table(symval, sym->st_size,
sizeof(struct ap_device_id), "ap",
do_ap_entry, mod);
else if (sym_is(symname, "__mod_css_device_table"))
do_table(symval, sym->st_size,
sizeof(struct css_device_id), "css",
do_css_entry, mod);
else if (sym_is(symname, "__mod_serio_device_table"))
do_table(symval, sym->st_size,
sizeof(struct serio_device_id), "serio",
do_serio_entry, mod);
else if (sym_is(symname, "__mod_acpi_device_table"))
do_table(symval, sym->st_size,
sizeof(struct acpi_device_id), "acpi",
do_acpi_entry, mod);
else if (sym_is(symname, "__mod_pnp_device_table"))
do_pnp_device_entry(symval, sym->st_size, mod);
else if (sym_is(symname, "__mod_pnp_card_device_table"))
do_pnp_card_entries(symval, sym->st_size, mod);
else if (sym_is(symname, "__mod_pcmcia_device_table"))
do_table(symval, sym->st_size,
sizeof(struct pcmcia_device_id), "pcmcia",
do_pcmcia_entry, mod);
else if (sym_is(symname, "__mod_of_device_table"))
do_table(symval, sym->st_size,
sizeof(struct of_device_id), "of",
do_of_entry, mod);
else if (sym_is(symname, "__mod_vio_device_table"))
do_table(symval, sym->st_size,
sizeof(struct vio_device_id), "vio",
do_vio_entry, mod);
else if (sym_is(symname, "__mod_input_device_table"))
do_table(symval, sym->st_size,
sizeof(struct input_device_id), "input",
do_input_entry, mod);
else if (sym_is(symname, "__mod_eisa_device_table"))
do_table(symval, sym->st_size,
sizeof(struct eisa_device_id), "eisa",
do_eisa_entry, mod);
else if (sym_is(symname, "__mod_parisc_device_table"))
do_table(symval, sym->st_size,
sizeof(struct parisc_device_id), "parisc",
do_parisc_entry, mod);
else if (sym_is(symname, "__mod_sdio_device_table"))
do_table(symval, sym->st_size,
sizeof(struct sdio_device_id), "sdio",
do_sdio_entry, mod);
else if (sym_is(symname, "__mod_ssb_device_table"))
do_table(symval, sym->st_size,
sizeof(struct ssb_device_id), "ssb",
do_ssb_entry, mod);
else if (sym_is(symname, "__mod_virtio_device_table"))
do_table(symval, sym->st_size,
sizeof(struct virtio_device_id), "virtio",
do_virtio_entry, mod);
else if (sym_is(symname, "__mod_i2c_device_table"))
do_table(symval, sym->st_size,
sizeof(struct i2c_device_id), "i2c",
do_i2c_entry, mod);
else if (sym_is(symname, "__mod_spi_device_table"))
do_table(symval, sym->st_size,
sizeof(struct spi_device_id), "spi",
do_spi_entry, mod);
else if (sym_is(symname, "__mod_dmi_device_table"))
do_table(symval, sym->st_size,
sizeof(struct dmi_system_id), "dmi",
do_dmi_entry, mod);
else if (sym_is(symname, "__mod_platform_device_table"))
do_table(symval, sym->st_size,
sizeof(struct platform_device_id), "platform",
do_platform_entry, mod);
free(zeros);
}
/* Now add out buffered information to the generated C source */
void add_moddevtable(struct buffer *buf, struct module *mod)
{
buf_printf(buf, "\n");
buf_write(buf, mod->dev_table_buf.p, mod->dev_table_buf.pos);
free(mod->dev_table_buf.p);
}

View File

@@ -0,0 +1,97 @@
/*
* Post-process kernel image and kernel modules for Fast LKM hash loader
*
* Copyright (C) 2008-2009 STMicroelectronics Ltd
*
* Author(s): Carmelo Amoroso <carmelo.amoroso@st.com>, STMicroelectronics
*
* 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
* any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Usage: it is called directly by kbuild
* (see $(rule_ksymhash) defined in scripts/Makefile.ksymhash)
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <sys/stat.h>
#include "elflib.h"
static inline
void compute_exported_hash(const struct elf_info *elf, enum ksymtab_type tp)
{
struct kernel_symbol *sym;
struct kernel_symbol *start = elf->ksym_tables[tp].start;
struct kernel_symbol *stop = elf->ksym_tables[tp].stop;
for (sym = start; sym < stop; sym++)
sym->hash_value = gnu_hash(GET_KSTRING(sym, elf->kstr_offset));
}
static void compute_unresolved_hash(struct elf_info *elf)
{
Elf_Sym *sym;
ksym_hash_t *hash_values = elf->undef_hash.start;
if (!hash_values)
/* .undef.hash section is not present */
return;
for (sym = elf->symtab_start; sym < elf->symtab_stop; sym++) {
if (sym->st_shndx == SHN_UNDEF) {
/* undefined symbol */
if (ELF_ST_BIND(sym->st_info) != STB_GLOBAL &&
ELF_ST_BIND(sym->st_info) != STB_WEAK)
continue;
else {
/* GLOBAL or WEAK undefined symbols */
*hash_values = gnu_hash((unsigned char *)
(elf->strtab + sym->st_name));
/*
* The hash_values array stored into the
* .undef.hash section that is ordered as the
* undefined symbols of the .symtab
*/
hash_values++;
}
}
}
}
int main(int argc, char **argv)
{
enum ksymtab_type k;
struct elf_info info = { };
if (!parse_writable_elf(&info, argv[1]))
exit(1);
for (k = KSYMTAB; k < KSYMTAB_ALL; k++)
if (info.ksym_tables[k].name)
/* Compute hash value for exported symbols */
compute_exported_hash(&info, k);
compute_unresolved_hash(&info);
parse_elf_finish(&info);
return 0;
}

View File

@@ -0,0 +1,246 @@
/*
* Functions used by modpost to create data sections for Fast LKM hash loader
*
* Copyright (C) 2008-2009 STMicroelectronics Ltd
*
* Author(s): Carmelo Amoroso <carmelo.amoroso@st.com>, STMicroelectronics
*
* 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
* any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#define EMPTY_SLOT -1
#include "modpost.h"
/**
* Record hash_values for unresolved symbols
**/
void add_undef_hash(struct buffer *b, struct module *mod)
{
struct symbol *s;
unsigned int i;
buf_printf(b, "#ifdef CONFIG_LKM_ELF_HASH\n");
buf_printf(b, "static unsigned long __symtab_hash[]\n");
buf_printf(b, "__used\n");
buf_printf(b, "__attribute__((section(\".undef.hash\"))) = {\n\t");
for (s = mod->unres, i = 0; s; s = s->next) {
/*
* Fill with zero, the order of unresolved symbol is not yet
* correct. This will create a placeholder for the hash values.
*/
buf_printf(b, "%lx, ", 0L);
if (!(++i % 24))
buf_printf(b, "\n\t");
}
buf_printf(b, "\n};\n");
buf_printf(b, "#endif\n");
}
/* Array used to determine the number of hash table buckets to use
based on the number of symbols there are. If there are fewer than
3 symbols we use 1 bucket, fewer than 17 symbols we use 3 buckets,
fewer than 37 we use 17 buckets, and so forth. We never use more
than 32771 buckets. */
static const size_t elf_buckets[] = {
1, 3, 17, 37, 67, 97, 131, 197, 263, 521, 1031, 2053, 4099, 8209,
16411, 32771, 0
};
/* FIXME: must implement the optimized algorithm for best size choosing */
static uint32_t compute_bucket_count(unsigned long int nsyms, int gnu_hash)
{
uint32_t best_size = 0;
unsigned int i;
/*
* This is the fallback solution if no 64bit type is available or if we
* are not supposed to spend much time on optimizations. We select the
* bucket count using a fixed set of numbers.
*/
for (i = 0; elf_buckets[i] != 0; i++) {
best_size = elf_buckets[i];
if (nsyms < elf_buckets[i + 1])
break;
}
if (gnu_hash && best_size < 2)
best_size = 2;
return best_size;
}
/* Handle collisions: return the max of the used slot of the chain
-1 in case of error */
static int handle_collision(struct elf_htable *htable, unsigned long idx,
unsigned long value)
{
uint32_t *slot;
/* sanity check: check chain's boundary */
if (idx >= htable->nchain)
return -1;
slot = &htable->chains[idx];
/* Fill the chains[idx] with the new value, if empty. */
if (*slot == EMPTY_SLOT) {
*slot = value;
return 0;
}
/*
* If the slot is already used, used the value itself
* as a new index for the next chain entry.
* Do it recursively.
*/
return handle_collision(htable, *slot, value);
}
static int fill_hashtable(struct elf_htable *htable,
struct kernel_symbol *start,
struct kernel_symbol *stop,
long kstrings_base)
{
struct kernel_symbol *ksym;
uint32_t nb;
unsigned long hvalue;
int last_chain_slot;
/* sanity check */
if ((htable->elf_buckets == NULL) || (htable->chains == NULL))
return -1;
/* Initialize buckets and chains with EMPTY_SLOT */
memset(htable->elf_buckets, EMPTY_SLOT,
htable->nbucket * sizeof(uint32_t));
memset(htable->chains, EMPTY_SLOT, htable->nchain * sizeof(uint32_t));
nb = htable->nbucket;
for (ksym = start, hvalue = 0; ksym < stop; ksym++, hvalue++) {
const unsigned char *name = GET_KSTRING(ksym, kstrings_base);
unsigned long h = gnu_hash(name);
unsigned long idx = h % nb;
uint32_t *slot = &htable->elf_buckets[idx];
/*
* Store the index of the export symbol ksym in its
* related __ksymtable in the hash table buckets for
* using during lookup.
* If the slot is alredy used ( != -1) then we have a collision
* it needs to create an entry in the chain
*/
if (*slot == EMPTY_SLOT)
*slot = hvalue;
else {
if (handle_collision(htable, *slot, hvalue) < 0)
/* Something wrong happened */
return -1;
}
}
/*
* Update the chain lenght with the best value
* so that we will cut unused entries beyond this upper limit
* In the best case, when there are not collisions, htable->chains
* will be 0 size... good !
*/
/* Look for upper chains empty slot */
for (last_chain_slot = htable->nchain; --last_chain_slot >= 0 &&
htable->chains[last_chain_slot] == EMPTY_SLOT;)
;
htable->nchain = last_chain_slot + 1;
return 0;
}
static void add_elf_hashtable(struct buffer *b, const struct elf_info *elf,
enum ksymtab_type type)
{
const char *table = elf->ksym_tables[type].name;
struct kernel_symbol *kstart = elf->ksym_tables[type].start;
struct kernel_symbol *kstop = elf->ksym_tables[type].stop;
struct elf_htable htable = {
.nbucket = 0,
.nchain = 0,
.elf_buckets = NULL,
.chains = NULL,
};
unsigned long nsyms = (unsigned long)(kstop - kstart);
unsigned long i;
htable.nbucket = compute_bucket_count(nsyms, 0);
htable.elf_buckets = (uint32_t *)
malloc(htable.nbucket * sizeof(uint32_t));
if (!htable.elf_buckets)
return;
/*
* Worst case: the chain is as long as the maximum number of
* exported symbols should be put in the chain
*/
htable.nchain = nsyms;
htable.chains = (uint32_t *) malloc(htable.nchain * sizeof(uint32_t));
if (!htable.chains)
return;
/*
* When handling relocatable objects as at this stage, ksym->name
* is always an offset with respect to the __ksymtab_strings, so we must
* use elf->kstrings for both vmlinux.o and <module>.o
*/
if (fill_hashtable(&htable, kstart, kstop, (long) elf->kstrings) < 0)
return;
buf_printf(b, "#ifdef CONFIG_LKM_ELF_HASH\n\n");
buf_printf(b, "#include <linux/types.h>\n");
buf_printf(b, "static uint32_t htable%s[]\n", table);
buf_printf(b, "__used\n");
buf_printf(b, "__attribute__((section(\"%s.htable\"))) = {\n", table);
/* 1st entry is nbucket */
buf_printf(b, "\t%u, /* bucket lenght*/\n", htable.nbucket);
/* 2nd entry is nchain */
buf_printf(b, "\t%u, /* chain lenght */\n", htable.nchain);
buf_printf(b, "\t/* the buckets */\n\t");
for (i = 0; i < htable.nbucket; i++) {
buf_printf(b, "%4d, ", htable.elf_buckets[i]);
if (!((i+1) % 12))
buf_printf(b, "\n\t");
}
buf_printf(b, "\n\t/* the chains */\n\t");
for (i = 0; i < htable.nchain; i++) {
buf_printf(b, "%4d, ", htable.chains[i]);
if (!((i+1) % 12))
buf_printf(b, "\n\t");
}
buf_printf(b, "\n};\n");
buf_printf(b, "#endif\n");
free(htable.elf_buckets);
free(htable.chains);
}
/*
* Add hash table (ELF SysV) for exported symbols
*/
void add_ksymtable_hash(struct buffer *b, struct module *mod)
{
enum ksymtab_type k;
/* Skip __ksymtab_strings */
for (k = KSYMTAB; k < KSYMTAB_ALL; k++)
if (mod->info->ksym_tables[k].name)
add_elf_hashtable(b, mod->info, k);
}

View File

@@ -0,0 +1,66 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <elf.h>
int
main(int argc, char **argv)
{
unsigned char ei[EI_NIDENT];
union { short s; char c[2]; } endian_test;
if (argc != 2) {
fprintf(stderr, "Error: no arch\n");
}
if (fread(ei, 1, EI_NIDENT, stdin) != EI_NIDENT) {
fprintf(stderr, "Error: input truncated\n");
return 1;
}
if (memcmp(ei, ELFMAG, SELFMAG) != 0) {
fprintf(stderr, "Error: not ELF\n");
return 1;
}
switch (ei[EI_CLASS]) {
case ELFCLASS32:
printf("#define KERNEL_ELFCLASS ELFCLASS32\n");
break;
case ELFCLASS64:
printf("#define KERNEL_ELFCLASS ELFCLASS64\n");
break;
default:
exit(1);
}
switch (ei[EI_DATA]) {
case ELFDATA2LSB:
printf("#define KERNEL_ELFDATA ELFDATA2LSB\n");
break;
case ELFDATA2MSB:
printf("#define KERNEL_ELFDATA ELFDATA2MSB\n");
break;
default:
exit(1);
}
if (sizeof(unsigned long) == 4) {
printf("#define HOST_ELFCLASS ELFCLASS32\n");
} else if (sizeof(unsigned long) == 8) {
printf("#define HOST_ELFCLASS ELFCLASS64\n");
}
endian_test.s = 0x0102;
if (memcmp(endian_test.c, "\x01\x02", 2) == 0)
printf("#define HOST_ELFDATA ELFDATA2MSB\n");
else if (memcmp(endian_test.c, "\x02\x01", 2) == 0)
printf("#define HOST_ELFDATA ELFDATA2LSB\n");
else
exit(1);
if ((strcmp(argv[1], "h8300") == 0)
|| (strcmp(argv[1], "blackfin") == 0))
printf("#define MODULE_SYMBOL_PREFIX \"_\"\n");
else
printf("#define MODULE_SYMBOL_PREFIX \"\"\n");
return 0;
}

2029
kernel/scripts/mod/modpost.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,92 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <elf.h>
#include "elflib.h"
#define NOFAIL(ptr) do_nofail((ptr), #ptr)
void *do_nofail(void *ptr, const char *expr);
struct buffer {
char *p;
int pos;
int size;
};
void __attribute__((format(printf, 2, 3)))
buf_printf(struct buffer *buf, const char *fmt, ...);
void
buf_write(struct buffer *buf, const char *s, int len);
struct module {
struct module *next;
const char *name;
int gpl_compatible;
struct symbol *unres;
struct elf_info *info;
int seen;
int skip;
int has_init;
int has_cleanup;
struct buffer dev_table_buf;
char **markers;
size_t nmarkers;
char srcversion[25];
};
/* How a symbol is exported */
enum export {
export_plain, export_unused, export_gpl,
export_unused_gpl, export_gpl_future, export_unknown
};
/* A hash of all exported symbols,
* struct symbol is also used for lists of unresolved symbols */
#define SYMBOL_HASH_SIZE 1024
struct symbol {
struct symbol *next;
struct module *module;
unsigned int crc;
int crc_valid;
unsigned int weak:1;
unsigned int vmlinux:1; /* 1 if symbol is defined in vmlinux */
unsigned int kernel:1; /* 1 if symbol is from kernel
* (only for external modules) **/
unsigned int preloaded:1; /* 1 if symbol from Module.symvers */
enum export export; /* Type of export */
char name[0];
};
/* file2alias.c */
extern unsigned int cross_build;
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);
/* sumversion.c */
void maybe_frob_rcs_version(const char *modfilename,
char *version,
void *modinfo,
unsigned long modinfo_offset);
void get_src_version(const char *modname, char sum[], unsigned sumlen);
/* from modpost.c */
char* get_next_line(unsigned long *pos, void *file, unsigned long size);
/* from ktablehash.c */
#ifdef CONFIG_LKM_ELF_HASH
void add_undef_hash(struct buffer *b, struct module *mod);
void add_ksymtable_hash(struct buffer *b, struct module *mod);
#endif

View File

@@ -0,0 +1,504 @@
#include <netinet/in.h>
#ifdef __sun__
#include <inttypes.h>
#else
#include <stdint.h>
#endif
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <limits.h>
#include "modpost.h"
/*
* 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;
}
/* Check whether the file is a static library or not */
static int is_static_library(const char *objfile)
{
int len = strlen(objfile);
if (objfile[len - 2] == '.' && objfile[len - 1] == 'a')
return 1;
else
return 0;
}
/* 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, *line, *dir;
const char *base;
unsigned long flen, pos = 0;
int dirlen, ret = 0, check_files = 0;
cmd = NOFAIL(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);
}
dir = NOFAIL(malloc(dirlen + 1));
strncpy(dir, objfile, dirlen);
dir[dirlen] = '\0';
file = grab_file(cmd, &flen);
if (!file) {
warn("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/module.h \
Sum all files in the same dir or subdirs.
*/
while ((line = get_next_line(&pos, file, flen)) != NULL) {
char* p = line;
if (strncmp(line, "deps_", sizeof("deps_")-1) == 0) {
check_files = 1;
continue;
}
if (!check_files)
continue;
/* Continue until line does not end with '\' */
if ( *(p + strlen(p)-1) != '\\')
break;
/* Terminate line at first space, to get rid of final ' \' */
while (*p) {
if (isspace(*p)) {
*p = '\0';
break;
}
p++;
}
/* Check if this file is in same dir as objfile */
if ((strstr(line, dir)+strlen(dir)-1) == strrchr(line, '/')) {
if (!parse_file(line, md)) {
warn("could not open %s: %s\n",
line, strerror(errno));
goto out_file;
}
}
}
/* Everyone parsed OK */
ret = 1;
out_file:
release_file(file, flen);
out:
free(dir);
free(cmd);
return ret;
}
/* Calc and record src checksum. */
void get_src_version(const char *modname, char sum[], unsigned sumlen)
{
void *file;
unsigned long len;
struct md4_ctx md;
char *sources, *end, *fname;
const char *basename;
char filelist[PATH_MAX + 1];
char *modverdir = getenv("MODVERDIR");
if (!modverdir)
modverdir = ".";
/* 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, "%s/%.*s.mod", modverdir,
(int) strlen(basename) - 2, basename);
file = grab_file(filelist, &len);
if (!file)
/* not a module or .mod file missing - ignore */
return;
sources = strchr(file, '\n');
if (!sources) {
warn("malformed versions file for %s\n", modname);
goto release;
}
sources++;
end = strchr(sources, '\n');
if (!end) {
warn("bad ending versions file for %s\n", modname);
goto release;
}
*end = '\0';
md4_init(&md);
while ((fname = strsep(&sources, " ")) != NULL) {
if (!*fname)
continue;
if (!(is_static_library(fname)) &&
!parse_source_files(fname, &md))
goto release;
}
md4_final_ascii(&md, sum, sumlen);
release:
release_file(file, len);
}
static void write_version(const char *filename, const char *sum,
unsigned long offset)
{
int fd;
fd = open(filename, O_RDWR);
if (fd < 0) {
warn("changing sum in %s failed: %s\n",
filename, strerror(errno));
return;
}
if (lseek(fd, offset, SEEK_SET) == (off_t)-1) {
warn("changing sum in %s:%lu failed: %s\n",
filename, offset, strerror(errno));
goto out;
}
if (write(fd, sum, strlen(sum)+1) != strlen(sum)+1) {
warn("writing sum in %s failed: %s\n",
filename, strerror(errno));
goto out;
}
out:
close(fd);
}
static int strip_rcs_crap(char *version)
{
unsigned int len, full_len;
if (strncmp(version, "$Revision", strlen("$Revision")) != 0)
return 0;
/* 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));
return 1;
}
/* Clean up RCS-style version numbers. */
void maybe_frob_rcs_version(const char *modfilename,
char *version,
void *modinfo,
unsigned long version_offset)
{
if (strip_rcs_crap(version))
write_version(modfilename, version, version_offset);
}