lkml.org 
[lkml]   [1996]   [Apr]   [27]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    /
    Date
    From
    SubjectRe: signal(SIGFPE,SIG_IGN), possible solution?


    On Thu, 25 Apr 1996, Ben Wing wrote:
    >
    > Linus (who has obviously gotten a bit frazzled from working so hard
    > on the kernel lately) sees fit to rant:

    guilty as charged..

    > You've sent this whole long flame, but completely missed the point
    > that I was *NOT* talking about division by zero, but rather about
    > overflow -- e.g. if I divide 0xFFFFFFFFFFFFFFFF (a 64-bit number)
    > by 0xFFFFFFF (a 28-bit number) using the idivl instruction, the
    > processor issues an exception because the result does not fit into
    > 32 bit, but takes 36 bits. Returning MAX_INT here is NOT random --
    > it's the closest reasonable approximation.

    You don't see the problem, do you?

    I did notice that you were talking about overflow rather than division by
    zero, but that doesn't really change anything. It's the same thing: do we
    accept software that gives unpredictable results or not?

    For _you_ it is acceptable to get a rounding error.

    For somebody else it may not be. And the kernel has to assume the worst,
    for the simple reason that it can't _tell_ what the program wants. I have
    yet to add the ESP-driver to the kernel to read the mind of the user..

    > And yes, I had considered longjmp()ing out of a signal handler.
    > However, this idivl instruction occurs HUNDREDS, maybe thousands,
    > of times in the renderer. Can you imagine the pain involved in
    > ensuring there was a setjmp() everywhere?

    You don't need to do that. In fact, you don't _want_ to do that, for the
    same reason you don't want to check the arguments to the division.

    Now, you're obviously using inline assembly or something, as I don't
    think gcc will ever compile any division to do the 64/32->32 thing. If
    you're ready to do that kind of thing, then you must be ready to play
    around a bit in a signal handler or play with longjump.

    In a signal handler, you could even do

    #include <asm/sigcontext.h>

    void sigfpe_handler(int signr, struct sigcontext context)
    {
    fixup(&context);
    }

    Where the "fixup()" routine does a disassembly of the %eip that faulted,
    and jumps over it. (Depending on how you have written your inline macro,
    you may know that the division is always done with a register, so then
    the "fixup" can be as simple as just doing

    context.eip += 2;
    context.eax = ~0;
    context.edx = ~0;

    and that's it..

    Notice that by NOT doing in the kernel, you win, because
    - your program can know when the overflow occurs, and in some cases
    that's important so that it can mark a certain pixel as having
    overflowed.
    - the kernel doesn't have to worry about what the user wants to do this
    time.
    - because you know what you're doing, you don't need to do extra work
    like the kernel would have had to (you can skip the disassembly, for
    example.
    - you have a chance in hell of porting your program to some other
    platform in the future (the linux signal stack bears a remarkable
    similarity to the IBCS2 standard x86 unix signal stack)
    - you can round the result any way _you_ want to. In fact, you can play
    games to make it very easy for youself by having a fixed sequence of
    instructions to handle it.

    And because you handle it youself, you have any flexibility you want to
    with error handling. For example, you can make the fault handler jump to
    some specified point in your function with something like this:

    __label__ error_handler;
    __asm__("divl %2"
    :"=a" (low), "=d" (high)
    :"g" (divisor), "c" (&&error_handler))
    ... do normal cases ...

    error_handler:
    ... check against zero division or overflow, so whatever you want to ..


    Then, your handler for SIGFPE needs only to do something like

    context.eip = context.ecx;

    and there is no overhead at all for taking a fault and _knowing_ about it
    for the normal case when you don't fault (well, the __asm__ statement
    sets up %ecx to point to the fault handler, but thatäs one instruction
    and one register, so it may well be worth it for you).

    In short, you've been barking up the wrong tree all the time. Instead of
    trying to ignore SIGFPE which is arguably totally idiotic, you should
    _handle_ them. If your application is speed critical, then the handling
    might be something like the above (whcih is certainly not pretty, but at
    least it's _clever_).

    And if it isn't _that_ speed critical, then you can do it portably right,
    and use siglongjmp().

    Notice? By doing it in the user process, you have the _choice_, and you
    can do it right. If Linux did it in the kernel, you could never do it
    right for everybody..

    > Do I REALLY have to set up my own signal handler that looked at
    > the assembly and stepped the program counter over the instruction?
    > Isn't that more than a bit absurd?

    "more than a bit absurd"?

    That's _exactly_ what you asked _me_ to do in the kernel.. How does it
    feel to have the tables turned on you?

    You essentially asked me to do the same "absurd" thing, but for no real
    reason, and from the kernel which is unpageable and where every little
    piece of memory _stays_ in memory even though 99.95% of all programs
    don't care or even _want_ this functionality..

    THAT is why I think people have no grip on reality on this thing.

    > Is it braindamage if I expect that if I say 'signal (SIGFPE, SIG_IGN)'
    > then the machine will ignore the SIGFPE and continue in its merry
    > way? If the only way to get this braindamage is to **explicitly
    > request it**, how can it possibly make all other programs unsafe?

    It's braindead, because you're confusing the act of handling a signal
    with the act of _generating_ one.

    When your program does a "signal(SIGFPE, SIG_IGN)", that means that it
    will ignore any signals sent to it, and the kernel honours that.

    That does not mean that the kernel should stop generating them (or try to
    make the hardware stop generating them). You told the symptoms to go
    away, but you didn't fix the problem - why do you expect the problem to
    go away?

    Oh, btw, this discussion _has_ resulted in something. As of 1.3.96, the
    kernel will totally ignore and override any signal blocking and/or
    SIG_IGN for errors that it can't (or won't) recover from.

    "My name is Linus Torvalds, you messed with my kernel, prepare to die"

    Linus


    \
     
     \ /
      Last update: 2005-03-22 13:37    [W:2.489 / U:0.064 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site