summaryrefslogtreecommitdiffstats
path: root/mdk-stage1/insmod-modutils/insmod.c
diff options
context:
space:
mode:
Diffstat (limited to 'mdk-stage1/insmod-modutils/insmod.c')
-rw-r--r--mdk-stage1/insmod-modutils/insmod.c255
1 files changed, 216 insertions, 39 deletions
diff --git a/mdk-stage1/insmod-modutils/insmod.c b/mdk-stage1/insmod-modutils/insmod.c
index 359b28837..b0b7c2a0d 100644
--- a/mdk-stage1/insmod-modutils/insmod.c
+++ b/mdk-stage1/insmod-modutils/insmod.c
@@ -54,16 +54,15 @@
Keith Owens <kaos@ocs.com.au> November 2000.
Add persistent data support.
Keith Owens <kaos@ocs.com.au> November 2000.
+ Add tainted module support.
+ Keith Owens <kaos@ocs.com.au> September 2001.
*/
-#ident "$Id$"
-
#include "../insmod.h"
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
-//#include <alloca.h> provided by stdlib
#include <limits.h>
#include <ctype.h>
#include <errno.h>
@@ -71,6 +70,7 @@
#include <getopt.h>
#include <sys/stat.h>
#include <sys/file.h>
+#include <sys/fcntl.h>
#include "module.h"
#include "obj.h"
@@ -90,9 +90,12 @@ static int flag_silent_probe = 0;
static int flag_export = 1;
static int flag_load_map = 0;
static int flag_ksymoops = 1;
+static int flag_numeric_only = 0;
static int n_ext_modules_used;
static int m_has_modinfo;
+static int gplonly_seen;
+static int warnings;
extern int insmod_main(int argc, char **argv);
extern int insmod_main_32(int argc, char **argv);
@@ -105,6 +108,15 @@ extern int kallsyms_main(int argc, char **argv);
/*======================================================================*/
+/* Only use the numeric part of the version string? */
+
+static void use_numeric_only(int major, int minor, char *str)
+{
+ if (((major << 8) + minor) >= 0x0205 /* kernel 2.5 */
+ || flag_numeric_only)
+ *str = '\0';
+}
+
/* Get the kernel version in the canonical integer form. */
static int get_kernel_version(char str[STRVERSIONLEN])
@@ -112,8 +124,9 @@ static int get_kernel_version(char str[STRVERSIONLEN])
char *p, *q;
int a, b, c;
- strncpy(str, uts_info.release, STRVERSIONLEN);
- p = uts_info.release;
+ strncpy(str, uts_info.release, STRVERSIONLEN-1);
+ str[STRVERSIONLEN-1] = '\0';
+ p = str;
a = strtoul(p, &p, 10);
if (*p != '.')
@@ -124,6 +137,7 @@ static int get_kernel_version(char str[STRVERSIONLEN])
c = strtoul(p + 1, &q, 10);
if (p + 1 == q)
return -1;
+ use_numeric_only(a, b, q);
return a << 16 | b << 8 | c;
}
@@ -232,7 +246,7 @@ static unsigned long ncv_symbol_hash(const char *str)
* to the new module.
*/
static int add_symbols_from(struct obj_file *f, int idx,
- struct module_symbol *syms, size_t nsyms)
+ struct module_symbol *syms, size_t nsyms, int gpl)
{
struct module_symbol *s;
size_t i;
@@ -247,8 +261,57 @@ static int add_symbols_from(struct obj_file *f, int idx,
*/
struct obj_symbol *sym;
+ /* 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) {
+ gplonly_seen = 1;
+ if (gpl)
+ ((char *)s->name) += 8;
+ else
+ continue;
+ }
+
sym = obj_find_symbol(f, (char *) s->name);
- if (sym && !ELFW(ST_BIND) (sym->info) == STB_LOCAL) {
+#ifdef ARCH_ppc64
+ if (!sym)
+ {
+ static size_t buflen = 0;
+ static char *buf = 0;
+ int len;
+
+ /* ppc64 is one of those architectures with
+ function descriptors. A function is exported
+ and accessed across object boundaries via its
+ function descriptor. The function code symbol
+ happens to be the function name, prefixed with
+ '.', and a function call is a branch to the
+ code symbol. The linker recognises when a call
+ crosses object boundaries, and inserts a stub
+ to call via the function descriptor.
+ obj_ppc64.c of course does the same thing, so
+ here we recognise that an undefined code symbol
+ can be satisfied by the corresponding function
+ descriptor symbol. */
+
+ len = strlen ((char *) s->name) + 2;
+ if (buflen < len)
+ {
+ buflen = len + (len >> 1);
+ if (buf)
+ free (buf);
+ buf = malloc (buflen);
+ }
+ buf[0] = '.';
+ strcpy (buf + 1, (char *) s->name);
+ sym = obj_find_symbol(f, buf);
+ }
+#endif /* ARCH_ppc64 */
+
+ if (sym && ELFW(ST_BIND) (sym->info) != STB_LOCAL) {
sym = obj_add_symbol(f, (char *) s->name, -1,
ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE),
idx, s->value, 0);
@@ -264,7 +327,7 @@ static int add_symbols_from(struct obj_file *f, int idx,
return used;
}
-static void add_kernel_symbols(struct obj_file *f)
+static void add_kernel_symbols(struct obj_file *f, int gpl)
{
struct module_stat *m;
size_t i, nused = 0;
@@ -272,13 +335,13 @@ static void add_kernel_symbols(struct obj_file *f)
/* Add module symbols first. */
for (i = 0, m = module_stat; i < n_module_stat; ++i, ++m)
if (m->nsyms &&
- add_symbols_from(f, SHN_HIRESERVE + 2 + i, m->syms, m->nsyms))
+ add_symbols_from(f, SHN_HIRESERVE + 2 + i, m->syms, m->nsyms, gpl))
m->status = 1 /* used */, ++nused;
n_ext_modules_used = nused;
/* And finally the symbols from the kernel proper. */
if (nksyms)
- add_symbols_from(f, SHN_HIRESERVE + 1, ksyms, nksyms);
+ add_symbols_from(f, SHN_HIRESERVE + 1, ksyms, nksyms, gpl);
}
static void hide_special_symbols(struct obj_file *f)
@@ -326,7 +389,7 @@ static void print_load_map(struct obj_file *f)
if (a == -1)
a = 0;
- lprintf("%-16s%08lx %0*lx 2**%d",
+ lprintf("%-15s %08lx %0*lx 2**%d",
sec->name,
(long)sec->header.sh_size,
(int) (2 * sizeof(void *)),
@@ -465,9 +528,10 @@ static int old_create_mod_use_count(struct obj_file *f)
*/
got = obj_find_symbol(f, "_GLOBAL_OFFSET_TABLE_");
if (got)
-{
+ {
sec = obj_create_alloced_section(f, ".got",
- sizeof(long), sizeof(long));
+ sizeof(long), sizeof(long),
+ SHF_WRITE);
got->secidx = sec->idx; /* mark the symbol as defined */
}
return 1;
@@ -491,7 +555,8 @@ static void add_ksymtab(struct obj_file *f, struct obj_symbol *sym)
sec = NULL;
}
if (!sec)
- sec = obj_create_alloced_section(f, "__ksymtab", tgt_sizeof_void_p, 0);
+ sec = obj_create_alloced_section(f, "__ksymtab",
+ tgt_sizeof_void_p, 0, 0);
if (!sec)
return;
sec->header.sh_flags |= SHF_ALLOC;
@@ -513,9 +578,9 @@ static int create_module_ksymtab(struct obj_file *f)
struct module_ref *dep;
struct obj_symbol *tm;
- sec = obj_create_alloced_section(f, ".kmodtab", tgt_sizeof_void_p,
- (sizeof(struct module_ref)
- * n_ext_modules_used));
+ sec = obj_create_alloced_section(f, ".kmodtab",
+ tgt_sizeof_void_p,
+ sizeof(struct module_ref) * n_ext_modules_used, 0);
if (!sec)
return 0;
@@ -524,6 +589,9 @@ static int create_module_ksymtab(struct obj_file *f)
for (i = 0; i < n_module_stat; ++i)
if (module_stat[i].status /* used */) {
dep->dep = module_stat[i].addr;
+#ifdef ARCH_ppc64
+ dep->dep |= ppc64_module_base (f);
+#endif
obj_symbol_patch(f, sec->idx, (char *) &dep->ref - sec->contents, tm);
dep->next_ref = 0;
++dep;
@@ -573,7 +641,9 @@ static int get_module_version(struct obj_file *f, char str[STRVERSIONLEN])
} else
m_has_modinfo = 1;
- strncpy(str, p, STRVERSIONLEN);
+ strncpy(str, p, STRVERSIONLEN-1);
+ str[STRVERSIONLEN-1] = '\0';
+ p = str;
a = strtoul(p, &p, 10);
if (*p != '.')
@@ -584,6 +654,7 @@ static int get_module_version(struct obj_file *f, char str[STRVERSIONLEN])
c = strtoul(p + 1, &q, 10);
if (p + 1 == q)
return -1;
+ use_numeric_only(a, b, q);
return a << 16 | b << 8 | c;
}
@@ -645,6 +716,7 @@ static void add_ksymoops_symbols(struct obj_file *f, const char *filename,
int save_errno = errno;
error("cannot get realpath for %s", filename);
errno = save_errno;
+ perror("");
absolute_filename = xstrdup(filename);
}
@@ -679,7 +751,7 @@ static void add_ksymoops_symbols(struct obj_file *f, const char *filename,
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,
- 2*sizeof(statbuf.st_mtime), statbuf.st_mtime,
+ (int)(2*sizeof(statbuf.st_mtime)), statbuf.st_mtime,
version);
sym = obj_add_symbol(f, name, -1,
ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE),
@@ -756,14 +828,10 @@ static int process_module_arguments(struct obj_file *f, int argc, char **argv, i
memcpy(key + 5, *argv, n);
key[n + 5] = '\0';
if ((fmt = get_modinfo_value(f, key)) == NULL) {
- if (required) {
- error("invalid parameter %s", key);
- return 0;
- }
- else {
- if (flag_verbose)
- lprintf("ignoring %s", *argv);
- continue; /* silently ignore optional parameters */
+ if (required || flag_verbose) {
+ lprintf("Warning: ignoring %s, no such parameter in this module", *argv);
+ ++warnings;
+ continue;
}
}
key += 5;
@@ -1014,7 +1082,7 @@ static int add_kallsyms(struct obj_file *f,
}
}
if (!*module_kallsyms)
- *module_kallsyms = obj_create_alloced_section(f, KALLSYMS_SEC_NAME, 0, 0);
+ *module_kallsyms = obj_create_alloced_section(f, KALLSYMS_SEC_NAME, 0, 0, 0);
/* Size and populate kallsyms */
if (obj_kallsyms(f, &f_kallsyms))
@@ -1046,7 +1114,7 @@ static int add_archdata(struct obj_file *f,
}
}
if (!*sec)
- *sec = obj_create_alloced_section(f, ARCHDATA_SEC_NAME, 16, 0);
+ *sec = obj_create_alloced_section(f, ARCHDATA_SEC_NAME, 16, 0, 0);
/* Size and populate archdata */
if (arch_archdata(f, *sec))
@@ -1147,7 +1215,8 @@ static int init_module(const char *m_name, struct obj_file *f,
if (ret) {
error("init_module: %m");
lprintf("Hint: insmod errors can be caused by incorrect module parameters, "
- "including invalid IO or IRQ parameters");
+ "including invalid IO or IRQ parameters.\n"
+ " You may find more information in syslog or the output from dmesg");
}
}
@@ -1276,6 +1345,7 @@ static int check_module_parameter(struct obj_file *f, char *key, char *value, in
* most error conditions. Make these all errors in 2.5.
*/
lprintf("Warning: %s symbol for parameter %s not found", error_file, key);
+ ++warnings;
return(1);
}
@@ -1290,6 +1360,7 @@ static int check_module_parameter(struct obj_file *f, char *key, char *value, in
if (max < min) {
lprintf("Warning: %s parameter %s has max < min!", error_file, key);
+ ++warnings;
return(1);
}
@@ -1297,6 +1368,7 @@ static int check_module_parameter(struct obj_file *f, char *key, char *value, in
case 'c':
if (!isdigit(p[1])) {
lprintf("%s parameter %s has no size after 'c'!", error_file, key);
+ ++warnings;
return(1);
}
while (isdigit(p[1]))
@@ -1310,9 +1382,11 @@ static int check_module_parameter(struct obj_file *f, char *key, char *value, in
break;
case '\0':
lprintf("%s parameter %s has no format character!", error_file, key);
+ ++warnings;
return(1);
default:
lprintf("%s parameter %s has unknown format character '%c'", error_file, key, *p);
+ ++warnings;
return(1);
}
switch (*++p) {
@@ -1327,6 +1401,7 @@ static int check_module_parameter(struct obj_file *f, char *key, char *value, in
break;
default:
lprintf("%s parameter %s has unknown format modifier '%c'", error_file, key, *p);
+ ++warnings;
return(1);
}
return(0);
@@ -1374,6 +1449,74 @@ static void check_module_parameters(struct obj_file *f, int *persist_flag)
return;
}
+static void set_tainted(struct obj_file *f, int fd, int kernel_has_tainted,
+ int noload, 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 */
+ lprintf("Warning: loading %s will taint the kernel: %s%s",
+ f->filename, text1, text2);
+ ++warnings;
+ if (first) {
+ lprintf(" See %s for information about tainted modules", TAINT_URL);
+ first = 0;
+ }
+ if (fd >= 0 && !noload) {
+ 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, int noload)
+{
+ static const char tainted_file[] = TAINT_FILENAME;
+ int fd, kernel_has_tainted;
+ const char *ptr;
+
+ 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;
+ }
+ }
+ else
+ kernel_has_tainted = 1;
+
+ switch (obj_gpl_license(f, &ptr)) {
+ case 0:
+ break;
+ case 1:
+ set_tainted(f, fd, kernel_has_tainted, noload, 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, 1, noload, TAINT_PROPRIETORY_MODULE, "non-GPL license - ", ptr);
+ break;
+ default:
+ set_tainted(f, fd, 1, noload, TAINT_PROPRIETORY_MODULE, "Unexpected return from obj_gpl_license", "");
+ break;
+ }
+
+ if (flag_force_load)
+ set_tainted(f, fd, 1, noload, TAINT_FORCED_MODULE, "forced load", "");
+ if (fd >= 0)
+ close(fd);
+}
/* For common 3264 code, only compile the usage message once, in the 64 bit version */
#if defined(COMMON_3264) && defined(ONLY_32)
@@ -1382,7 +1525,7 @@ extern void insmod_usage(void); /* Use the copy in the 64 bit version */
void insmod_usage(void)
{
fputs("Usage:\n"
- "insmod [-fhkLmnpqrsSvVxXyY] [-e persist_name] [-o module_name] [-O blob_name] [-P prefix] module [ symbol=value ... ]\n"
+ "insmod [-fhkLmnpqrsSvVxXyYN] [-e persist_name] [-o module_name] [-O blob_name] [-P prefix] module [ symbol=value ... ]\n"
"\n"
" module Name of a loadable kernel module ('.o' can be omitted)\n"
" -f, --force Force loading under wrong kernel version\n"
@@ -1402,6 +1545,7 @@ void insmod_usage(void)
" -X, --export Do export externs (default)\n"
" -y, --noksymoops Do not add ksymoops symbols\n"
" -Y, --ksymoops Do add ksymoops symbols (default)\n"
+ " -N, --numeric-only Only check the numeric part of the kernel version\n"
" -e persist_name\n"
" --persist=persist_name Filename to hold any persistent data from the module\n"
" -o NAME, --name=NAME Set internal module name to NAME\n"
@@ -1445,8 +1589,8 @@ int INSMOD_MAIN(int argc, char **argv)
{"export", 0, 0, 'X'},
{"noksymoops", 0, 0, 'y'},
{"ksymoops", 0, 0, 'Y'},
-
{"persist", 1, 0, 'e'},
+ {"numeric-only", 1, 0, 'N'},
{"name", 1, 0, 'o'},
{"blob", 1, 0, 'O'},
{"prefix", 1, 0, 'P'},
@@ -1472,6 +1616,7 @@ int INSMOD_MAIN(int argc, char **argv)
int force_kallsyms = 0;
int persist_parms = 0; /* does module have persistent parms? */
int i;
+ int gpl;
error_file = "insmod";
@@ -1479,7 +1624,7 @@ int INSMOD_MAIN(int argc, char **argv)
errors = optind = 0;
/* Process the command line. */
- while ((o = getopt_long(argc, argv, "fhkLmnpqrsSvVxXyYe:o:O:P:R:",
+ while ((o = getopt_long(argc, argv, "fhkLmnpqrsSvVxXyYNe:o:O:P:R:",
&long_opts[0], NULL)) != EOF)
switch (o) {
case 'f': /* force loading */
@@ -1533,6 +1678,9 @@ int INSMOD_MAIN(int argc, char **argv)
case 'Y': /* do define ksymoops symbols */
flag_ksymoops = 1;
break;
+ case 'N': /* only check numeric part of kernel version */
+ flag_numeric_only = 1;
+ break;
case 'e': /* persistent data filename */
free(persist_name);
@@ -1566,8 +1714,10 @@ int INSMOD_MAIN(int argc, char **argv)
(!persistdir || !*persistdir)) {
free(persist_name);
persist_name = NULL;
- if (flag_verbose)
+ if (flag_verbose) {
lprintf("insmod: -e \"\" ignored, no persistdir");
+ ++warnings;
+ }
}
if (m_name == NULL) {
@@ -1624,7 +1774,7 @@ int INSMOD_MAIN(int argc, char **argv)
*/
set_ncv_prefix(NULL);
- for (i = 0; i < n_module_stat; ++i) {
+ for (i = 0; !noload && i < n_module_stat; ++i) {
if (strcmp(module_stat[i].name, m_name) == 0) {
error("a module named %s already exists", m_name);
goto out;
@@ -1652,6 +1802,7 @@ int INSMOD_MAIN(int argc, char **argv)
"\t%s was compiled for kernel version %s\n"
"\twhile this kernel is version %s",
filename, m_strversion, k_strversion);
+ ++warnings;
} else {
if (!quiet)
error("kernel-module version mismatch\n"
@@ -1665,7 +1816,13 @@ int INSMOD_MAIN(int argc, char **argv)
obj_set_symbol_compare(f, ncv_strcmp, ncv_symbol_hash);
/* Let the module know about the kernel symbols. */
- add_kernel_symbols(f);
+ gpl = obj_gpl_license(f, NULL) == 0;
+ add_kernel_symbols(f, gpl);
+
+#ifdef ARCH_ppc64
+ if (!ppc64_process_syms (f))
+ goto out;
+#endif
/* Allocate common symbols, symbol tables, and string tables.
*
@@ -1684,17 +1841,33 @@ int INSMOD_MAIN(int argc, char **argv)
goto out;
#endif
- if (!obj_check_undefineds(f, quiet)) /* DEPMOD, obj_clear_undefineds */
+ arch_create_got(f); /* DEPMOD */
+ if (!obj_check_undefineds(f, quiet)) { /* DEPMOD, obj_clear_undefineds */
+ if (!gpl && !quiet) {
+ if (gplonly_seen)
+ error("\n"
+ "Hint: You are trying to load a module without a GPL compatible license\n"
+ " and it has unresolved symbols. The module may be trying to access\n"
+ " GPLONLY symbols but the problem is more likely to be a coding or\n"
+ " user error. Contact the module supplier for assistance, only they\n"
+ " can help you.\n");
+ else
+ error("\n"
+ "Hint: You are trying to load a module without a GPL compatible license\n"
+ " and it has unresolved symbols. Contact the module supplier for\n"
+ " assistance, only they can help you.\n");
+ }
goto out;
+ }
obj_allocate_commons(f); /* DEPMOD */
check_module_parameters(f, &persist_parms);
+ check_tainted_module(f, noload);
if (optind < argc) {
if (!process_module_arguments(f, argc - optind, argv + optind, 1))
goto out;
}
- arch_create_got(f); /* DEPMOD */
hide_special_symbols(f);
if (persist_parms && persist_name && *persist_name) {
@@ -1842,6 +2015,9 @@ int INSMOD_MAIN(int argc, char **argv)
} else {
errno = 0;
m_addr = create_module(m_name, m_size);
+#ifdef ARCH_ppc64
+ m_addr |= ppc64_module_base (f);
+#endif
switch (errno) {
case 0:
break;
@@ -1917,6 +2093,8 @@ int INSMOD_MAIN(int argc, char **argv)
delete_module(m_name);
goto out;
}
+ if (warnings && !noload)
+ lprintf("Module %s loaded, with warnings", m_name);
exit_status = 0;
out:
@@ -1941,7 +2119,6 @@ int insmod_main(int argc, char **argv)
#endif /* defined(COMMON_3264) && defined(ONLY_64) */
-
int insmod_call(char * full_filename, char * params)
{
int argc = 2;