Bug 70387 - -fnon-call-exceptions has no effect
Summary: -fnon-call-exceptions has no effect
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: target (show other bugs)
Version: 5.3.0
: P3 enhancement
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: EH
Depends on:
Blocks:
 
Reported: 2016-03-24 06:54 UTC by jwjagersma
Modified: 2024-11-28 21:00 UTC (History)
2 users (show)

See Also:
Host:
Target: i586-pc-msdosdjgpp
Build:
Known to work: 3.3.4
Known to fail: 4.3.4, 5.3.0, 6.3.0
Last reconfirmed: 2016-03-24 00:00:00


Attachments
Test case (937 bytes, text/plain)
2016-03-24 11:01 UTC, jwjagersma
Details
Test case 2 (224 bytes, text/plain)
2016-03-25 17:19 UTC, jwjagersma
Details

Note You need to log in before you can comment on or make changes to this bug.
Description jwjagersma 2016-03-24 06:54:07 UTC
I wrote some code to trap a hardware exception, and transfer control to a signal handler which throws a C++ exception.
From the documentation, I believe '-fnon-call-exceptions' (and/or '-fasynchronous-unwind-tables') should allow me to do this, but it doesn't work.
It appears that gcc doesn't consider that non-call instructions may throw, and silently omits the try/catch block around it. The exception is only caught when the trapped instruction appears in a (non-inlined) function, or inbetween two function calls.


--- Output from 'gcc -v':

Using built-in specs.
COLLECT_GCC=D:\msys64\usr\local\djgpp\i586-pc-msdosdjgpp\bin\gcc.exe
COLLECT_LTO_WRAPPER=D:/msys64/usr/local/djgpp/lib/gcc/../../libexec/gcc/i586-pc-msdosdjgpp/5.3.0/lto-wrapper.exe
Target: i586-pc-msdosdjgpp
Configured with: ../gnu/gcc-5.30/configure --host= --build=x86_64-w64-mingw32 --target=i586-pc-msdosdjgpp --program-prefix=i586-pc-msdosdjgpp- --prefix=/usr/local/djgpp --disable-nls --disable-plugin --disable-lto --enable-lto --enable-libquadmath-support --with-gmp=/home/JW/build-djgpp/build/djcross-gcc-5.3.0/tmpinst --with-mpfr=/home/JW/build-djgpp/build/djcross-gcc-5.3.0/tmpinst --with-mpc=/home/JW/build-djgpp/build/djcross-gcc-5.3.0/tmpinst --enable-version-specific-runtime-libs --enable-languages=c,c++
Thread model: single
gcc version 5.3.0 (GCC)


--- Example code: (implementation details omitted)

void throw_exception()
{ 
    throw std::runtime_error("Division by zero!");
}

__attribute__((noinline))
void try_div0()
{
    cout << 1 / 0 << endl;
}

void nop() { asm(""); }

int main()
{
    // this class traps a hardware exception (division by zero, in this case) and calls the supplied lambda function.
    exception_wrapper div0_exc { 0, [] (exception_frame* frame, bool)
    { 
            // only handle exceptions that occured in our own code
        if (frame->address.segment != get_cs()) return false;
            // sub <fault esp>, 4;
        frame->stack.offset -= 4;
            // get pointer to [<fault esp>]
        auto* stack = reinterpret_cast<std::uintptr_t *>(frame->stack.offset);
            // mov [<fault esp>], <fault address>;
        *stack = frame->address.offset;
            // resume at throw_exception()
        frame->address.offset = reinterpret_cast<std::uintptr_t>(throw_exception);
        return true;
    } };

    try
    {   // thrown from inside a function, this exception is caught.
        try_div0();
    }
    catch (std::exception& e) { cout << "oops: " << e.what() << endl; }
    
    try
    {   // thrown inbetween two function calls, this exception is caught.
        nop();
        cout << 1 / 0 << endl;
        nop();
    }
    catch (std::exception& e) { cout << "oops: " << e.what() << endl; }
    
    try
    {   // throws, but is NOT CAUGHT!
        cout << 1 / 0 << endl;
    }
    catch (std::exception& e) { cout << "oops: " << e.what() << endl; }
}
Comment 1 Richard Biener 2016-03-24 09:55:44 UTC
We need a complete testcase ("implementation details omitted" doesn't help).

The EH info is present as far as I can see
Comment 2 jwjagersma 2016-03-24 11:01:29 UTC
Created attachment 38077 [details]
Test case

I reduced my code to the bare minimum required to reproduce this issue.

Compile with djgpp:
"g++ -std=gnu++14 -fnon-call-exceptions -fasynchronous-unwind-tables -o traptest.exe traptest.cpp"
Comment 3 jwjagersma 2016-03-25 16:39:56 UTC
Possibly interesting observation; the exception can be caught when using a pointer as divisor:

int i = 0;
int* volatile p = &i;
try
{
    std::cout << 1 / *p << std::endl;
}
catch (std::exception& e) 
{ 
    std::cout << "oops: " << e.what() << std::endl;
}
Comment 4 jwjagersma 2016-03-25 17:19:02 UTC
Created attachment 38096 [details]
Test case 2

Generic test case, which doesn't require djgpp or a DOS machine. (Assuming throwing from inline asm is similar enough)

compile with:
"g++ -std=gnu++14 -fnon-call-exceptions throw_from_asm.cpp"
Comment 5 jwjagersma 2017-03-28 15:03:57 UTC
Bumping this, I hope it will be resolved someday.
I found a reference to the same issue, with another test case using posix signal handlers:
https://cygwin.com/ml/cygwin/2010-07/msg00195.html
This claims it used to work on 3.3.4, and failed from 4.3.4 on.
Comment 6 Drea Pinski 2021-06-04 00:21:02 UTC
(In reply to jwjagersma from comment #4)
> Created attachment 38096 [details]
> Test case 2
> 
> Generic test case, which doesn't require djgpp or a DOS machine. (Assuming
> throwing from inline asm is similar enough)
> 
> compile with:
> "g++ -std=gnu++14 -fnon-call-exceptions throw_from_asm.cpp"

Yes GCC adds no unwind info and it is hard to do from an inline-asm since GCC has no information on what the inline-asm could do. So that part is more of a documentation rather than anything else there.

As far as the other one, throwing from an interrupt in DOS requires you have to have a MD_FALLBACK_FRAME_STATE_FOR defined which is not done for djgpp and would need to be custom for your interrupt handler. I don't know the best way forward for this really except to say there is not much to be done here unless someone steps up and adds interrupt handler support to djgpp and then adds throwing through the interrupt handler support too.