Skip to content

Commit 7062765

Browse files
author
H. Peter Anvin
committed
x86, extable: Switch to relative exception table entries
Switch to using relative exception table entries on x86. On i386, this has the advantage that the exception table entries don't need to be relocated; on x86-64 this means the exception table entries take up only half the space. In either case, a 32-bit delta is sufficient, as the range of kernel code addresses is limited. Since part of the goal is to avoid needing to adjust the entries when the kernel is relocated, the old trick of using addresses in the NULL pointer range to indicate uaccess_err no longer works (and unlike RISC architectures we can't use a flag bit); instead use an delta just below +2G to indicate these special entries. The reach is still limited to a single instruction. Signed-off-by: H. Peter Anvin <hpa@zytor.com> Cc: David Daney <david.daney@cavium.com> Link: http://lkml.kernel.org/r/CA%2B55aFyijf43qSu3N9nWHEBwaGbb7T2Oq9A=9EyR=Jtyqfq_cQ@mail.gmail.com
1 parent fa574a4 commit 7062765

File tree

3 files changed

+146
-22
lines changed

3 files changed

+146
-22
lines changed

arch/x86/include/asm/asm.h

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,26 +42,30 @@
4242
#ifdef __ASSEMBLY__
4343
# define _ASM_EXTABLE(from,to) \
4444
.pushsection "__ex_table","a" ; \
45-
_ASM_ALIGN ; \
46-
_ASM_PTR from , to ; \
45+
.balign 8 ; \
46+
.long (from) - . ; \
47+
.long (to) - . ; \
4748
.popsection
4849

4950
# define _ASM_EXTABLE_EX(from,to) \
5051
.pushsection "__ex_table","a" ; \
51-
_ASM_ALIGN ; \
52-
_ASM_PTR from , (to) - (from) ; \
52+
.balign 8 ; \
53+
.long (from) - . ; \
54+
.long (to) - . + 0x7ffffff0 ; \
5355
.popsection
5456
#else
5557
# define _ASM_EXTABLE(from,to) \
5658
" .pushsection \"__ex_table\",\"a\"\n" \
57-
_ASM_ALIGN "\n" \
58-
_ASM_PTR #from "," #to "\n" \
59+
" .balign 8\n" \
60+
" .long (" #from ") - .\n" \
61+
" .long (" #to ") - .\n" \
5962
" .popsection\n"
6063

6164
# define _ASM_EXTABLE_EX(from,to) \
6265
" .pushsection \"__ex_table\",\"a\"\n" \
63-
_ASM_ALIGN "\n" \
64-
_ASM_PTR #from ",(" #to ")-(" #from ")\n" \
66+
" .balign 8\n" \
67+
" .long (" #from ") - .\n" \
68+
" .long (" #to ") - . + 0x7ffffff0\n" \
6569
" .popsection\n"
6670
#endif
6771

arch/x86/include/asm/uaccess.h

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,12 @@
7979
#define access_ok(type, addr, size) (likely(__range_not_ok(addr, size) == 0))
8080

8181
/*
82-
* The exception table consists of pairs of addresses: the first is the
83-
* address of an instruction that is allowed to fault, and the second is
84-
* the address at which the program should continue. No registers are
85-
* modified, so it is entirely up to the continuation code to figure out
86-
* what to do.
82+
* The exception table consists of pairs of addresses relative to the
83+
* exception table enty itself: the first is the address of an
84+
* instruction that is allowed to fault, and the second is the address
85+
* at which the program should continue. No registers are modified,
86+
* so it is entirely up to the continuation code to figure out what to
87+
* do.
8788
*
8889
* All the routines below use bits of fixup code that are out of line
8990
* with the main instruction path. This means when everything is well,
@@ -92,10 +93,14 @@
9293
*/
9394

9495
struct exception_table_entry {
95-
unsigned long insn, fixup;
96+
int insn, fixup;
9697
};
98+
/* This is not the generic standard exception_table_entry format */
99+
#define ARCH_HAS_SORT_EXTABLE
100+
#define ARCH_HAS_SEARCH_EXTABLE
97101

98102
extern int fixup_exception(struct pt_regs *regs);
103+
extern int early_fixup_exception(unsigned long *ip);
99104

