/*
* Mini insmod implementation for busybox
*
- * This version of insmod supports x86, ARM, SH3/4, powerpc, m68k,
+ * This version of insmod supports x86, ARM, SH3/4/5, powerpc, m68k,
* MIPS, and v850e.
*
* Copyright (C) 1999,2000 by Lineo, inc. and Erik Andersen
- * Copyright (C) 1999,2000,2001,2002 by Erik Andersen <andersee@debian.org>
+ * Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.org>
* and Ron Alder <alder@lineo.com>
*
* Miles Bader <miles@gnu.org> added NEC V850E support.
* very minor changes required to also work with StrongArm and presumably
* all ARM based systems.
*
+ * Paul Mundt <lethal@linux-sh.org> 08-Aug-2003.
+ * Integrated support for sh64 (SH-5), from preliminary modutils
+ * patches from Benedict Gaster <benedict.gaster@superh.com>.
+ * Currently limited to support for 32bit ABI.
+ *
* Magnus Damm <damm@opensource.se> 22-May-2002.
* The plt and got code are now using the same structs.
* Added generic linked list code to fully support PowerPC.
#include <assert.h>
#include <string.h>
#include <getopt.h>
+#include <fcntl.h>
#include <sys/utsname.h>
#include "busybox.h"
#define ELFCLASSM ELFCLASS32
#endif
+#if defined(__s390__)
+#define CONFIG_USE_PLT_ENTRIES
+#define CONFIG_PLT_ENTRY_SIZE 8
+#define CONFIG_USE_GOT_ENTRIES
+#define CONFIG_GOT_ENTRY_SIZE 8
+#define CONFIG_USE_SINGLE
+
+#define MATCH_MACHINE(x) (x == EM_S390)
+#define SHT_RELM SHT_RELA
+#define Elf32_RelM Elf32_Rela
+#define ELFCLASSM ELFCLASS32
+#endif
+
#if defined(__i386__)
#define CONFIG_USE_GOT_ENTRIES
#define CONFIG_GOT_ENTRY_SIZE 4
#define Elf32_RelM Elf32_Rela
#define ELFCLASSM ELFCLASS32
-/* the SH changes have only been tested on the SH4 in =little endian= mode */
+/* the SH changes have only been tested in =little endian= mode */
/* I'm not sure about big endian, so let's warn: */
-#if (defined(__SH4__) || defined(__SH3__)) && defined(__BIG_ENDIAN__)
-#error insmod.c may require changes for use on big endian SH4/SH3
+#if defined(__sh__) && defined(__BIG_ENDIAN__)
+#error insmod.c may require changes for use on big endian SH
#endif
/* it may or may not work on the SH1/SH2... So let's error on those
also */
-#if (defined(__sh__) && (!(defined(__SH3__) || defined(__SH4__))))
-#error insmod.c may require changes for non-SH3/SH4 use
+#if ((!(defined(__SH3__) || defined(__SH4__) || defined(__SH5__)))) && \
+ (defined(__sh__))
+#error insmod.c may require changes for SH1 or SH2 use
#endif
#endif
#ifndef MODUTILS_MODULE_H
static const int MODUTILS_MODULE_H = 1;
-#ident "$Id: insmod.c,v 1.83 2002/05/24 06:50:15 andersen Exp $"
+#ident "$Id: insmod.c,v 1.103 2003/09/03 00:42:58 bug1 Exp $"
/* This file contains the structures used by the 2.0 and 2.1 kernels.
We do not use the kernel headers directly because we do not wish
#ifndef MODUTILS_OBJ_H
static const int MODUTILS_OBJ_H = 1;
-#ident "$Id: insmod.c,v 1.83 2002/05/24 06:50:15 andersen Exp $"
+#ident "$Id: insmod.c,v 1.103 2003/09/03 00:42:58 bug1 Exp $"
/* The relocatable object is manipulated using elfin types. */
# endif
#endif
-/* For some reason this is missing from libc5. */
+/* For some reason this is missing from some ancient C libraries.... */
#ifndef ELF32_ST_INFO
# define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf))
#endif
static void arch_create_got (struct obj_file *f);
+static int obj_gpl_license(struct obj_file *f, const char **license);
+
#ifdef CONFIG_FEATURE_NEW_MODULE_INTERFACE
static int arch_init_module (struct obj_file *f, struct new_module *);
#endif
//----------------------------------------------------------------------------
-
/* SPFX is always a string, so it can be concatenated to string constants. */
#ifdef SYMBOL_PREFIX
#define SPFX SYMBOL_PREFIX
static int flag_force_load = 0;
static int flag_autoclean = 0;
static int flag_verbose = 0;
+static int flag_quiet = 0;
static int flag_export = 1;
static int n_ext_modules_used;
extern int delete_module(const char *);
-static char m_filename[FILENAME_MAX + 1];
-static char m_fullName[FILENAME_MAX + 1];
+static char *m_filename;
+static char *m_fullName;
if (fullname[0] == '\0')
return (FALSE);
else {
- char *tmp, *tmp1 = xstrdup(filename);
- tmp = get_last_path_component(tmp1);
+ char *tmp, *tmp1 = bb_xstrdup(filename);
+ tmp = bb_get_last_path_component(tmp1);
if (strcmp(tmp, fullname) == 0) {
free(tmp1);
/* Stop searching if we find a match */
- safe_strncpy(m_filename, filename, sizeof(m_filename));
+ m_filename = bb_xstrdup(filename);
return (TRUE);
}
free(tmp1);
*loc += v - got;
break;
+#elif defined(__s390__)
+ case R_390_32:
+ *(unsigned int *) loc += v;
+ break;
+ case R_390_16:
+ *(unsigned short *) loc += v;
+ break;
+ case R_390_8:
+ *(unsigned char *) loc += v;
+ break;
+
+ case R_390_PC32:
+ *(unsigned int *) loc += v - dot;
+ break;
+ case R_390_PC16DBL:
+ *(unsigned short *) loc += (v - dot) >> 1;
+ break;
+ case R_390_PC16:
+ *(unsigned short *) loc += v - dot;
+ break;
+
+ case R_390_PLT32:
+ case R_390_PLT16DBL:
+ /* find the plt entry and initialize it. */
+ assert(isym != NULL);
+ pe = (struct arch_single_entry *) &isym->pltent;
+ assert(pe->allocated);
+ if (pe->inited == 0) {
+ ip = (unsigned long *)(ifile->plt->contents + pe->offset);
+ ip[0] = 0x0d105810; /* basr 1,0; lg 1,10(1); br 1 */
+ ip[1] = 0x100607f1;
+ if (ELF32_R_TYPE(rel->r_info) == R_390_PLT16DBL)
+ ip[2] = v - 2;
+ else
+ ip[2] = v;
+ pe->inited = 1;
+ }
+
+ /* Insert relative distance to target. */
+ v = plt + pe->offset - dot;
+ if (ELF32_R_TYPE(rel->r_info) == R_390_PLT32)
+ *(unsigned int *) loc = (unsigned int) v;
+ else if (ELF32_R_TYPE(rel->r_info) == R_390_PLT16DBL)
+ *(unsigned short *) loc = (unsigned short) ((v + 2) >> 1);
+ break;
+
+ case R_390_GLOB_DAT:
+ case R_390_JMP_SLOT:
+ *loc = v;
+ break;
+
+ case R_390_RELATIVE:
+ *loc += f->baseaddr;
+ break;
+
+ case R_390_GOTPC:
+ assert(got != 0);
+ *(unsigned long *) loc += got - dot;
+ break;
+
+ case R_390_GOT12:
+ case R_390_GOT16:
+ case R_390_GOT32:
+ assert(isym != NULL);
+ assert(got != 0);
+ if (!isym->gotent.inited)
+ {
+ isym->gotent.inited = 1;
+ *(Elf32_Addr *)(ifile->got->contents + isym->gotent.offset) = v;
+ }
+ if (ELF32_R_TYPE(rel->r_info) == R_390_GOT12)
+ *(unsigned short *) loc |= (*(unsigned short *) loc + isym->gotent.offset) & 0xfff;
+ else if (ELF32_R_TYPE(rel->r_info) == R_390_GOT16)
+ *(unsigned short *) loc += isym->gotent.offset;
+ else if (ELF32_R_TYPE(rel->r_info) == R_390_GOT32)
+ *(unsigned int *) loc += isym->gotent.offset;
+ break;
+
+ case R_390_GOTOFF:
+ assert(got != 0);
+ *loc += v - got;
+ break;
+
#elif defined(__i386__)
case R_386_NONE:
*loc = v - got;
break;
-#endif
+#if defined(__SH5__)
+ case R_SH_IMM_MEDLOW16:
+ case R_SH_IMM_LOW16:
+ {
+ Elf32_Addr word;
+
+ if (ELF32_R_TYPE(rel->r_info) == R_SH_IMM_MEDLOW16)
+ v >>= 16;
+
+ /*
+ * movi and shori have the format:
+ *
+ * | op | imm | reg | reserved |
+ * 31..26 25..10 9.. 4 3 .. 0
+ *
+ * so we simply mask and or in imm.
+ */
+ word = *loc & ~0x3fffc00;
+ word |= (v & 0xffff) << 10;
+
+ *loc = word;
+
+ break;
+ }
+
+ case R_SH_IMM_MEDLOW16_PCREL:
+ case R_SH_IMM_LOW16_PCREL:
+ {
+ Elf32_Addr word;
+
+ word = *loc & ~0x3fffc00;
+
+ v -= dot;
+
+ if (ELF32_R_TYPE(rel->r_info) == R_SH_IMM_MEDLOW16_PCREL)
+ v >>= 16;
+
+ word |= (v & 0xffff) << 10;
+
+ *loc = word;
+
+ break;
+ }
+#endif /* __SH5__ */
+#endif /* __sh__ */
default:
printf("Warning: unhandled reloc %d\n",(int)ELF32_R_TYPE(rel->r_info));
/* Don't report an error if the symbol is coming from
the kernel or some external module. */
if (secidx <= SHN_HIRESERVE)
- error_msg("%s multiply defined", name);
+ bb_error_msg("%s multiply defined", name);
return sym;
}
}
if (ELFW(ST_BIND)(info) == STB_LOCAL && symidx != -1) {
if (symidx >= f->local_symtab_size)
- error_msg("local symbol %s with index %ld exceeds local_symtab_size %ld",
+ bb_error_msg("local symbol %s with index %ld exceeds local_symtab_size %ld",
name, (long) symidx, (long) f->local_symtab_size);
else
f->local_symtab[symidx] = sym;
char *name_buf = 0;
size_t name_alloced_size = 0;
#endif
+#ifdef CONFIG_FEATURE_CHECK_TAINTED_MODULE
+ int gpl;
+ gpl = obj_gpl_license(f, NULL) == 0;
+#endif
for (i = 0, s = syms; i < nsyms; ++i, ++s) {
/* Only add symbols that are already marked external.
If we override locals we may cause problems for
argument initialization. We will also create a false
dependency on the module. */
struct obj_symbol *sym;
- char *name = (char *)s->name;
+ char *name;
+
+ /* GPL licensed modules can use symbols exported with
+ * EXPORT_SYMBOL_GPL, so ignore any GPLONLY_ prefix on the
+ * exported names. Non-GPL modules never see any GPLONLY_
+ * symbols so they cannot fudge it by adding the prefix on
+ * their references.
+ */
+ if (strncmp((char *)s->name, "GPLONLY_", 8) == 0) {
+#ifdef CONFIG_FEATURE_CHECK_TAINTED_MODULE
+ if (gpl)
+ ((char *)s->name) += 8;
+ else
+#endif
+ continue;
+ }
+ name = (char *)s->name;
#ifdef SYMBOL_PREFIX
/* Prepend SYMBOL_PREFIX to the symbol's name (the
/* Also check that the parameter was not resolved from the kernel. */
if (sym == NULL || sym->secidx > SHN_HIRESERVE) {
- error_msg("symbol for parameter %s not found", p);
+ bb_error_msg("symbol for parameter %s not found", p);
return 0;
}
str = alloca(strlen(q));
for (r = str, q++; *q != '"'; ++q, ++r) {
if (*q == '\0') {
- error_msg("improperly terminated string argument for %s", p);
+ bb_error_msg("improperly terminated string argument for %s", p);
return 0;
} else if (*q == '\\')
switch (*++q) {
return -1;
p = f->sections[sym->secidx]->contents + sym->value;
- strncpy(str, p, STRVERSIONLEN);
+ safe_strncpy(str, p, STRVERSIONLEN);
a = strtoul(p, &p, 10);
if (*p != '.')
nks = get_kernel_syms(NULL);
if (nks <= 0) {
if (nks)
- perror_msg("get_kernel_syms: %s", m_name);
+ bb_perror_msg("get_kernel_syms: %s", m_name);
else
- error_msg("No kernel symbols");
+ bb_error_msg("No kernel symbols");
return 0;
}
m_size | (flag_autoclean ? OLD_MOD_AUTOCLEAN
: 0), &routines, symtab);
if (ret)
- perror_msg("init_module: %s", m_name);
+ bb_perror_msg("init_module: %s", m_name);
free(image);
free(symtab);
p = get_modinfo_value(f, key);
key += 5;
if (p == NULL) {
- error_msg("invalid parameter %s", key);
+ bb_error_msg("invalid parameter %s", key);
return 0;
}
/* Also check that the parameter was not resolved from the kernel. */
if (sym == NULL || sym->secidx > SHN_HIRESERVE) {
- error_msg("symbol for parameter %s not found", key);
+ bb_error_msg("symbol for parameter %s not found", key);
return 0;
}
str = alloca(strlen(q));
for (r = str, q++; *q != '"'; ++q, ++r) {
if (*q == '\0') {
- error_msg("improperly terminated string argument for %s",
+ bb_error_msg("improperly terminated string argument for %s",
key);
return 0;
} else if (*q == '\\')
/* Get the size of each member */
/* Probably we should do that outside the loop ? */
if (!isdigit(*(p + 1))) {
- error_msg("parameter type 'c' for %s must be followed by"
+ bb_error_msg("parameter type 'c' for %s must be followed by"
" the maximum size", key);
return 0;
}
/* Check length */
if (strlen(str) >= charssize) {
- error_msg("string too long for %s (max %ld)", key,
+ bb_error_msg("string too long for %s (max %ld)", key,
charssize - 1);
return 0;
}
break;
default:
- error_msg("unknown parameter type '%c' for %s", *p, key);
+ bb_error_msg("unknown parameter type '%c' for %s", *p, key);
return 0;
}
}
case ',':
if (++n > max) {
- error_msg("too many values for %s (max %d)", key, max);
+ bb_error_msg("too many values for %s (max %d)", key, max);
return 0;
}
++q;
break;
default:
- error_msg("invalid argument syntax for %s", key);
+ bb_error_msg("invalid argument syntax for %s", key);
return 0;
}
}
end_of_arg:
if (n < min) {
- error_msg("too few values for %s (min %d)", key, min);
+ bb_error_msg("too few values for %s (min %d)", key, min);
return 0;
}
p = get_modinfo_value(f, "kernel_version");
if (p == NULL)
return -1;
- strncpy(str, p, STRVERSIONLEN);
+ safe_strncpy(str, p, STRVERSIONLEN);
a = strtoul(p, &p, 10);
if (*p != '.')
module_names = xrealloc(module_names, bufsize = ret);
goto retry_modules_load;
}
- perror_msg("QM_MODULES");
+ bb_perror_msg("QM_MODULES");
return 0;
}
/* The module was removed out from underneath us. */
continue;
}
- perror_msg("query_module: QM_INFO: %s", mn);
+ bb_perror_msg("query_module: QM_INFO: %s", mn);
return 0;
}
/* The module was removed out from underneath us. */
continue;
default:
- perror_msg("query_module: QM_SYMBOLS: %s", mn);
+ bb_perror_msg("query_module: QM_SYMBOLS: %s", mn);
return 0;
}
}
syms = xrealloc(syms, bufsize = ret);
goto retry_kern_sym_load;
}
- perror_msg("kernel: QM_SYMBOLS");
+ bb_perror_msg("kernel: QM_SYMBOLS");
return 0;
}
nksyms = nsyms = ret;
return 1;
}
+#ifdef CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS
+/* add an entry to the __ksymtab section, creating it if necessary */
+static void new_add_ksymtab(struct obj_file *f, struct obj_symbol *sym)
+{
+ struct obj_section *sec;
+ ElfW(Addr) ofs;
+
+ /* ensure __ksymtab is allocated, EXPORT_NOSYMBOLS creates a non-alloc section.
+ * If __ksymtab is defined but not marked alloc, x out the first character
+ * (no obj_delete routine) and create a new __ksymtab with the correct
+ * characteristics.
+ */
+ sec = obj_find_section(f, "__ksymtab");
+ if (sec && !(sec->header.sh_flags & SHF_ALLOC)) {
+ *((char *)(sec->name)) = 'x'; /* override const */
+ sec = NULL;
+ }
+ if (!sec)
+ sec = obj_create_alloced_section(f, "__ksymtab",
+ tgt_sizeof_void_p, 0);
+ if (!sec)
+ return;
+ sec->header.sh_flags |= SHF_ALLOC;
+ sec->header.sh_addralign = tgt_sizeof_void_p; /* Empty section might
+ be byte-aligned */
+ ofs = sec->header.sh_size;
+ obj_symbol_patch(f, sec->idx, ofs, sym);
+ obj_string_patch(f, sec->idx, ofs + tgt_sizeof_void_p, sym->name);
+ obj_extend_section(sec, 2 * tgt_sizeof_char_p);
+}
+#endif /* CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS */
static int new_create_module_ksymtab(struct obj_file *f)
{
sec = obj_find_section(f, ".this");
if (!sec || !sec->contents) {
- perror_msg_and_die("corrupt module %s?",m_name);
+ bb_perror_msg_and_die("corrupt module %s?",m_name);
}
module = (struct new_module *) sec->contents;
m_addr = sec->header.sh_addr;
ret = new_sys_init_module(m_name, (struct new_module *) image);
if (ret)
- perror_msg("init_module: %s", m_name);
+ bb_perror_msg("init_module: %s", m_name);
free(image);
#define new_init_module(x, y, z) TRUE
#define new_create_this_module(x, y) 0
+#define new_add_ksymtab(x, y) -1
#define new_create_module_ksymtab(x)
#define query_module(v, w, x, y, z) -1
sym->secidx = SHN_ABS;
sym->value = 0;
} else {
- error_msg("unresolved symbol %s", sym->name);
+ if (!flag_quiet) {
+ bb_error_msg("unresolved symbol %s", sym->name);
+ }
ret = 0;
}
}
errmsg = "Unhandled relocation";
bad_reloc:
if (extsym) {
- error_msg("%s of type %ld for %s", errmsg,
+ bb_error_msg("%s of type %ld for %s", errmsg,
(long) ELFW(R_TYPE) (rel->r_info),
strtab + extsym->st_name);
} else {
- error_msg("%s of type %ld", errmsg,
+ bb_error_msg("%s of type %ld", errmsg,
(long) ELFW(R_TYPE) (rel->r_info));
}
ret = 0;
fseek(fp, 0, SEEK_SET);
if (fread(&f->header, sizeof(f->header), 1, fp) != 1) {
- perror_msg("error reading ELF header");
+ bb_perror_msg("error reading ELF header");
return NULL;
}
|| f->header.e_ident[EI_MAG1] != ELFMAG1
|| f->header.e_ident[EI_MAG2] != ELFMAG2
|| f->header.e_ident[EI_MAG3] != ELFMAG3) {
- error_msg("not an ELF file");
+ bb_error_msg("not an ELF file");
return NULL;
}
if (f->header.e_ident[EI_CLASS] != ELFCLASSM
|| f->header.e_ident[EI_DATA] != ELFDATAM
|| f->header.e_ident[EI_VERSION] != EV_CURRENT
|| !MATCH_MACHINE(f->header.e_machine)) {
- error_msg("ELF file not for this architecture");
+ bb_error_msg("ELF file not for this architecture");
return NULL;
}
if (f->header.e_type != ET_REL) {
- error_msg("ELF file not a relocatable object");
+ bb_error_msg("ELF file not a relocatable object");
return NULL;
}
/* Read the section headers. */
if (f->header.e_shentsize != sizeof(ElfW(Shdr))) {
- error_msg("section header size mismatch: %lu != %lu",
+ bb_error_msg("section header size mismatch: %lu != %lu",
(unsigned long) f->header.e_shentsize,
(unsigned long) sizeof(ElfW(Shdr)));
return NULL;
section_headers = alloca(sizeof(ElfW(Shdr)) * shnum);
fseek(fp, f->header.e_shoff, SEEK_SET);
if (fread(section_headers, sizeof(ElfW(Shdr)), shnum, fp) != shnum) {
- perror_msg("error reading ELF section headers");
+ bb_perror_msg("error reading ELF section headers");
return NULL;
}
sec->contents = xmalloc(sec->header.sh_size);
fseek(fp, sec->header.sh_offset, SEEK_SET);
if (fread(sec->contents, sec->header.sh_size, 1, fp) != 1) {
- perror_msg("error reading ELF section data");
+ bb_perror_msg("error reading ELF section data");
return NULL;
}
} else {
#if SHT_RELM == SHT_REL
case SHT_RELA:
- error_msg("RELA relocations not supported on this architecture");
+ bb_error_msg("RELA relocations not supported on this architecture");
return NULL;
#else
case SHT_REL:
- error_msg("REL relocations not supported on this architecture");
+ bb_error_msg("REL relocations not supported on this architecture");
return NULL;
#endif
break;
}
- error_msg("can't handle sections of type %ld",
+ bb_error_msg("can't handle sections of type %ld",
(long) sec->header.sh_type);
return NULL;
}
ElfW(Sym) * sym;
if (sec->header.sh_entsize != sizeof(ElfW(Sym))) {
- error_msg("symbol size mismatch: %lu != %lu",
+ bb_error_msg("symbol size mismatch: %lu != %lu",
(unsigned long) sec->header.sh_entsize,
(unsigned long) sizeof(ElfW(Sym)));
return NULL;
/* Insert all symbols into the hash table. */
for (j = 1, ++sym; j < nsym; ++j, ++sym) {
+ ElfW(Addr) val = sym->st_value;
const char *name;
if (sym->st_name)
name = strtab + sym->st_name;
else
name = f->sections[sym->st_shndx]->name;
+#if defined(__SH5__)
+ /*
+ * For sh64 it is possible that the target of a branch
+ * requires a mode switch (32 to 16 and back again).
+ *
+ * This is implied by the lsb being set in the target
+ * address for SHmedia mode and clear for SHcompact.
+ */
+ val |= sym->st_other & 4;
+#endif
+
obj_add_symbol(f, name, j, sym->st_info, sym->st_shndx,
- sym->st_value, sym->st_size);
+ val, sym->st_size);
}
}
break;
case SHT_RELM:
if (sec->header.sh_entsize != sizeof(ElfW(RelM))) {
- error_msg("relocation entry size mismatch: %lu != %lu",
+ bb_error_msg("relocation entry size mismatch: %lu != %lu",
(unsigned long) sec->header.sh_entsize,
(unsigned long) sizeof(ElfW(RelM)));
return NULL;
sec->contents = imagebase + (sec->header.sh_addr - base);
fseek(fp, sec->header.sh_offset, SEEK_SET);
if (fread(sec->contents, sec->header.sh_size, 1, fp) != 1) {
- error_msg("error reading ELF section data: %s\n", strerror(errno));
+ bb_error_msg("error reading ELF section data: %s\n", strerror(errno));
return 0;
}
}
+#ifdef CONFIG_FEATURE_CHECK_TAINTED_MODULE
+static int obj_gpl_license(struct obj_file *f, const char **license)
+{
+ struct obj_section *sec;
+ /* This list must match *exactly* the list of allowable licenses in
+ * linux/include/linux/module.h. Checking for leading "GPL" will not
+ * work, somebody will use "GPL sucks, this is proprietary".
+ */
+ static const char *gpl_licenses[] = {
+ "GPL",
+ "GPL v2",
+ "GPL and additional rights",
+ "Dual BSD/GPL",
+ "Dual MPL/GPL",
+ };
+
+ if ((sec = obj_find_section(f, ".modinfo"))) {
+ const char *value, *ptr, *endptr;
+ ptr = sec->contents;
+ endptr = ptr + sec->header.sh_size;
+ while (ptr < endptr) {
+ if ((value = strchr(ptr, '=')) && strncmp(ptr, "license", value-ptr) == 0) {
+ int i;
+ if (license)
+ *license = value+1;
+ for (i = 0; i < sizeof(gpl_licenses)/sizeof(gpl_licenses[0]); ++i) {
+ if (strcmp(value+1, gpl_licenses[i]) == 0)
+ return(0);
+ }
+ return(2);
+ }
+ if (strchr(ptr, '\0'))
+ ptr = strchr(ptr, '\0') + 1;
+ else
+ ptr = endptr;
+ }
+ }
+ return(1);
+}
+
+#define TAINT_FILENAME "/proc/sys/kernel/tainted"
+#define TAINT_PROPRIETORY_MODULE (1<<0)
+#define TAINT_FORCED_MODULE (1<<1)
+#define TAINT_UNSAFE_SMP (1<<2)
+#define TAINT_URL "http://www.tux.org/lkml/#export-tainted"
+
+static void set_tainted(struct obj_file *f, int fd, char *m_name,
+ int kernel_has_tainted, int taint, const char *text1, const char *text2)
+{
+ char buf[80];
+ int oldval;
+ static int first = 1;
+ if (fd < 0 && !kernel_has_tainted)
+ return; /* New modutils on old kernel */
+ printf("Warning: loading %s will taint the kernel: %s%s\n",
+ m_name, text1, text2);
+ if (first) {
+ printf(" See %s for information about tainted modules\n", TAINT_URL);
+ first = 0;
+ }
+ if (fd >= 0) {
+ read(fd, buf, sizeof(buf)-1);
+ buf[sizeof(buf)-1] = '\0';
+ oldval = strtoul(buf, NULL, 10);
+ sprintf(buf, "%d\n", oldval | taint);
+ write(fd, buf, strlen(buf));
+ }
+}
+
+/* Check if loading this module will taint the kernel. */
+static void check_tainted_module(struct obj_file *f, char *m_name)
+{
+ static const char tainted_file[] = TAINT_FILENAME;
+ int fd, kernel_has_tainted;
+ const char *ptr;
+
+ kernel_has_tainted = 1;
+ if ((fd = open(tainted_file, O_RDWR)) < 0) {
+ if (errno == ENOENT)
+ kernel_has_tainted = 0;
+ else if (errno == EACCES)
+ kernel_has_tainted = 1;
+ else {
+ perror(tainted_file);
+ kernel_has_tainted = 0;
+ }
+ }
+
+ switch (obj_gpl_license(f, &ptr)) {
+ case 0:
+ break;
+ case 1:
+ set_tainted(f, fd, m_name, kernel_has_tainted, TAINT_PROPRIETORY_MODULE, "no license", "");
+ break;
+ case 2:
+ /* The module has a non-GPL license so we pretend that the
+ * kernel always has a taint flag to get a warning even on
+ * kernels without the proc flag.
+ */
+ set_tainted(f, fd, m_name, 1, TAINT_PROPRIETORY_MODULE, "non-GPL license - ", ptr);
+ break;
+ default:
+ set_tainted(f, fd, m_name, 1, TAINT_PROPRIETORY_MODULE, "Unexpected return from obj_gpl_license", "");
+ break;
+ }
+
+ if (flag_force_load)
+ set_tainted(f, fd, m_name, 1, TAINT_FORCED_MODULE, "forced load", "");
+
+ if (fd >= 0)
+ close(fd);
+}
+#else /* CONFIG_FEATURE_CHECK_TAINTED_MODULE */
+#define check_tainted_module(x, y) do { } while(0);
+#endif /* CONFIG_FEATURE_CHECK_TAINTED_MODULE */
+
+#ifdef CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS
+/* add module source, timestamp, kernel version and a symbol for the
+ * start of some sections. this info is used by ksymoops to do better
+ * debugging.
+ */
+static int
+get_module_version(struct obj_file *f, char str[STRVERSIONLEN])
+{
+#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING
+ if (get_modinfo_value(f, "kernel_version") == NULL)
+ return old_get_module_version(f, str);
+ else
+ return new_get_module_version(f, str);
+#else /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */
+ strncpy(str, "???", sizeof(str));
+ return -1;
+#endif /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */
+}
+
+/* add module source, timestamp, kernel version and a symbol for the
+ * start of some sections. this info is used by ksymoops to do better
+ * debugging.
+ */
+static void
+add_ksymoops_symbols(struct obj_file *f, const char *filename,
+ const char *m_name)
+{
+ static const char symprefix[] = "__insmod_";
+ struct obj_section *sec;
+ struct obj_symbol *sym;
+ char *name, *absolute_filename;
+ char str[STRVERSIONLEN], real[PATH_MAX];
+ int i, l, lm_name, lfilename, use_ksymtab, version;
+ struct stat statbuf;
+
+ static const char *section_names[] = {
+ ".text",
+ ".rodata",
+ ".data",
+ ".bss"
+ ".sbss"
+ };
+
+ if (realpath(filename, real)) {
+ absolute_filename = bb_xstrdup(real);
+ }
+ else {
+ int save_errno = errno;
+ bb_error_msg("cannot get realpath for %s", filename);
+ errno = save_errno;
+ perror("");
+ absolute_filename = bb_xstrdup(filename);
+ }
+
+ lm_name = strlen(m_name);
+ lfilename = strlen(absolute_filename);
+
+ /* add to ksymtab if it already exists or there is no ksymtab and other symbols
+ * are not to be exported. otherwise leave ksymtab alone for now, the
+ * "export all symbols" compatibility code will export these symbols later.
+ */
+ use_ksymtab = obj_find_section(f, "__ksymtab") || !flag_export;
+
+ if ((sec = obj_find_section(f, ".this"))) {
+ /* tag the module header with the object name, last modified
+ * timestamp and module version. worst case for module version
+ * is 0xffffff, decimal 16777215. putting all three fields in
+ * one symbol is less readable but saves kernel space.
+ */
+ l = sizeof(symprefix)+ /* "__insmod_" */
+ lm_name+ /* module name */
+ 2+ /* "_O" */
+ lfilename+ /* object filename */
+ 2+ /* "_M" */
+ 2*sizeof(statbuf.st_mtime)+ /* mtime in hex */
+ 2+ /* "_V" */
+ 8+ /* version in dec */
+ 1; /* nul */
+ name = xmalloc(l);
+ if (stat(absolute_filename, &statbuf) != 0)
+ statbuf.st_mtime = 0;
+ version = get_module_version(f, str); /* -1 if not found */
+ snprintf(name, l, "%s%s_O%s_M%0*lX_V%d",
+ symprefix, m_name, absolute_filename,
+ (int)(2*sizeof(statbuf.st_mtime)), statbuf.st_mtime,
+ version);
+ sym = obj_add_symbol(f, name, -1,
+ ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE),
+ sec->idx, sec->header.sh_addr, 0);
+ if (use_ksymtab)
+ new_add_ksymtab(f, sym);
+ }
+ free(absolute_filename);
+#ifdef _NOT_SUPPORTED_
+ /* record where the persistent data is going, same address as previous symbol */
+
+ if (f->persist) {
+ l = sizeof(symprefix)+ /* "__insmod_" */
+ lm_name+ /* module name */
+ 2+ /* "_P" */
+ strlen(f->persist)+ /* data store */
+ 1; /* nul */
+ name = xmalloc(l);
+ snprintf(name, l, "%s%s_P%s",
+ symprefix, m_name, f->persist);
+ sym = obj_add_symbol(f, name, -1, ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE),
+ sec->idx, sec->header.sh_addr, 0);
+ if (use_ksymtab)
+ new_add_ksymtab(f, sym);
+ }
+#endif /* _NOT_SUPPORTED_ */
+ /* tag the desired sections if size is non-zero */
+
+ for (i = 0; i < sizeof(section_names)/sizeof(section_names[0]); ++i) {
+ if ((sec = obj_find_section(f, section_names[i])) &&
+ sec->header.sh_size) {
+ l = sizeof(symprefix)+ /* "__insmod_" */
+ lm_name+ /* module name */
+ 2+ /* "_S" */
+ strlen(sec->name)+ /* section name */
+ 2+ /* "_L" */
+ 8+ /* length in dec */
+ 1; /* nul */
+ name = xmalloc(l);
+ snprintf(name, l, "%s%s_S%s_L%ld",
+ symprefix, m_name, sec->name,
+ (long)sec->header.sh_size);
+ sym = obj_add_symbol(f, name, -1, ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE),
+ sec->idx, sec->header.sh_addr, 0);
+ if (use_ksymtab)
+ new_add_ksymtab(f, sym);
+ }
+ }
+}
+#endif /* CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS */
+
+#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP
+static void print_load_map(struct obj_file *f)
+{
+ struct obj_symbol *sym;
+ struct obj_symbol **all, **p;
+ struct obj_section *sec;
+ int i, nsyms, *loaded;
+
+ /* Report on the section layout. */
+
+ printf("Sections: Size %-*s Align\n",
+ (int) (2 * sizeof(void *)), "Address");
+
+ for (sec = f->load_order; sec; sec = sec->load_next) {
+ int a;
+ unsigned long tmp;
+
+ for (a = -1, tmp = sec->header.sh_addralign; tmp; ++a)
+ tmp >>= 1;
+ if (a == -1)
+ a = 0;
+
+ printf("%-15s %08lx %0*lx 2**%d\n",
+ sec->name,
+ (long)sec->header.sh_size,
+ (int) (2 * sizeof(void *)),
+ (long)sec->header.sh_addr,
+ a);
+ }
+#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL
+ /* Quick reference which section indicies are loaded. */
+
+ loaded = alloca(sizeof(int) * (i = f->header.e_shnum));
+ while (--i >= 0)
+ loaded[i] = (f->sections[i]->header.sh_flags & SHF_ALLOC) != 0;
+
+ /* Collect the symbols we'll be listing. */
+
+ for (nsyms = i = 0; i < HASH_BUCKETS; ++i)
+ for (sym = f->symtab[i]; sym; sym = sym->next)
+ if (sym->secidx <= SHN_HIRESERVE
+ && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx]))
+ ++nsyms;
+
+ all = alloca(nsyms * sizeof(struct obj_symbol *));
+
+ for (i = 0, p = all; i < HASH_BUCKETS; ++i)
+ for (sym = f->symtab[i]; sym; sym = sym->next)
+ if (sym->secidx <= SHN_HIRESERVE
+ && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx]))
+ *p++ = sym;
+
+ /* And list them. */
+ printf("\nSymbols:\n");
+ for (p = all; p < all + nsyms; ++p) {
+ char type = '?';
+ unsigned long value;
+
+ sym = *p;
+ if (sym->secidx == SHN_ABS) {
+ type = 'A';
+ value = sym->value;
+ } else if (sym->secidx == SHN_UNDEF) {
+ type = 'U';
+ value = 0;
+ } else {
+ sec = f->sections[sym->secidx];
+
+ if (sec->header.sh_type == SHT_NOBITS)
+ type = 'B';
+ else if (sec->header.sh_flags & SHF_ALLOC) {
+ if (sec->header.sh_flags & SHF_EXECINSTR)
+ type = 'T';
+ else if (sec->header.sh_flags & SHF_WRITE)
+ type = 'D';
+ else
+ type = 'R';
+ }
+ value = sym->value + sec->header.sh_addr;
+ }
+
+ if (ELFW(ST_BIND) (sym->info) == STB_LOCAL)
+ type = tolower(type);
+
+ printf("%0*lx %c %s\n", (int) (2 * sizeof(void *)), value,
+ type, sym->name);
+ }
+#endif
+}
+
+#endif
extern int insmod_main( int argc, char **argv)
{
int k_crcs;
int k_new_syscalls;
int len;
- char *tmp;
+ char *tmp, *tmp1;
unsigned long m_size;
ElfW(Addr) m_addr;
- FILE *fp;
struct obj_file *f;
struct stat st;
- char m_name[FILENAME_MAX + 1] = "\0";
+ char *m_name = 0;
int exit_status = EXIT_FAILURE;
int m_has_modinfo;
#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING
int m_version;
int m_crcs;
#endif
+#ifdef CONFIG_FEATURE_CLEAN_UP
+ FILE *fp = 0;
+#else
+ FILE *fp;
+#endif
+#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP
+ int flag_print_load_map = 0;
+#endif
/* Parse any options */
- while ((opt = getopt(argc, argv, "fksvxLo:")) > 0) {
+#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP
+ while ((opt = getopt(argc, argv, "fkqsvxmLo:")) > 0) {
+#else
+ while ((opt = getopt(argc, argv, "fkqsvxLo:")) > 0) {
+#endif
switch (opt) {
case 'f': /* force loading */
flag_force_load = 1;
case 'v': /* verbose output */
flag_verbose = 1;
break;
+ case 'q': /* silent */
+ flag_quiet = 1;
+ break;
case 'x': /* do not export externs */
flag_export = 0;
break;
case 'o': /* name the output module */
- strncpy(m_name, optarg, FILENAME_MAX);
+ free(m_name);
+ m_name = bb_xstrdup(optarg);
break;
case 'L': /* Stub warning */
/* This is needed for compatibility with modprobe.
* that. So be careful and plan your life around not
* loading the same module 50 times concurrently. */
break;
+#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP
+ case 'm': /* print module load map */
+ flag_print_load_map = 1;
+ break;
+#endif
default:
- show_usage();
+ bb_show_usage();
}
}
if (argv[optind] == NULL) {
- show_usage();
+ bb_show_usage();
}
/* Grab the module name */
- if ((tmp = strrchr(argv[optind], '/')) != NULL) {
- tmp++;
- } else {
- tmp = argv[optind];
- }
+ tmp1 = bb_xstrdup(argv[optind]);
+ tmp = basename(tmp1);
len = strlen(tmp);
- if (len > 2 && tmp[len - 2] == '.' && tmp[len - 1] == 'o')
- len -= 2;
- memcpy(m_fullName, tmp, len);
- m_fullName[len]='\0';
- if (*m_name == '\0') {
- strcpy(m_name, m_fullName);
+ if (len > 2 && tmp[len - 2] == '.' && tmp[len - 1] == 'o') {
+ len-=2;
+ tmp[len] = '\0';
+ }
+
+ bb_xasprintf(&m_fullName, "%s.o", tmp);
+
+ if (!m_name) {
+ m_name = tmp;
+ } else {
+ free(tmp1);
+ tmp1 = 0; /* flag for free(m_name) before exit() */
}
- strcat(m_fullName, ".o");
/* Get a filedesc for the module. Check we we have a complete path */
if (stat(argv[optind], &st) < 0 || !S_ISREG(st.st_mode) ||
/* Hmm. Could not open it. First search under /lib/modules/`uname -r`,
* but do not error out yet if we fail to find it... */
if (uname(&myuname) == 0) {
- char module_dir[FILENAME_MAX];
+ char *module_dir;
+ char *tmdn;
char real_module_dir[FILENAME_MAX];
- snprintf (module_dir, sizeof(module_dir), "%s/%s",
- _PATH_MODULES, myuname.release);
+
+ tmdn = concat_path_file(_PATH_MODULES, myuname.release);
/* Jump through hoops in case /lib/modules/`uname -r`
* is a symlink. We do not want recursive_action to
* follow symlinks, but we do want to follow the
* /lib/modules/`uname -r` dir, So resolve it ourselves
* if it is a link... */
- if (realpath (module_dir, real_module_dir) == NULL)
- strcpy(real_module_dir, module_dir);
- recursive_action(real_module_dir, TRUE, FALSE, FALSE,
+ if (realpath (tmdn, real_module_dir) == NULL)
+ module_dir = tmdn;
+ else
+ module_dir = real_module_dir;
+ recursive_action(module_dir, TRUE, FALSE, FALSE,
check_module_name_match, 0, m_fullName);
+ free(tmdn);
}
/* Check if we have found anything yet */
- if (m_filename[0] == '\0' || ((fp = fopen(m_filename, "r")) == NULL))
+ if (m_filename == 0 || ((fp = fopen(m_filename, "r")) == NULL))
{
char module_dir[FILENAME_MAX];
+
+ free(m_filename);
+ m_filename = 0;
if (realpath (_PATH_MODULES, module_dir) == NULL)
strcpy(module_dir, _PATH_MODULES);
/* No module found under /lib/modules/`uname -r`, this
if (! recursive_action(module_dir, TRUE, FALSE, FALSE,
check_module_name_match, 0, m_fullName))
{
- if (m_filename[0] == '\0'
+ if (m_filename == 0
|| ((fp = fopen(m_filename, "r")) == NULL))
{
- error_msg("%s: no module by that name found", m_fullName);
- return EXIT_FAILURE;
+ bb_error_msg("%s: no module by that name found", m_fullName);
+ goto out;
}
} else
- error_msg_and_die("%s: no module by that name found", m_fullName);
+ bb_error_msg_and_die("%s: no module by that name found", m_fullName);
}
} else
- safe_strncpy(m_filename, argv[optind], sizeof(m_filename));
+ m_filename = bb_xstrdup(argv[optind]);
printf("Using %s\n", m_filename);
+#ifdef CONFIG_FEATURE_REALLY_NEW_MODULE_INTERFACE
+ if (create_module(NULL, 0) < 0 && errno == ENOSYS) {
+ optind--;
+ argv[optind] = m_filename;
+ return insmod_ng_main(argc - optind, argv + optind);
+ }
+#endif
+
if ((f = obj_load(fp, LOADBITS)) == NULL)
- perror_msg_and_die("Could not load the module");
+ bb_perror_msg_and_die("Could not load the module");
if (get_modinfo_value(f, "kernel_version") == NULL)
m_has_modinfo = 0;
#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING
/* Version correspondence? */
-
- if (uname(&uts_info) < 0)
- uts_info.release[0] = '\0';
- if (m_has_modinfo) {
- m_version = new_get_module_version(f, m_strversion);
- } else {
- m_version = old_get_module_version(f, m_strversion);
- if (m_version == -1) {
- error_msg("couldn't find the kernel version the module was "
- "compiled for");
- goto out;
+ if (!flag_quiet) {
+ if (uname(&uts_info) < 0)
+ uts_info.release[0] = '\0';
+ if (m_has_modinfo) {
+ m_version = new_get_module_version(f, m_strversion);
+ } else {
+ m_version = old_get_module_version(f, m_strversion);
+ if (m_version == -1) {
+ bb_error_msg("couldn't find the kernel version the module was "
+ "compiled for");
+ goto out;
+ }
}
- }
- if (strncmp(uts_info.release, m_strversion, STRVERSIONLEN) != 0) {
- if (flag_force_load) {
- error_msg("Warning: kernel-module version mismatch\n"
- "\t%s was compiled for kernel version %s\n"
- "\twhile this kernel is version %s",
- m_filename, m_strversion, uts_info.release);
- } else {
- error_msg("kernel-module version mismatch\n"
- "\t%s was compiled for kernel version %s\n"
- "\twhile this kernel is version %s.",
- m_filename, m_strversion, uts_info.release);
- goto out;
+ if (strncmp(uts_info.release, m_strversion, STRVERSIONLEN) != 0) {
+ if (flag_force_load) {
+ bb_error_msg("Warning: kernel-module version mismatch\n"
+ "\t%s was compiled for kernel version %s\n"
+ "\twhile this kernel is version %s",
+ m_filename, m_strversion, uts_info.release);
+ } else {
+ bb_error_msg("kernel-module version mismatch\n"
+ "\t%s was compiled for kernel version %s\n"
+ "\twhile this kernel is version %s.",
+ m_filename, m_strversion, uts_info.release);
+ goto out;
+ }
}
}
k_crcs = 0;
goto out;
k_crcs = new_is_kernel_checksummed();
#else
- error_msg("Not configured to support new kernels");
+ bb_error_msg("Not configured to support new kernels");
goto out;
#endif
} else {
goto out;
k_crcs = old_is_kernel_checksummed();
#else
- error_msg("Not configured to support old kernels");
+ bb_error_msg("Not configured to support old kernels");
goto out;
#endif
}
goto out;
}
obj_allocate_commons(f);
+ check_tainted_module(f, m_name);
/* done with the module name, on to the optional var=value arguments */
++optind;
arch_create_got(f);
hide_special_symbols(f);
+#ifdef CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS
+ add_ksymoops_symbols(f, m_filename, m_name);
+#endif /* CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS */
+
if (k_new_syscalls)
new_create_module_ksymtab(f);
m_addr = create_module(m_name, m_size);
- if (m_addr==-1) switch (errno) {
+ if (m_addr == -1) switch (errno) {
case EEXIST:
- error_msg("A module named %s already exists", m_name);
+ bb_error_msg("A module named %s already exists", m_name);
goto out;
case ENOMEM:
- error_msg("Can't allocate kernel memory for module; needed %lu bytes",
+ bb_error_msg("Can't allocate kernel memory for module; needed %lu bytes",
m_size);
goto out;
default:
- perror_msg("create_module: %s", m_name);
+ bb_perror_msg("create_module: %s", m_name);
goto out;
}
goto out;
}
+#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP
+ if(flag_print_load_map)
+ print_load_map(f);
+#endif
+
exit_status = EXIT_SUCCESS;
out:
+#ifdef CONFIG_FEATURE_CLEAN_UP
+ if(fp)
fclose(fp);
+ if(tmp1) {
+ free(tmp1);
+ } else {
+ free(m_name);
+ }
+ free(m_filename);
+#endif
return(exit_status);
}