Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0bbb2753 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux-2.6-for-linus:
  module: convert to stop_machine_create/destroy.
  stop_machine: introduce stop_machine_create/destroy.
  parisc: fix module loading failure of large kernel modules
  module: fix module loading failure of large kernel modules for parisc
  module: fix warning of unused function when !CONFIG_PROC_FS
  kernel/module.c: compare symbol values when marking symbols as exported in /proc/kallsyms.
  remove CONFIG_KMOD
parents 0578c3b4 9e01892c
Loading
Loading
Loading
Loading
+4 −2
Original line number Original line Diff line number Diff line
@@ -23,8 +23,10 @@ struct mod_arch_specific
{
{
	unsigned long got_offset, got_count, got_max;
	unsigned long got_offset, got_count, got_max;
	unsigned long fdesc_offset, fdesc_count, fdesc_max;
	unsigned long fdesc_offset, fdesc_count, fdesc_max;
	unsigned long stub_offset, stub_count, stub_max;
	struct {
	unsigned long init_stub_offset, init_stub_count, init_stub_max;
		unsigned long stub_offset;
		unsigned int stub_entries;
		} *section;
	int unwind_section;
	int unwind_section;
	struct unwind_table *unwind;
	struct unwind_table *unwind;
};
};
+135 −81
Original line number Original line Diff line number Diff line
@@ -6,6 +6,7 @@
 *
 *
 *    Linux/PA-RISC Project (http://www.parisc-linux.org/)
 *    Linux/PA-RISC Project (http://www.parisc-linux.org/)
 *    Copyright (C) 2003 Randolph Chung <tausq at debian . org>
 *    Copyright (C) 2003 Randolph Chung <tausq at debian . org>
 *    Copyright (C) 2008 Helge Deller <deller@gmx.de>
 *
 *
 *
 *
 *    This program is free software; you can redistribute it and/or modify
 *    This program is free software; you can redistribute it and/or modify
@@ -24,6 +25,19 @@
 *
 *
 *
 *
 *    Notes:
 *    Notes:
 *    - PLT stub handling
 *      On 32bit (and sometimes 64bit) and with big kernel modules like xfs or
 *      ipv6 the relocation types R_PARISC_PCREL17F and R_PARISC_PCREL22F may
 *      fail to reach their PLT stub if we only create one big stub array for
 *      all sections at the beginning of the core or init section.
 *      Instead we now insert individual PLT stub entries directly in front of
 *      of the code sections where the stubs are actually called.
 *      This reduces the distance between the PCREL location and the stub entry
 *      so that the relocations can be fulfilled.
 *      While calculating the final layout of the kernel module in memory, the
 *      kernel module loader calls arch_mod_section_prepend() to request the
 *      to be reserved amount of memory in front of each individual section.
 *
 *    - SEGREL32 handling
 *    - SEGREL32 handling
 *      We are not doing SEGREL32 handling correctly. According to the ABI, we
 *      We are not doing SEGREL32 handling correctly. According to the ABI, we
 *      should do a value offset, like this:
 *      should do a value offset, like this:
@@ -58,9 +72,13 @@
#define DEBUGP(fmt...)
#define DEBUGP(fmt...)
#endif
#endif


#define RELOC_REACHABLE(val, bits) \
	(( ( !((val) & (1<<((bits)-1))) && ((val)>>(bits)) != 0 )  ||	\
	     ( ((val) & (1<<((bits)-1))) && ((val)>>(bits)) != (((__typeof__(val))(~0))>>((bits)+2)))) ? \
	0 : 1)

#define CHECK_RELOC(val, bits) \
#define CHECK_RELOC(val, bits) \
	if ( ( !((val) & (1<<((bits)-1))) && ((val)>>(bits)) != 0 )  ||	\
	if (!RELOC_REACHABLE(val, bits)) { \
	     ( ((val) & (1<<((bits)-1))) && ((val)>>(bits)) != (((__typeof__(val))(~0))>>((bits)+2)))) { \
		printk(KERN_ERR "module %s relocation of symbol %s is out of range (0x%lx in %d bits)\n", \
		printk(KERN_ERR "module %s relocation of symbol %s is out of range (0x%lx in %d bits)\n", \
		me->name, strtab + sym->st_name, (unsigned long)val, bits); \
		me->name, strtab + sym->st_name, (unsigned long)val, bits); \
		return -ENOEXEC;			\
		return -ENOEXEC;			\
@@ -92,13 +110,6 @@ static inline int in_local(struct module *me, void *loc)
	return in_init(me, loc) || in_core(me, loc);
	return in_init(me, loc) || in_core(me, loc);
}
}


static inline int in_local_section(struct module *me, void *loc, void *dot)
{
	return (in_init(me, loc) && in_init(me, dot)) ||
		(in_core(me, loc) && in_core(me, dot));
}


#ifndef CONFIG_64BIT
#ifndef CONFIG_64BIT
struct got_entry {
struct got_entry {
	Elf32_Addr addr;
	Elf32_Addr addr;
@@ -258,23 +269,42 @@ static inline unsigned long count_stubs(const Elf_Rela *rela, unsigned long n)
/* Free memory returned from module_alloc */
/* Free memory returned from module_alloc */
void module_free(struct module *mod, void *module_region)
void module_free(struct module *mod, void *module_region)
{
{
	kfree(mod->arch.section);
	mod->arch.section = NULL;

	vfree(module_region);
	vfree(module_region);
	/* FIXME: If module_region == mod->init_region, trim exception
	/* FIXME: If module_region == mod->init_region, trim exception
           table entries. */
           table entries. */
}
}


/* Additional bytes needed in front of individual sections */
unsigned int arch_mod_section_prepend(struct module *mod,
				      unsigned int section)
{
	/* size needed for all stubs of this section (including
	 * one additional for correct alignment of the stubs) */
	return (mod->arch.section[section].stub_entries + 1)
		* sizeof(struct stub_entry);
}

#define CONST 
#define CONST 
int module_frob_arch_sections(CONST Elf_Ehdr *hdr,
int module_frob_arch_sections(CONST Elf_Ehdr *hdr,
			      CONST Elf_Shdr *sechdrs,
			      CONST Elf_Shdr *sechdrs,
			      CONST char *secstrings,
			      CONST char *secstrings,
			      struct module *me)
			      struct module *me)
{
{
	unsigned long gots = 0, fdescs = 0, stubs = 0, init_stubs = 0;
	unsigned long gots = 0, fdescs = 0, len;
	unsigned int i;
	unsigned int i;


	len = hdr->e_shnum * sizeof(me->arch.section[0]);
	me->arch.section = kzalloc(len, GFP_KERNEL);
	if (!me->arch.section)
		return -ENOMEM;

	for (i = 1; i < hdr->e_shnum; i++) {
	for (i = 1; i < hdr->e_shnum; i++) {
		const Elf_Rela *rels = (void *)hdr + sechdrs[i].sh_offset;
		const Elf_Rela *rels = (void *)sechdrs[i].sh_addr;
		unsigned long nrels = sechdrs[i].sh_size / sizeof(*rels);
		unsigned long nrels = sechdrs[i].sh_size / sizeof(*rels);
		unsigned int count, s;


		if (strncmp(secstrings + sechdrs[i].sh_name,
		if (strncmp(secstrings + sechdrs[i].sh_name,
			    ".PARISC.unwind", 14) == 0)
			    ".PARISC.unwind", 14) == 0)
@@ -290,11 +320,23 @@ int module_frob_arch_sections(CONST Elf_Ehdr *hdr,
		 */
		 */
		gots += count_gots(rels, nrels);
		gots += count_gots(rels, nrels);
		fdescs += count_fdescs(rels, nrels);
		fdescs += count_fdescs(rels, nrels);
		if(strncmp(secstrings + sechdrs[i].sh_name,

			   ".rela.init", 10) == 0)
		/* XXX: By sorting the relocs and finding duplicate entries
			init_stubs += count_stubs(rels, nrels);
		 *  we could reduce the number of necessary stubs and save
		else
		 *  some memory. */
			stubs += count_stubs(rels, nrels);
		count = count_stubs(rels, nrels);
		if (!count)
			continue;

		/* so we need relocation stubs. reserve necessary memory. */
		/* sh_info gives the section for which we need to add stubs. */
		s = sechdrs[i].sh_info;

		/* each code section should only have one relocation section */
		WARN_ON(me->arch.section[s].stub_entries);

		/* store number of stubs we need for this section */
		me->arch.section[s].stub_entries += count;
	}
	}


	/* align things a bit */
	/* align things a bit */
@@ -306,18 +348,8 @@ int module_frob_arch_sections(CONST Elf_Ehdr *hdr,
	me->arch.fdesc_offset = me->core_size;
	me->arch.fdesc_offset = me->core_size;
	me->core_size += fdescs * sizeof(Elf_Fdesc);
	me->core_size += fdescs * sizeof(Elf_Fdesc);


	me->core_size = ALIGN(me->core_size, 16);
	me->arch.stub_offset = me->core_size;
	me->core_size += stubs * sizeof(struct stub_entry);

	me->init_size = ALIGN(me->init_size, 16);
	me->arch.init_stub_offset = me->init_size;
	me->init_size += init_stubs * sizeof(struct stub_entry);

	me->arch.got_max = gots;
	me->arch.got_max = gots;
	me->arch.fdesc_max = fdescs;
	me->arch.fdesc_max = fdescs;
	me->arch.stub_max = stubs;
	me->arch.init_stub_max = init_stubs;


	return 0;
	return 0;
}
}
@@ -380,23 +412,27 @@ enum elf_stub_type {
};
};


static Elf_Addr get_stub(struct module *me, unsigned long value, long addend,
static Elf_Addr get_stub(struct module *me, unsigned long value, long addend,
	enum elf_stub_type stub_type, int init_section)
	enum elf_stub_type stub_type, Elf_Addr loc0, unsigned int targetsec)
{
{
	unsigned long i;
	struct stub_entry *stub;
	struct stub_entry *stub;


	if(init_section) {
	/* initialize stub_offset to point in front of the section */
		i = me->arch.init_stub_count++;
	if (!me->arch.section[targetsec].stub_offset) {
		BUG_ON(me->arch.init_stub_count > me->arch.init_stub_max);
		loc0 -= (me->arch.section[targetsec].stub_entries + 1) *
		stub = me->module_init + me->arch.init_stub_offset + 
				sizeof(struct stub_entry);
			i * sizeof(struct stub_entry);
		/* get correct alignment for the stubs */
	} else {
		loc0 = ALIGN(loc0, sizeof(struct stub_entry));
		i = me->arch.stub_count++;
		me->arch.section[targetsec].stub_offset = loc0;
		BUG_ON(me->arch.stub_count > me->arch.stub_max);
		stub = me->module_core + me->arch.stub_offset + 
			i * sizeof(struct stub_entry);
	}
	}


	/* get address of stub entry */
	stub = (void *) me->arch.section[targetsec].stub_offset;
	me->arch.section[targetsec].stub_offset += sizeof(struct stub_entry);

	/* do not write outside available stub area */
	BUG_ON(0 == me->arch.section[targetsec].stub_entries--);


#ifndef CONFIG_64BIT
#ifndef CONFIG_64BIT
/* for 32-bit the stub looks like this:
/* for 32-bit the stub looks like this:
 * 	ldil L'XXX,%r1
 * 	ldil L'XXX,%r1
@@ -489,15 +525,19 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
	Elf32_Addr val;
	Elf32_Addr val;
	Elf32_Sword addend;
	Elf32_Sword addend;
	Elf32_Addr dot;
	Elf32_Addr dot;
	Elf_Addr loc0;
	unsigned int targetsec = sechdrs[relsec].sh_info;
	//unsigned long dp = (unsigned long)$global$;
	//unsigned long dp = (unsigned long)$global$;
	register unsigned long dp asm ("r27");
	register unsigned long dp asm ("r27");


	DEBUGP("Applying relocate section %u to %u\n", relsec,
	DEBUGP("Applying relocate section %u to %u\n", relsec,
	       sechdrs[relsec].sh_info);
	       targetsec);
	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
		/* This is where to make the change */
		/* This is where to make the change */
		loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
		loc = (void *)sechdrs[targetsec].sh_addr
		      + rel[i].r_offset;
		      + rel[i].r_offset;
		/* This is the start of the target section */
		loc0 = sechdrs[targetsec].sh_addr;
		/* This is the symbol it is referring to */
		/* This is the symbol it is referring to */
		sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
		sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
			+ ELF32_R_SYM(rel[i].r_info);
			+ ELF32_R_SYM(rel[i].r_info);
@@ -569,19 +609,32 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
			break;
			break;
		case R_PARISC_PCREL17F:
		case R_PARISC_PCREL17F:
			/* 17-bit PC relative address */
			/* 17-bit PC relative address */
			val = get_stub(me, val, addend, ELF_STUB_GOT, in_init(me, loc));
			/* calculate direct call offset */
			val += addend;
			val = (val - dot - 8)/4;
			val = (val - dot - 8)/4;
			CHECK_RELOC(val, 17)
			if (!RELOC_REACHABLE(val, 17)) {
				/* direct distance too far, create
				 * stub entry instead */
				val = get_stub(me, sym->st_value, addend,
					ELF_STUB_DIRECT, loc0, targetsec);
				val = (val - dot - 8)/4;
				CHECK_RELOC(val, 17);
			}
			*loc = (*loc & ~0x1f1ffd) | reassemble_17(val);
			*loc = (*loc & ~0x1f1ffd) | reassemble_17(val);
			break;
			break;
		case R_PARISC_PCREL22F:
		case R_PARISC_PCREL22F:
			/* 22-bit PC relative address; only defined for pa20 */
			/* 22-bit PC relative address; only defined for pa20 */
			val = get_stub(me, val, addend, ELF_STUB_GOT, in_init(me, loc));
			/* calculate direct call offset */
			DEBUGP("STUB FOR %s loc %lx+%lx at %lx\n", 
			val += addend;
			       strtab + sym->st_name, (unsigned long)loc, addend, 
			val = (val - dot - 8)/4;
			       val)
			if (!RELOC_REACHABLE(val, 22)) {
				/* direct distance too far, create
				 * stub entry instead */
				val = get_stub(me, sym->st_value, addend,
					ELF_STUB_DIRECT, loc0, targetsec);
				val = (val - dot - 8)/4;
				val = (val - dot - 8)/4;
				CHECK_RELOC(val, 22);
				CHECK_RELOC(val, 22);
			}
			*loc = (*loc & ~0x3ff1ffd) | reassemble_22(val);
			*loc = (*loc & ~0x3ff1ffd) | reassemble_22(val);
			break;
			break;


@@ -610,13 +663,17 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
	Elf64_Addr val;
	Elf64_Addr val;
	Elf64_Sxword addend;
	Elf64_Sxword addend;
	Elf64_Addr dot;
	Elf64_Addr dot;
	Elf_Addr loc0;
	unsigned int targetsec = sechdrs[relsec].sh_info;


	DEBUGP("Applying relocate section %u to %u\n", relsec,
	DEBUGP("Applying relocate section %u to %u\n", relsec,
	       sechdrs[relsec].sh_info);
	       targetsec);
	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
		/* This is where to make the change */
		/* This is where to make the change */
		loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
		loc = (void *)sechdrs[targetsec].sh_addr
		      + rel[i].r_offset;
		      + rel[i].r_offset;
		/* This is the start of the target section */
		loc0 = sechdrs[targetsec].sh_addr;
		/* This is the symbol it is referring to */
		/* This is the symbol it is referring to */
		sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
		sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
			+ ELF64_R_SYM(rel[i].r_info);
			+ ELF64_R_SYM(rel[i].r_info);
@@ -672,42 +729,40 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
			DEBUGP("PCREL22F Symbol %s loc %p val %lx\n",
			DEBUGP("PCREL22F Symbol %s loc %p val %lx\n",
			       strtab + sym->st_name,
			       strtab + sym->st_name,
			       loc, val);
			       loc, val);
			val += addend;
			/* can we reach it locally? */
			/* can we reach it locally? */
			if(!in_local_section(me, (void *)val, (void *)dot)) {
			if (in_local(me, (void *)val)) {

				/* this is the case where the symbol is local
				if (in_local(me, (void *)val))
				 * to the module, but in a different section,
					/* this is the case where the
				 * so stub the jump in case it's more than 22
					 * symbol is local to the
					 * module, but in a different
					 * section, so stub the jump
					 * in case it's more than 22
				 * bits away */
				 * bits away */
					val = get_stub(me, val, addend, ELF_STUB_DIRECT,
				val = (val - dot - 8)/4;
						       in_init(me, loc));
				if (!RELOC_REACHABLE(val, 22)) {
				else if (strncmp(strtab + sym->st_name, "$$", 2)
					/* direct distance too far, create
					 * stub entry instead */
					val = get_stub(me, sym->st_value,
						addend, ELF_STUB_DIRECT,
						loc0, targetsec);
				} else {
					/* Ok, we can reach it directly. */
					val = sym->st_value;
					val += addend;
				}
			} else {
				val = sym->st_value;
				if (strncmp(strtab + sym->st_name, "$$", 2)
				    == 0)
				    == 0)
					val = get_stub(me, val, addend, ELF_STUB_MILLI,
					val = get_stub(me, val, addend, ELF_STUB_MILLI,
						       in_init(me, loc));
						       loc0, targetsec);
				else
				else
					val = get_stub(me, val, addend, ELF_STUB_GOT,
					val = get_stub(me, val, addend, ELF_STUB_GOT,
						       in_init(me, loc));
						       loc0, targetsec);
			}
			}
			DEBUGP("STUB FOR %s loc %lx, val %lx+%lx at %lx\n", 
			DEBUGP("STUB FOR %s loc %lx, val %lx+%lx at %lx\n", 
			       strtab + sym->st_name, loc, sym->st_value,
			       strtab + sym->st_name, loc, sym->st_value,
			       addend, val);
			       addend, val);
			/* FIXME: local symbols work as long as the
			 * core and init pieces aren't separated too
			 * far.  If this is ever broken, you will trip
			 * the check below.  The way to fix it would
			 * be to generate local stubs to go between init
			 * and core */
			if((Elf64_Sxword)(val - dot - 8) > 0x800000 -1 ||
			   (Elf64_Sxword)(val - dot - 8) < -0x800000) {
				printk(KERN_ERR "Module %s, symbol %s is out of range for PCREL22F relocation\n",
				       me->name, strtab + sym->st_name);
				return -ENOEXEC;
			}
			val = (val - dot - 8)/4;
			val = (val - dot - 8)/4;
			CHECK_RELOC(val, 22);
			*loc = (*loc & ~0x3ff1ffd) | reassemble_22(val);
			*loc = (*loc & ~0x3ff1ffd) | reassemble_22(val);
			break;
			break;
		case R_PARISC_DIR64:
		case R_PARISC_DIR64:
@@ -794,12 +849,8 @@ int module_finalize(const Elf_Ehdr *hdr,
	addr = (u32 *)entry->addr;
	addr = (u32 *)entry->addr;
	printk("INSNS: %x %x %x %x\n",
	printk("INSNS: %x %x %x %x\n",
	       addr[0], addr[1], addr[2], addr[3]);
	       addr[0], addr[1], addr[2], addr[3]);
	printk("stubs used %ld, stubs max %ld\n"
	printk("got entries used %ld, gots max %ld\n"
	       "init_stubs used %ld, init stubs max %ld\n"
	       "got entries used %ld, gots max %ld\n"
	       "fdescs used %ld, fdescs max %ld\n",
	       "fdescs used %ld, fdescs max %ld\n",
	       me->arch.stub_count, me->arch.stub_max,
	       me->arch.init_stub_count, me->arch.init_stub_max,
	       me->arch.got_count, me->arch.got_max,
	       me->arch.got_count, me->arch.got_max,
	       me->arch.fdesc_count, me->arch.fdesc_max);
	       me->arch.fdesc_count, me->arch.fdesc_max);
#endif
#endif
@@ -830,6 +881,9 @@ int module_finalize(const Elf_Ehdr *hdr,
		return -EINVAL;
		return -EINVAL;
	}
	}


	kfree(me->arch.section);
	me->arch.section = NULL;

	/* no symbol table */
	/* no symbol table */
	if(symhdr == NULL)
	if(symhdr == NULL)
		return 0;
		return 0;
+3 −0
Original line number Original line Diff line number Diff line
@@ -13,6 +13,9 @@ int module_frob_arch_sections(Elf_Ehdr *hdr,
			      char *secstrings,
			      char *secstrings,
			      struct module *mod);
			      struct module *mod);


/* Additional bytes needed by arch in front of individual sections */
unsigned int arch_mod_section_prepend(struct module *mod, unsigned int section);

/* Allocator used for allocating struct module, core sections and init
/* Allocator used for allocating struct module, core sections and init
   sections.  Returns NULL on failure. */
   sections.  Returns NULL on failure. */
void *module_alloc(unsigned long size);
void *module_alloc(unsigned long size);
+22 −0
Original line number Original line Diff line number Diff line
@@ -35,6 +35,24 @@ int stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus);
 * won't come or go while it's being called.  Used by hotplug cpu.
 * won't come or go while it's being called.  Used by hotplug cpu.
 */
 */
int __stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus);
int __stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus);

/**
 * stop_machine_create: create all stop_machine threads
 *
 * Description: This causes all stop_machine threads to be created before
 * stop_machine actually gets called. This can be used by subsystems that
 * need a non failing stop_machine infrastructure.
 */
int stop_machine_create(void);

/**
 * stop_machine_destroy: destroy all stop_machine threads
 *
 * Description: This causes all stop_machine threads which were created with
 * stop_machine_create to be destroyed again.
 */
void stop_machine_destroy(void);

#else
#else


static inline int stop_machine(int (*fn)(void *), void *data,
static inline int stop_machine(int (*fn)(void *), void *data,
@@ -46,5 +64,9 @@ static inline int stop_machine(int (*fn)(void *), void *data,
	local_irq_enable();
	local_irq_enable();
	return ret;
	return ret;
}
}

static inline int stop_machine_create(void) { return 0; }
static inline void stop_machine_destroy(void) { }

#endif /* CONFIG_SMP */
#endif /* CONFIG_SMP */
#endif /* _LINUX_STOP_MACHINE */
#endif /* _LINUX_STOP_MACHINE */
+0 −6
Original line number Original line Diff line number Diff line
@@ -916,12 +916,6 @@ config MODULE_SRCVERSION_ALL
	  the version).  With this option, such a "srcversion" field
	  the version).  With this option, such a "srcversion" field
	  will be created for all modules.  If unsure, say N.
	  will be created for all modules.  If unsure, say N.


config KMOD
	def_bool y
	help
	  This is being removed soon.  These days, CONFIG_MODULES
	  implies CONFIG_KMOD, so use that instead.

endif # MODULES
endif # MODULES


config INIT_ALL_POSSIBLE
config INIT_ALL_POSSIBLE
Loading