100105
/*
101106
* These are the main single-value transfer routines. They automatically

arch/x86/mm/extable.c

Lines changed: 123 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
11
#include <linux/module.h>
22
#include <linux/spinlock.h>
3+
#include <linux/sort.h>
34
#include <asm/uaccess.h>
45

6+
static inline unsigned long
7+
ex_insn_addr(const struct exception_table_entry *x)
8+
{
9+
return (unsigned long)&x->insn + x->insn;
10+
}
11+
static inline unsigned long
12+
ex_fixup_addr(const struct exception_table_entry *x)
13+
{
14+
return (unsigned long)&x->fixup + x->fixup;
15+
}
516

617
int fixup_exception(struct pt_regs *regs)
718
{
819
const struct exception_table_entry *fixup;
20+
unsigned long new_ip;
921

1022
#ifdef CONFIG_PNPBIOS
1123
if (unlikely(SEGMENT_IS_PNP_CODE(regs->cs))) {
@@ -23,13 +35,14 @@ int fixup_exception(struct pt_regs *regs)
2335

2436
fixup = search_exception_tables(regs->ip);
2537
if (fixup) {
26-
/* If fixup is less than 16, it means uaccess error */
27-
if (fixup->fixup < 16) {
38+
new_ip = ex_fixup_addr(fixup);
39+
40+
if (fixup->fixup - fixup->insn >= 0x7ffffff0 - 4) {
41+
/* Special hack for uaccess_err */
2842
current_thread_info()->uaccess_err = 1;
29-
regs->ip += fixup->fixup;
30-
return 1;
43+
new_ip -= 0x7ffffff0;
3144
}
32-
regs->ip = fixup->fixup;
45+
regs->ip = new_ip;
3346
return 1;
3447
}
3548

@@ -40,15 +53,117 @@ int fixup_exception(struct pt_regs *regs)
4053
int __init early_fixup_exception(unsigned long *ip)
4154
{
4255
const struct exception_table_entry *fixup;
56+
unsigned long new_ip;
4357

4458
fixup = search_exception_tables(*ip);
4559
if (fixup) {
46-
if (fixup->fixup < 16)
47-
return 0; /* Not supported during early boot */
60+
new_ip = ex_fixup_addr(fixup);
61+
62+
if (fixup->fixup - fixup->insn >= 0x7ffffff0 - 4) {
63+
/* uaccess handling not supported during early boot */
64+
return 0;
65+
}
4866

49-
*ip = fixup->fixup;
67+
*ip = new_ip;
5068
return 1;
5169
}
5270

5371
return 0;
5472
}
73+
74+
/*
75+
* Search one exception table for an entry corresponding to the
76+
* given instruction address, and return the address of the entry,
77+
* or NULL if none is found.
78+
* We use a binary search, and thus we assume that the table is
79+
* already sorted.
80+
*/
81+
const struct exception_table_entry *
82+
search_extable(const struct exception_table_entry *first,
83+
const struct exception_table_entry *last,
84+
unsigned long value)
85+
{
86+
while (first <= last) {
87+
const struct exception_table_entry *mid;
88+
unsigned long addr;
89+
90+
mid = ((last - first) >> 1) + first;
91+
addr = ex_insn_addr(mid);
92+
if (addr < value)
93+
first = mid + 1;
94+
else if (addr > value)
95+
last = mid - 1;
96+
else
97+
return mid;
98+
}
99+
return NULL;
100+
}
101+
102+
/*
103+
* The exception table needs to be sorted so that the binary
104+
* search that we use to find entries in it works properly.
105+
* This is used both for the kernel exception table and for
106+
* the exception tables of modules that get loaded.
107+
*
108+
*/
109+
static int cmp_ex(const void *a, const void *b)
110+
{
111+
const struct exception_table_entry *x = a, *y = b;
112+
113+
/*
114+
* This value will always end up fittin in an int, because on
115+
* both i386 and x86-64 the kernel symbol-reachable address
116+
* space is < 2 GiB.
117+
*
118+
* This compare is only valid after normalization.
119+
*/
120+
return x->insn - y->insn;
121+
}
122+
123+
void sort_extable(struct exception_table_entry *start,
124+
struct exception_table_entry *finish)
125+
{
126+
struct exception_table_entry *p;
127+
int i;
128+
129+
/* Convert all entries to being relative to the start of the section */
130+
i = 0;
131+
for (p = start; p < finish; p++) {
132+
p->insn += i;
133+
i += 4;
134+
p->fixup += i;
135+
i += 4;
136+
}
137+
138+
sort(start, finish - start, sizeof(struct exception_table_entry),
139+
cmp_ex, NULL);
140+
141+
/* Denormalize all entries */
142+
i = 0;
143+
for (p = start; p < finish; p++) {
144+
p->insn -= i;
145+
i += 4;
146+
p->fixup -= i;
147+
i += 4;
148+
}
149+
}
150+
151+
#ifdef CONFIG_MODULES
152+
/*
153+
* If the exception table is sorted, any referring to the module init
154+
* will be at the beginning or the end.
155+
*/
156+
void trim_init_extable(struct module *m)
157+
{
158+
/*trim the beginning*/
159+
while (m->num_exentries &&
160+
within_module_init(ex_insn_addr(&m->extable[0]), m)) {
161+
m->extable++;
162+
m->num_exentries--;
163+
}
164+
/*trim the end*/
165+
while (m->num_exentries &&
166+
within_module_init(ex_insn_addr(&m->extable[m->num_exentries-1]), m))
167+
m->num_exentries--;
168+
}
169+
#endif /* CONFIG_MODULES */

0 commit comments

Comments
 (0)