Messages in this thread |  | | From | Richard Henderson <> | Subject | Re: Linux-2.1.4 | Date | Thu, 17 Oct 1996 15:26:58 -0500 (CDT) |
| |
> another option is to trick gcc into producing the proper symbol entries > around the exception-prone code. We have to generate two symbols: > > symbol_1: mov src, target_that_might_fault > > symbol_2: exception_handling_logic > > Thus the exception handler only has to read the EIP value of symbol_2 out > of a table indexed by symbol_1 [=exception EIP].
This general idea sounds very promising. And it doesn't need a custom program to implement either. Consider the following for the Alpha:
#define get_user_64(x,addr) \ ({ \ long __err; \ __asm__("clr %1\n" \ "1: ldq %0, %2\n" \ ".section __ex_table,\"aw\"\n\t" \ ".quad 1b\n" \ ".text" \ : "=r"(x), "=r"(__err) \ : "m"(*(addr))); \ __err; \ })
Notice that we are collecting the address of the instruction that is allowed to fault in section __ex_table. There is an ld feature (at least as of binutils 2.7) that, for each section whose entire name is a valid C symbol, generates the two symbols __start_<section> and __stop_<section>. Additionally, due to the way the data is generated and sections assembled, we know that the addresses will be sorted by increasing address.
Thus we can get the extent of the table and do a binary search for our faulting address, the register in which to store -EFAULT from *(pc - 4) & 0x1f, and the return address is the next instruction.
That simple table and two instruction idea should work in all cases for any machine that can direcly load/store all data sizes.
For the Alpha, we need more to handle sub-word loads/stores efficiently. For get_user_8 the insn sequence is
clr err 1: ldq_u val, addr extbl val, addr, val 3:
and for put_user_8 the sequence is
clr err 1: ldq_u tmp, addr insbl val, addr, tmp2 mskbl tmp, addr, tmp or tmp, tmp2, tmp 2: stq_u tmp, addr 3:
Now, here we have three options. First, we could add knowledge about how to decode these fragments into the exception handler so that it knows how to handle each case via the single address entry. Second, we could put the 1's addresses in separate tables and build only the knowledge of the exact displacements into the exception handler. Finally, we could use a table format such as:
.quad 1b .byte 3b-1b, 2b-1b, 2b-3b, <errreg>
At this point I'm in favour of the first option, as kernel space should be put at a premium. Continuing to think along those lines, it'd be nice if we could use .gprel32 instead of .quad. This, I think, requires a tiny compiler patch to add a bit to force the surrounding function to load its gp so it'd be available for the exception handler.
This covers the single load/store case, what about the block routines such as clear_user? They certainly will still want to be able to bail out of the entire operation directly.
I'd suggest that this be done by exchanging the exception count + known register for a single abort address, since the nested exceptions we could have gotten with exception() ... end_exception() are no longer possible.
Oh yes, modules. We'd need to make modules register their tables on startup so that we can search those as well.
Comments? No rush on any of this really; we have lots of things to do in the meantime.
r~
|  |