[PATCH] fix module loading with 32-bit modutils
Hi,
As noticed by various people before, it is currently not possible to
load kernel modules when using a 32 bit distribution with an amd64
(2.4.x) kernel, nor is it possible to install 64 bit modutils if you
want to be able to fall back to a 32 bit kernel.
The problem is fixed on all other 64 bit architectures, so it is
relatively straightforward to port this. The kernel changes
are simply copied from sparc64 and work fine.
For the modutils, I had to adapt the module_base() that was previously
defined only for ppc64. That hack is not really nice but it works
as long as kernel symbols never appear below 0xffffffff00000000
(i.e. outside of the top 4GB).
Arnd <><
--- linux-2.4.23-pre3/arch/x86_64/ia32/sys_ia32.c 2003-09-01 22:06:19.000000000 +0000
+++ linux-2.4.23-pre3-x86_64/arch/x86_64/ia32/sys_ia32.c 2003-09-18 00:49:29.000000000 +0000
@@ -2534,16 +2555,421 @@
}
#endif
-long sys32_module_warning(void)
-{
- static long warn_time = -(60*HZ);
- if (time_before(warn_time + 60*HZ,jiffies) && strcmp(current->comm,"klogd")) {
- printk(KERN_INFO "%s: 32bit modutils not supported on 64bit kernel\n",
- current->comm);
- warn_time = jiffies;
- }
- return -ENOSYS ;
-}
+#ifdef CONFIG_MODULES
+
+extern asmlinkage unsigned long sys_create_module(const char *name_user, size_t size);
+
+asmlinkage unsigned long sys32_create_module(const char *name_user, __kernel_size_t32 size)
+{
+ return sys_create_module(name_user, (size_t)size);
+}
+
+extern asmlinkage int sys_init_module(const char *name_user, struct module *mod_user);
+
+/* Hey, when you're trying to init module, take time and prepare us a nice 64bit
+ * module structure, even if from 32bit modutils... Why to pollute kernel... :))
+ */
+asmlinkage int sys32_init_module(const char *name_user, struct module *mod_user)
+{
+ return sys_init_module(name_user, mod_user);
+}
+
+extern asmlinkage int sys_delete_module(const char *name_user);
+
+asmlinkage int sys32_delete_module(const char *name_user)
+{
+ return sys_delete_module(name_user);
+}
+
+struct module_info32 {
+ u32 addr;
+ u32 size;
+ u32 flags;
+ s32 usecount;
+};
+
+/* Query various bits about modules. */
+
+static inline long
+get_mod_name(const char *user_name, char **buf)
+{
+ unsigned long page;
+ long retval;
+
+ if ((unsigned long)user_name >= TASK_SIZE
+ && !segment_eq(get_fs (), KERNEL_DS))
+ return -EFAULT;
+
+ page = __get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ retval = strncpy_from_user((char *)page, user_name, PAGE_SIZE);
+ if (retval > 0) {
+ if (retval < PAGE_SIZE) {
+ *buf = (char *)page;
+ return retval;
+ }
+ retval = -ENAMETOOLONG;
+ } else if (!retval)
+ retval = -EINVAL;
+
+ free_page(page);
+ return retval;
+}
+
+static inline void
+put_mod_name(char *buf)
+{
+ free_page((unsigned long)buf);
+}
+
+static __inline__ struct module *find_module(const char *name)
+{
+ struct module *mod;
+
+ for (mod = module_list; mod ; mod = mod->next) {
+ if (mod->flags & MOD_DELETED)
+ continue;
+ if (!strcmp(mod->name, name))
+ break;
+ }
+
+ return mod;
+}
+
+static int
+qm_modules(char *buf, size_t bufsize, __kernel_size_t32 *ret)
+{
+ struct module *mod;
+ size_t nmod, space, len;
+
+ nmod = space = 0;
+
+ for (mod = module_list; mod->next != NULL; mod = mod->next, ++nmod) {
+ len = strlen(mod->name)+1;
+ if (len > bufsize)
+ goto calc_space_needed;
+ if (copy_to_user(buf, mod->name, len))
+ return -EFAULT;
+ buf += len;
+ bufsize -= len;
+ space += len;
+ }
+
+ if (put_user(nmod, ret))
+ return -EFAULT;
+ else
+ return 0;
+
+calc_space_needed:
+ space += len;
+ while ((mod = mod->next)->next != NULL)
+ space += strlen(mod->name)+1;
+
+ if (put_user(space, ret))
+ return -EFAULT;
+ else
+ return -ENOSPC;
+}
+
+static int
+qm_deps(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret)
+{
+ size_t i, space, len;
+
+ if (mod->next == NULL)
+ return -EINVAL;
+ if (!MOD_CAN_QUERY(mod))
+ return put_user(0, ret);
+
+ space = 0;
+ for (i = 0; i < mod->ndeps; ++i) {
+ const char *dep_name = mod->deps[i].dep->name;
+
+ len = strlen(dep_name)+1;
+ if (len > bufsize)
+ goto calc_space_needed;
+ if (copy_to_user(buf, dep_name, len))
+ return -EFAULT;
+ buf += len;
+ bufsize -= len;
+ space += len;
+ }
+
+ return put_user(i, ret);
+
+calc_space_needed:
+ space += len;
+ while (++i < mod->ndeps)
+ space += strlen(mod->deps[i].dep->name)+1;
+
+ if (put_user(space, ret))
+ return -EFAULT;
+ else
+ return -ENOSPC;
+}
+
+static int
+qm_refs(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret)
+{
+ size_t nrefs, space, len;
+ struct module_ref *ref;
+
+ if (mod->next == NULL)
+ return -EINVAL;
+ if (!MOD_CAN_QUERY(mod))
+ if (put_user(0, ret))
+ return -EFAULT;
+ else
+ return 0;
+
+ space = 0;
+ for (nrefs = 0, ref = mod->refs; ref ; ++nrefs, ref = ref->next_ref) {
+ const char *ref_name = ref->ref->name;
+
+ len = strlen(ref_name)+1;
+ if (len > bufsize)
+ goto calc_space_needed;
+ if (copy_to_user(buf, ref_name, len))
+ return -EFAULT;
+ buf += len;
+ bufsize -= len;
+ space += len;
+ }
+
+ if (put_user(nrefs, ret))
+ return -EFAULT;
+ else
+ return 0;
+
+calc_space_needed:
+ space += len;
+ while ((ref = ref->next_ref) != NULL)
+ space += strlen(ref->ref->name)+1;
+
+ if (put_user(space, ret))
+ return -EFAULT;
+ else
+ return -ENOSPC;
+}
+
+static inline int
+qm_symbols(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret)
+{
+ size_t i, space, len;
+ struct module_symbol *s;
+ char *strings;
+ unsigned *vals;
+
+ if (!MOD_CAN_QUERY(mod))
+ if (put_user(0, ret))
+ return -EFAULT;
+ else
+ return 0;
+
+ space = mod->nsyms * 2*sizeof(u32);
+
+ i = len = 0;
+ s = mod->syms;
+
+ if (space > bufsize)
+ goto calc_space_needed;
+
+ if (!access_ok(VERIFY_WRITE, buf, space))
+ return -EFAULT;
+
+ bufsize -= space;
+ vals = (unsigned *)buf;
+ strings = buf+space;
+
+ for (; i < mod->nsyms ; ++i, ++s, vals += 2) {
+ len = strlen(s->name)+1;
+ if (len > bufsize)
+ goto calc_space_needed;
+
+ if (copy_to_user(strings, s->name, len)
+ || __put_user(s->value, vals+0)
+ || __put_user(space, vals+1))
+ return -EFAULT;
+
+ strings += len;
+ bufsize -= len;
+ space += len;
+ }
+
+ if (put_user(i, ret))
+ return -EFAULT;
+ else
+ return 0;
+
+calc_space_needed:
+ for (; i < mod->nsyms; ++i, ++s)
+ space += strlen(s->name)+1;
+
+ if (put_user(space, ret))
+ return -EFAULT;
+ else
+ return -ENOSPC;
+}
+
+static inline int
+qm_info(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret)
+{
+ int error = 0;
+
+ if (mod->next == NULL)
+ return -EINVAL;
+
+ if (sizeof(struct module_info32) <= bufsize) {
+ struct module_info32 info;
+ info.addr = (unsigned long)mod;
+ info.size = mod->size;
+ info.flags = mod->flags;
+ info.usecount =
+ ((mod_member_present(mod, can_unload)
+ && mod->can_unload)
+ ? -1 : atomic_read(&mod->uc.usecount));
+
+ if (copy_to_user(buf, &info, sizeof(struct module_info32)))
+ return -EFAULT;
+ } else
+ error = -ENOSPC;
+
+ if (put_user(sizeof(struct module_info32), ret))
+ return -EFAULT;
+
+ return error;
+}
+
+asmlinkage int sys32_query_module(char *name_user, int which, char *buf, __kernel_size_t32 bufsize, u32 ret)
+{
+ struct module *mod;
+ int err;
+
+ lock_kernel();
+ if (name_user == 0) {
+ /* This finds "kernel_module" which is not exported. */
+ for(mod = module_list; mod->next != NULL; mod = mod->next)
+ ;
+ } else {
+ long namelen;
+ char *name;
+
+ if ((namelen = get_mod_name(name_user, &name)) < 0) {
+ err = namelen;
+ goto out;
+ }
+ err = -ENOENT;
+ if (namelen == 0) {
+ /* This finds "kernel_module" which is not exported. */
+ for(mod = module_list; mod->next != NULL; mod = mod->next)
+ ;
+ } else if ((mod = find_module(name)) == NULL) {
+ put_mod_name(name);
+ goto out;
+ }
+ put_mod_name(name);
+ }
+
+ switch (which)
+ {
+ case 0:
+ err = 0;
+ break;
+ case QM_MODULES:
+ err = qm_modules(buf, bufsize, (__kernel_size_t32 *)AA(ret));
+ break;
+ case QM_DEPS:
+ err = qm_deps(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret));
+ break;
+ case QM_REFS:
+ err = qm_refs(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret));
+ break;
+ case QM_SYMBOLS:
+ err = qm_symbols(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret));
+ break;
+ case QM_INFO:
+ err = qm_info(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret));
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+out:
+ unlock_kernel();
+ return err;
+}
+
+struct kernel_sym32 {
+ u32 value;
+ char name[60];
+};
+
+extern asmlinkage int sys_get_kernel_syms(struct kernel_sym *table);
+
+asmlinkage int sys32_get_kernel_syms(struct kernel_sym32 *table)
+{
+ int len, i;
+ struct kernel_sym *tbl;
+ mm_segment_t old_fs;
+
+ len = sys_get_kernel_syms(NULL);
+ if (!table) return len;
+ tbl = kmalloc (len * sizeof (struct kernel_sym), GFP_KERNEL);
+ if (!tbl) return -ENOMEM;
+ old_fs = get_fs();
+ set_fs (KERNEL_DS);
+ sys_get_kernel_syms(tbl);
+ set_fs (old_fs);
+ for (i = 0; i < len; i++, table++) {
+ if (put_user (tbl[i].value, &table->value) ||
+ copy_to_user (table->name, tbl[i].name, 60))
+ break;
+ }
+ kfree (tbl);
+ return i;
+}
+
+#else /* CONFIG_MODULES */
+
+asmlinkage unsigned long
+sys32_create_module(const char *name_user, size_t size)
+{
+ return -ENOSYS;
+}
+
+asmlinkage int
+sys32_init_module(const char *name_user, struct module *mod_user)
+{
+ return -ENOSYS;
+}
+
+asmlinkage int
+sys32_delete_module(const char *name_user)
+{
+ return -ENOSYS;
+}
+
+asmlinkage int
+sys32_query_module(const char *name_user, int which, char *buf, size_t bufsize,
+ size_t *ret)
+{
+ /* Let the program know about the new interface. Not that
+ it'll do them much good. */
+ if (which == 0)
+ return 0;
+
+ return -ENOSYS;
+}
+
+asmlinkage int
+sys32_get_kernel_syms(struct kernel_sym *table)
+{
+ return -ENOSYS;
+}
+
+#endif /* CONFIG_MODULES */
long sys32_vm86_warning(void)
{
--- linux-2.4.23-pre3/arch/x86_64/ia32/ia32entry.S 2003-09-01 22:06:19.000000000 +0000
+++ linux-2.4.23-pre3-x86_64/arch/x86_64/ia32/ia32entry.S 2003-09-17 20:37:53.000000000 +0000
@@ -256,10 +256,10 @@
.quad sys32_adjtimex
.quad sys32_mprotect /* 125 */
.quad sys32_sigprocmask
- .quad sys32_module_warning /* create_module */
- .quad sys32_module_warning /* init_module */
- .quad sys32_module_warning /* delete module */
- .quad sys32_module_warning /* 130 get_kernel_syms */
+ .quad sys32_create_module
+ .quad sys32_init_module
+ .quad sys32_delete_module
+ .quad sys32_get_kernel_syms /* 130 */
.quad ni_syscall /* quotactl */
.quad sys_getpgid
.quad sys_fchdir
@@ -296,7 +296,7 @@
.quad sys_setresuid16
.quad sys_getresuid16 /* 165 */
.quad sys32_vm86_warning /* vm86 */
- .quad quiet_ni_syscall /* query_module */
+ .quad sys32_query_module
.quad sys_poll
.quad sys32_nfsservctl
.quad sys_setresgid16 /* 170 */
diff -ur modutils-2.4.25/configure modutils-2.4.25-amd64/configure
--- modutils-2.4.25/configure 2003-03-29 00:08:42.000000000 +0000
+++ modutils-2.4.25-amd64/configure 2003-09-18 00:59:08.000000000 +0000
@@ -57,6 +57,10 @@
modules (default on s390 is yes, default on other
architectures is no)"
ac_help="$ac_help
+ --enable-common-i386 Make one executable handle 32 and 64 bit kernel
+ modules (default on i386 is yes, default on other
+ architectures is no)"
+ac_help="$ac_help
--enable-strip Strip binaries during install. Default is no
on IA64 (ski does not like stripped binaries),
yes on other architectures"
@@ -903,6 +907,24 @@
esac
+COMMON_i386=yes
+# Check whether --enable-common-i386 or --disable-common-i386 was given.
+if test "${enable_common_i386+set}" = set; then
+ enableval="$enable_common_i386"
+ if test "$enableval" = "yes"; then
+ COMMON_i386=yes
+ else
+ COMMON_i386=no
+fi
+fi
+
+case "$ARCH" in
+ x86_64) if test "$COMMON_i386" = "yes"; then ARCH="i386"; fi ;;
+ i386*) ;;
+ *) COMMON_i386=no ;;
+esac
+
+
case "$ARCH" in
@@ -1673,6 +1695,7 @@
s%@COMMON_hppa@%$COMMON_hppa%g
s%@COMMON_ppc@%$COMMON_ppc%g
s%@COMMON_s390@%$COMMON_s390%g
+s%@COMMON_i386@%$COMMON_i386%g
s%@ARCH@%$ARCH%g
s%@STRIP@%$STRIP%g
s%@USE_SYSCALL@%$USE_SYSCALL%g
diff -ur modutils-2.4.25/configure.in modutils-2.4.25-amd64/configure.in
--- modutils-2.4.25/configure.in 2003-03-29 00:08:42.000000000 +0000
+++ modutils-2.4.25-amd64/configure.in 2003-09-18 00:58:28.000000000 +0000
@@ -190,6 +190,23 @@
esac
AC_SUBST(COMMON_s390)
+COMMON_i386=yes
+AC_ARG_ENABLE(common-i386,
+[ --enable-common-i386 Make one executable handle 32 and 64 bit kernel
+ modules (default on i386 is yes, default on other
+ architectures is no)],
+[if test "$enableval" = "yes"; then
+ COMMON_i386=yes
+ else
+ COMMON_i386=no
+fi])
+case "$ARCH" in
+ x86_64) if test "$COMMON_i386" = "yes"; then ARCH="i386"; fi ;;
+ i386*) ;;
+ *) COMMON_i386=no ;;
+esac
+AC_SUBST(COMMON_i386)
+
AC_SUBST(ARCH)
case "$ARCH" in
diff -ur modutils-2.4.25/depmod/Makefile.in modutils-2.4.25-amd64/depmod/Makefile.in
--- modutils-2.4.25/depmod/Makefile.in 2003-03-28 23:54:20.000000000 +0000
+++ modutils-2.4.25-amd64/depmod/Makefile.in 2003-09-18 00:58:28.000000000 +0000
@@ -24,6 +24,10 @@
DEFS64 := -DELF_MACHINE_H='"elf_s390x.h"' -DARCH_s390x
endif
+ifeq (@COMMON_i386@,yes)
+DEFS64 := -DELF_MACHINE_H='"elf_x86_64.h"' -DARCH_x86_64
+endif
+
# arch independent definitions for common 32/64 code
ifdef DEFS64
diff -ur modutils-2.4.25/include/elf_ppc64.h modutils-2.4.25-amd64/include/elf_ppc64.h
--- modutils-2.4.25/include/elf_ppc64.h 2001-11-17 11:16:57.000000000 +0000
+++ modutils-2.4.25-amd64/include/elf_ppc64.h 2003-09-18 00:58:28.000000000 +0000
@@ -11,6 +11,7 @@
struct obj_file;
extern int ppc64_process_syms (struct obj_file *);
extern Elf64_Addr ppc64_module_base (struct obj_file *);
+#define arch_module_base ppc64_module_base
/* these ought to be eventually available in /usr/include/elf.h */
#ifndef EM_PPC64
diff -ur modutils-2.4.25/include/elf_x86_64.h modutils-2.4.25-amd64/include/elf_x86_64.h
--- modutils-2.4.25/include/elf_x86_64.h 2002-03-24 04:09:11.000000000 +0000
+++ modutils-2.4.25-amd64/include/elf_x86_64.h 2003-09-18 00:58:28.000000000 +0000
@@ -7,3 +7,5 @@
#define SHT_RELM SHT_RELA
#define Elf64_RelM Elf64_Rela
+
+#define arch_module_base(m) ((ElfW(Addr)) 0xffffffff00000000ull)
diff -ur modutils-2.4.25/include/obj.h modutils-2.4.25-amd64/include/obj.h
--- modutils-2.4.25/include/obj.h 2002-07-21 07:00:22.000000000 +0000
+++ modutils-2.4.25-amd64/include/obj.h 2003-09-18 00:58:28.000000000 +0000
@@ -259,6 +259,10 @@
int arch_archdata (struct obj_file *fin, struct obj_section *sec);
+#ifndef arch_module_base
+#define arch_module_base(m) ((ElfW(Addr))0)
+#endif
+
#define ARCHDATA_SEC_NAME "__archdata"
/* Pointers in objects can be 32 or 64 bit */
diff -ur modutils-2.4.25/insmod/Makefile.in modutils-2.4.25-amd64/insmod/Makefile.in
--- modutils-2.4.25/insmod/Makefile.in 2003-03-28 23:54:20.000000000 +0000
+++ modutils-2.4.25-amd64/insmod/Makefile.in 2003-09-18 00:58:28.000000000 +0000
@@ -46,6 +46,10 @@
DEFS64 := -DELF_MACHINE_H='"elf_s390x.h"' -DARCH_s390x
endif
+ifeq (@COMMON_i386@,yes)
+DEFS64 := -DELF_MACHINE_H='"elf_x86_64.h"' -DARCH_x86_64
+endif
+
# arch independent definitions for common 32/64 code
ifdef DEFS64
diff -ur modutils-2.4.25/insmod/insmod.c modutils-2.4.25-amd64/insmod/insmod.c
--- modutils-2.4.25/insmod/insmod.c 2003-03-23 02:34:28.000000000 +0000
+++ modutils-2.4.25-amd64/insmod/insmod.c 2003-09-18 00:58:28.000000000 +0000
@@ -648,9 +648,7 @@
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
+ dep->dep |= arch_module_base (f);
obj_symbol_patch(f, sec->idx, (char *) &dep->ref - sec->contents, tm);
dep->next_ref = 0;
++dep;
@@ -2083,9 +2081,7 @@
} else {
errno = 0;
m_addr = create_module(m_name, m_size);
-#ifdef ARCH_ppc64
- m_addr |= ppc64_module_base (f);
-#endif
+ m_addr |= arch_module_base (f);
switch (errno) {
case 0:
break;
diff -ur modutils-2.4.25/obj/Makefile.in modutils-2.4.25-amd64/obj/Makefile.in
--- modutils-2.4.25/obj/Makefile.in 2003-03-28 23:54:20.000000000 +0000
+++ modutils-2.4.25-amd64/obj/Makefile.in 2003-09-18 00:58:28.000000000 +0000
@@ -34,6 +34,12 @@
DEFS64 := -DELF_MACHINE_H='"elf_s390x.h"' -DARCH_s390x
endif
+ifeq (@COMMON_i386@,yes)
+LIBOBJ_OBJS += obj_x86_64_64.o
+LIBOBJ_SRCS += obj_x86_64.c
+DEFS64 := -DELF_MACHINE_H='"elf_x86_64.h"' -DARCH_x86_64
+endif
+
# arch independent definitions for common 32/64 code
Reply to: