Commit 35b93751 authored by Paul Mackerras's avatar Paul Mackerras Committed by Linus Torvalds

[PATCH] sort exception tables

This patch arranges for the exception tables to be sorted on most
architectures.  It sorts the main kernel exception table on startup
and the module exception tables when they get loaded.  The main table
is sorted reasonably early - just after kmem_cache_init - but that
could be moved even earlier if necessary.

There is now a lib/extable.c which includes the sort_extable()
function from arch/ppc/mm/extable.c and the search_extable() function
from arch/i386/mm/extable.c, which had been copied to many
architectures.  On many architectures, arch/$(ARCH)/mm/extable.c
became empty and so I have removed it.

There are four architectures which do things differently from i386:
alpha, ia64, sparc and sparc64.  Alpha and ia64 store the offset from
the offset from the exception table entry to the instruction, and
sparc and sparc64 have range entries in the table.  For those
architectures I have added empty sort_extable functions.  The
maintainers for those architectures can implement something better if
they care to.  As it is they are no worse off than before.

Although it is a moderately sizable patch, it ends up with a net
reduction of 377 lines in the size of the kernel source. :)

I have tested this on x86 and ppc with a module that uses __get_user
in an init function, deliberately laid out to get the exception table
out of order, and it works (whereas it oopsed without this patch).
parent d35e9aba
......@@ -6,6 +6,11 @@
#include <linux/module.h>
#include <asm/uaccess.h>
void sort_extable(struct exception_table_entry *start,
struct exception_table_entry *finish)
{
}
const struct exception_table_entry *
search_extable(const struct exception_table_entry *first,
const struct exception_table_entry *last,
......
......@@ -4,27 +4,6 @@
#include <linux/module.h>
#include <asm/uaccess.h>
const struct exception_table_entry *
search_extable(const struct exception_table_entry *first,
const struct exception_table_entry *last,
unsigned long value)
{
while (first <= last) {
const struct exception_table_entry *mid;
long diff;
mid = (last - first) / 2 + first;
diff = mid->insn - value;
if (diff == 0)
return mid;
else if (diff < 0)
first = mid+1;
else
last = mid-1;
}
return NULL;
}
int fixup_exception(struct pt_regs *regs)
{
const struct exception_table_entry *fixup;
......
......@@ -6,27 +6,6 @@
#include <linux/module.h>
#include <asm/uaccess.h>
const struct exception_table_entry *
search_extable(const struct exception_table_entry *first,
const struct exception_table_entry *last,
unsigned long value)
{
while (first <= last) {
const struct exception_table_entry *mid;
long diff;
mid = (last - first) / 2 + first;
diff = mid->insn - value;
if (diff == 0)
return mid;
else if (diff < 0)
first = mid+1;
else
last = mid-1;
}
return NULL;
}
int fixup_exception(struct pt_regs *regs)
{
const struct exception_table_entry *fixup;
......
......@@ -2,5 +2,5 @@
# Makefile for the linux cris-specific parts of the memory manager.
#
obj-y := init.o fault.o tlb.o extable.o ioremap.o
obj-y := init.o fault.o tlb.o ioremap.o
/*
* linux/arch/cris/mm/extable.c
*
* $Log: extable.c,v $
* Revision 1.4 2003/01/09 14:42:52 starvik
* Merge of Linux 2.5.55
*
* Revision 1.3 2002/11/21 07:24:54 starvik
* Made search_exception_table similar to implementation for other archs
* (now compiles with CONFIG_MODULES)
*
* Revision 1.2 2002/11/18 07:36:55 starvik
* Removed warning
*
* Revision 1.1 2001/12/17 13:59:27 bjornw
* Initial revision
*
* Revision 1.3 2001/09/27 13:52:40 bjornw
* Harmonize underscore-ness with other parts
*
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <asm/uaccess.h>
/* Simple binary search */
const struct exception_table_entry *
search_extable(const struct exception_table_entry *first,
const struct exception_table_entry *last,
unsigned long value)
{
while (first <= last) {
const struct exception_table_entry *mid;
long diff;
mid = (last - first) / 2 + first;
diff = mid->insn - value;
if (diff == 0)
return mid;
else if (diff < 0)
first = mid+1;
else
last = mid-1;
}
return NULL;
}
......@@ -7,4 +7,4 @@
#
# Note 2! The CFLAGS definition is now in the main makefile...
obj-y := init.o fault.o memory.o kmap.o extable.o
obj-y := init.o fault.o memory.o kmap.o
/*
* linux/arch/h8300/mm/extable.c
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <asm/uaccess.h>
/* Simple binary search */
const struct exception_table_entry *
search_extable(const struct exception_table_entry *first,
const struct exception_table_entry *last,
unsigned long value)
{
while (first <= last) {
const struct exception_table_entry *mid;
long diff;
mid = (last - first) / 2 + first;
diff = mid->insn - value;
if (diff == 0)
return mid;
else if (diff < 0)
first = mid+1;
else
last = mid-1;
}
return NULL;
}
......@@ -7,28 +7,6 @@
#include <linux/spinlock.h>
#include <asm/uaccess.h>
/* Simple binary search */
const struct exception_table_entry *
search_extable(const struct exception_table_entry *first,
const struct exception_table_entry *last,
unsigned long value)
{
while (first <= last) {
const struct exception_table_entry *mid;
long diff;
mid = (last - first) / 2 + first;
diff = mid->insn - value;
if (diff == 0)
return mid;
else if (diff < 0)
first = mid+1;
else
last = mid-1;
}
return NULL;
}
int fixup_exception(struct pt_regs *regs)
{
const struct exception_table_entry *fixup;
......
......@@ -10,6 +10,11 @@
#include <asm/uaccess.h>
#include <asm/module.h>
void sort_extable(struct exception_table_entry *start,
struct exception_table_entry *finish)
{
}
const struct exception_table_entry *
search_extable (const struct exception_table_entry *first,
const struct exception_table_entry *last,
......
......@@ -2,7 +2,7 @@
# Makefile for the linux m68k-specific parts of the memory manager.
#
obj-y := init.o fault.o extable.o hwtest.o
obj-y := init.o fault.o hwtest.o
ifndef CONFIG_SUN3
obj-y += kmap.o memory.o motorola.o
......
/*
* linux/arch/m68k/mm/extable.c
*/
#include <linux/config.h>
#include <linux/module.h>
#include <asm/uaccess.h>
extern const struct exception_table_entry __start___ex_table[];
extern const struct exception_table_entry __stop___ex_table[];
/* Simple binary search */
const struct exception_table_entry *
search_extable(const struct exception_table_entry *first,
const struct exception_table_entry *last,
unsigned long value)
{
while (first <= last) {
const struct exception_table_entry *mid;
long diff;
mid = (last - first) / 2 + first;
diff = value - mid->insn;
if (diff >= 0 && diff <= 2)
return mid;
else if (diff > 0)
first = mid+1;
else
last = mid-1;
}
return NULL;
}
......@@ -2,4 +2,4 @@
# Makefile for the linux m68knommu specific parts of the memory manager.
#
obj-y += init.o fault.o memory.o kmap.o extable.o
obj-y += init.o fault.o memory.o kmap.o
/*
* linux/arch/m68knommu/mm/extable.c
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <asm/uaccess.h>
/* Simple binary search */
const struct exception_table_entry *
search_extable(const struct exception_table_entry *first,
const struct exception_table_entry *last,
unsigned long value)
{
while (first <= last) {
const struct exception_table_entry *mid;
long diff;
mid = (last - first) / 2 + first;
diff = mid->insn - value;
if (diff == 0)
return mid;
else if (diff < 0)
first = mid+1;
else
last = mid-1;
}
return NULL;
}
......@@ -2,7 +2,7 @@
# Makefile for the Linux/MIPS-specific parts of the memory manager.
#
obj-y += cache.o extable.o fault.o loadmmu.o pgtable.o
obj-y += cache.o fault.o loadmmu.o pgtable.o
obj-$(CONFIG_MIPS32) += ioremap.o pgtable-32.o
obj-$(CONFIG_MIPS64) += pgtable-64.o
......
/*
* linux/arch/i386/mm/extable.c
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <asm/uaccess.h>
/* Simple binary search */
const struct exception_table_entry *
search_extable(const struct exception_table_entry *first,
const struct exception_table_entry *last,
unsigned long value)
{
while (first <= last) {
const struct exception_table_entry *mid;
long diff;
mid = (last - first) / 2 + first;
diff = mid->insn - value;
if (diff == 0)
return mid;
else if (diff < 0)
first = mid+1;
else
last = mid-1;
}
return NULL;
}
......@@ -2,4 +2,4 @@
# Makefile for arch/parisc/mm
#
obj-y := init.o fault.o extable.o ioremap.o
obj-y := init.o fault.o ioremap.o
/*
* Kernel exception handling table support. Derived from arch/i386/mm/extable.c.
*
* Copyright (C) 2000 Hewlett-Packard Co
* Copyright (C) 2000 John Marvin (jsm@fc.hp.com)
*/
#include <asm/uaccess.h>
const struct exception_table_entry *
search_extable(const struct exception_table_entry *first,
const struct exception_table_entry *last,
unsigned long addr)
{
/* Abort early if the search value is out of range. */
if ((addr < first->addr) || (addr > last->addr))
return 0;
while (first <= last) {
const struct exception_table_entry *mid;
long diff;
mid = first + ((last - first)/2);
diff = mid->addr - addr;
if (diff == 0)
return mid;
else if (diff < 0)
first = mid+1;
else
last = mid-1;
}
return 0;
}
......@@ -269,7 +269,6 @@ int apply_relocate_add(Elf32_Shdr *sechdrs,
return 0;
}
/* FIXME: Sort exception table --RR */
int module_finalize(const Elf_Ehdr *hdr,
const Elf_Shdr *sechdrs,
struct module *me)
......
......@@ -6,7 +6,7 @@ ifdef CONFIG_PPC64BRIDGE
EXTRA_AFLAGS := -Wa,-mppc64bridge
endif
obj-y := fault.o init.o mem_pieces.o extable.o \
obj-y := fault.o init.o mem_pieces.o \
mmu_context.o pgtable.o
obj-$(CONFIG_PPC_STD_MMU) += hashtable.o ppc_mmu.o tlb.o
......
/*
* arch/ppc/mm/extable.c
*
* from arch/i386/mm/extable.c
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <asm/uaccess.h>
extern struct exception_table_entry __start___ex_table[];
extern struct exception_table_entry __stop___ex_table[];
/*
* The exception table needs to be sorted because we use the macros
* which put things into the exception table in a variety of segments
* such as the prep, pmac, chrp, etc. segments as well as the init
* segment and the main kernel text segment.
*/
static inline void
sort_ex_table(struct exception_table_entry *start,
struct exception_table_entry *finish)
{
struct exception_table_entry el, *p, *q;
/* insertion sort */
for (p = start + 1; p < finish; ++p) {
/* start .. p-1 is sorted */
if (p[0].insn < p[-1].insn) {
/* move element p down to its right place */
el = *p;
q = p;
do {
/* el comes before q[-1], move q[-1] up one */
q[0] = q[-1];
--q;
} while (q > start && el.insn < q[-1].insn);
*q = el;
}
}
}
void __init
sort_exception_table(void)
{
sort_ex_table(__start___ex_table, __stop___ex_table);
}
/* Simple binary search */
const struct exception_table_entry *
search_extable(const struct exception_table_entry *first,
const struct exception_table_entry *last,
unsigned long value)
{
while (first <= last) {
const struct exception_table_entry *mid;
long diff;
mid = (last - first) / 2 + first;
diff = mid->insn - value;
if (diff == 0)
return mid;
else if (diff < 0)
first = mid+1;
else
last = mid-1;
}
return NULL;
}
......@@ -404,10 +404,6 @@ int module_finalize(const Elf_Ehdr *hdr,
*/
list_add(&me->arch.bug_list, &module_bug_list);
sort_ex_table((struct exception_table_entry *)me->extable,
(struct exception_table_entry *)me->extable +
me->num_exentries);
return 0;
}
......
......@@ -490,7 +490,6 @@ void __init ppc64_calibrate_delay(void)
}
extern void (*calibrate_delay)(void);
extern void sort_exception_table(void);
/*
* Called into from start_kernel, after lock_kernel has been called.
......@@ -538,7 +537,6 @@ void __init setup_arch(char **cmdline_p)
ppc_md.setup_arch();
paging_init();
sort_exception_table();
ppc64_boot_msg(0x15, "Setup Done");
}
......
......@@ -4,6 +4,6 @@
EXTRA_CFLAGS += -mno-minimal-toc
obj-y := fault.o init.o extable.o imalloc.o hash_utils.o hash_low.o
obj-y := fault.o init.o imalloc.o hash_utils.o hash_low.o
obj-$(CONFIG_DISCONTIGMEM) += numa.o
obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
......@@ -2,4 +2,4 @@
# Makefile for the linux s390-specific parts of the memory manager.
#
obj-y := init.o fault.o ioremap.o extable.o
obj-y := init.o fault.o ioremap.o
/*
* arch/s390/mm/extable.c
*
* S390 version
*
* identical to arch/i386/mm/extable.c
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <asm/uaccess.h>
/* Simple binary search */
const struct exception_table_entry *
search_extable(const struct exception_table_entry *first,
const struct exception_table_entry *last,
unsigned long value)
{
while (first <= last) {
const struct exception_table_entry *mid;
long diff;
mid = (last - first) / 2 + first;
diff = mid->insn - value;
if (diff == 0)
return mid;
else if (diff < 0)
first = mid+1;
else
last = mid-1;
}
return NULL;
}
......@@ -9,29 +9,6 @@
#include <linux/module.h>
#include <asm/uaccess.h>
/* Simple binary search */
const struct exception_table_entry *
search_extable(const struct exception_table_entry *first,
const struct exception_table_entry *last,
unsigned long value)
{
while (first <= last) {
const struct exception_table_entry *mid;
long diff;
mid = (last - first) / 2 + first;
diff = mid->insn - value;
if (diff == 0)
return mid;
else if (diff < 0)
first = mid+1;
else
last = mid-1;
}
return NULL;
}
int fixup_exception(struct pt_regs *regs)
{
const struct exception_table_entry *fixup;
......
......@@ -6,6 +6,11 @@
#include <linux/module.h>
#include <asm/uaccess.h>
void sort_extable(struct exception_table_entry *start,
struct exception_table_entry *finish)
{
}
/* Caller knows they are in a range if ret->fixup == 0 */
const struct exception_table_entry *
search_extable(const struct exception_table_entry *start,
......
......@@ -9,6 +9,11 @@
extern const struct exception_table_entry __start___ex_table[];
extern const struct exception_table_entry __stop___ex_table[];
void sort_extable(struct exception_table_entry *start,
struct exception_table_entry *finish)
{
}
/* Caller knows they are in a range if ret->fixup == 0 */
const struct exception_table_entry *
search_extable(const struct exception_table_entry *start,
......
......@@ -36,10 +36,9 @@ search_extable(const struct exception_table_entry *first,
/* When an exception handler is in an non standard section (like __init)
the fixup table can end up unordered. Fix that here. */
static __init int check_extable(void)
void sort_extable(struct exception_table_entry *start,
struct exception_table_entry *finish)
{
extern struct exception_table_entry __start___ex_table[];
extern struct exception_table_entry __stop___ex_table[];
struct exception_table_entry *e;
int change;
......@@ -47,7 +46,7 @@ static __init int check_extable(void)
best (and simplest) sort algorithm. */
do {
change = 0;
for (e = __start___ex_table+1; e < __stop___ex_table; e++) {
for (e = start+1; e < finish; e++) {
if (e->insn < e[-1].insn) {
struct exception_table_entry tmp = e[-1];
e[-1] = e[0];
......@@ -58,4 +57,3 @@ static __init int check_extable(void)
} while (change != 0);
return 0;
}
core_initcall(check_extable);
......@@ -60,8 +60,6 @@ struct exception_table_entry
unsigned long insn, fixup;
};
extern void sort_exception_table(void);
/*
* These are the main single-value transfer routines. They automatically
* use the right size if we just have the right pointer type.
......
......@@ -82,7 +82,6 @@ struct exception_table_entry
/* Returns 0 if exception not found and fixup otherwise. */
extern unsigned long search_exception_table(unsigned long);
extern void sort_exception_table(void);
/*
* These are the main single-value transfer routines. They automatically
......
......@@ -54,6 +54,9 @@ const struct exception_table_entry *
search_extable(const struct exception_table_entry *first,
const struct exception_table_entry *last,
unsigned long value);
void sort_extable(struct exception_table_entry *start,
struct exception_table_entry *finish);
void sort_main_extable(void);
#ifdef MODULE
#define ___module_cat(a,b) __mod_ ## a ## b
......
......@@ -435,6 +435,7 @@ asmlinkage void __init start_kernel(void)
page_address_init();
mem_init();
kmem_cache_init();
sort_main_extable();
if (late_time_init)
late_time_init();
calibrate_delay();
......
......@@ -16,11 +16,18 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <asm/sections.h>
extern const struct exception_table_entry __start___ex_table[];
extern const struct exception_table_entry __stop___ex_table[];
extern struct exception_table_entry __start___ex_table[];
extern struct exception_table_entry __stop___ex_table[];
/* Sort the kernel's built-in exception table */
void __init sort_main_extable(void)
{
sort_extable(__start___ex_table, __stop___ex_table);
}
/* Given an address, look for it in the exception tables. */
const struct exception_table_entry *search_exception_tables(unsigned long addr)
......
......@@ -1395,6 +1395,7 @@ static struct module *load_module(void __user *umod,
struct module *mod;
long err = 0;
void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */
struct exception_table_entry *extable;
DEBUGP("load_module: umod=%p, len=%lu, uargs=%p\n",
umod, len, uargs);
......@@ -1611,10 +1612,6 @@ static struct module *load_module(void __user *umod,
}
#endif
/* Set up exception table */
mod->num_exentries = sechdrs[exindex].sh_size / sizeof(*mod->extable);
mod->extable = (void *)sechdrs[exindex].sh_addr;
/* Now do relocations. */
for (i = 1; i < hdr->e_shnum; i++) {
const char *strtab = (char *)sechdrs[strindex].sh_addr;
......@@ -1637,6 +1634,11 @@ static struct module *load_module(void __user *umod,
goto cleanup;
}
/* Set up and sort exception table */
mod->num_exentries = sechdrs[exindex].sh_size / sizeof(*mod->extable);
mod->extable = extable = (void *)sechdrs[exindex].sh_addr;
sort_extable(extable, extable + mod->num_exentries);
/* Finally, copy percpu area over. */
percpu_modcopy(mod->percpu, (void *)sechdrs[pcpuindex].sh_addr,
sechdrs[pcpuindex].sh_size);
......
......@@ -6,7 +6,7 @@
lib-y := errno.o ctype.o string.o vsprintf.o cmdline.o \
bust_spinlocks.o rbtree.o radix-tree.o dump_stack.o \
kobject.o idr.o div64.o parser.o int_sqrt.o mask.o \
bitmap.o
bitmap.o extable.o
lib-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
......
/*
* arch/ppc64/mm/extable.c
* lib/extable.c
* Derived from arch/ppc/mm/extable.c and arch/i386/mm/extable.c.
*
* from arch/i386/mm/extable.c
* Copyright (C) 2004 Paul Mackerras, IBM Corp.
*
* 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.
* 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.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <asm/uaccess.h>
......@@ -17,14 +19,13 @@ extern struct exception_table_entry __start___ex_table[];
extern struct exception_table_entry __stop___ex_table[];
/*
* The exception table needs to be sorted because we use the macros
* which put things into the exception table in a variety of segments
* as well as the init segment and the main kernel text segment.
*
* Also used for modules.
* The exception table needs to be sorted so that the binary
* search that we use to find entries in it works properly.
* This is used both for the kernel exception table and for
* the exception tables of modules that get loaded.
*/
void __init_or_module sort_ex_table(struct exception_table_entry *start,
struct exception_table_entry *finish)
void sort_extable(struct exception_table_entry *start,
struct exception_table_entry *finish)
{
struct exception_table_entry el, *p, *q;
......@@ -45,13 +46,13 @@ void __init_or_module sort_ex_table(struct exception_table_entry *start,
}
}
void __init
sort_exception_table(void)
{
sort_ex_table(__start___ex_table, __stop___ex_table);
}
/* Simple binary search */
/*
* Search one exception table for an entry corresponding to the
* given instruction address, and return the address of the entry,
* or NULL if none is found.
* We use a binary search, and thus we assume that the table is
* already sorted.
*/
const struct exception_table_entry *
search_extable(const struct exception_table_entry *first,
const struct exception_table_entry *last,
......@@ -65,10 +66,10 @@ search_extable(const struct exception_table_entry *first,
diff = mid->insn - value;
if (diff == 0)
return mid;
else if (diff < 0)
if (diff < 0)
first = mid+1;
else
last = mid-1;
}
return NULL;
}
return NULL;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment