Strange exception handling behaviour with dlopen()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello,

I encountered strange aborts, caused by terminate(), probably because somehow an exception
was thrown from a "noexcept" function, or no exception handler was found.
All related to dlopen().

(I narrowed down this code from a bigger signal handling solution.)

1.
Let's start with a shared library, which throws a float during its static initialization phase:

// Compile with:
// g++ -shared -fPIC throw_in_static_init.cpp -o throw_in_static_init.so
#include <iostream>

static int throw314()
{
    std::cout << "throw314() called\n" << std::flush;
    throw 3.14f;
}

static int throwDuringInitialization = throw314();

2.
Load this library run-time with dlopen() and try to handle the exception:

// Compile with:
// g++ -Wl,-rpath,$PWD catch_load_exception_ABORTS_in_place.cpp -ldl
// ABORTS with terminate()
#include <iostream>
#include <dlfcn.h>

int main() {

    try {
        void* handle = dlopen("throw_in_static_init.so", RTLD_LAZY);
        std::cout << "Lib loading: " << (handle ? "successfull" : "failed") << "\n";
    } catch (float f) {
        std::cout << "Exception caught in main function: " << f << std::endl;
    }

    std::cout << "main() returns\n";
    return 0;
}

Result:
throw314() called
terminate called after throwing an instance of 'float'
Aborted (core dumped)

I checked this code with godbolt.org Compiler Explorer and found, that if I put the line

std::cout << "Lib loading: " << (handle ? "successfull" : "failed") << "\n";

in comment, then no exception handlig code was generated.
So maybe dlopen() was treated as "noexcept", and since an exception was thrown, it caused
terminate() to be called?

Let's move on:

3.
// Compile with:
// g++ -Wl,-rpath,$PWD catch_load_exception_OK_via_function.cpp -ldl
// Works as expected.
#include <iostream>
#include <dlfcn.h>

void loadFailing()
{
    void* handle = dlopen("throw_in_static_init.so", RTLD_LAZY);
    std::cout << "Lib loading: " << (handle ? "successfull" : "failed") << "\n";
}

int main() {

    try {
        loadFailing();
    } catch (float f) {
        std::cout << "Exception caught in main function: " << f << std::endl;
    }

    std::cout << "main() returns\n";
    return 0;
}

Result:
throw314() called
Exception caught in main function: 3.14
main() returns

WOW! It works now!

But why? How did dlopen() become a possible-throwing function now?

Let's move on and give it a small twist.

4.
// Compile with:
// g++ -Wl,-rpath,$PWD catch_load_exception_OK_in_place_w_guard_object.cpp -ldl
// Works as expected.
#include <iostream>
#include <dlfcn.h>

struct Guard
{
    Guard()
    {
        std::cout << "Guard() called\n";
    }
};

int main() {

    try {
        Guard g;
        
        void* handle = dlopen("throw_in_static_init.so", RTLD_LAZY);
        std::cout << "Lib loading: " << (handle ? "successfull" : "failed") << "\n";
    } catch (float f) {
        std::cout << "Exception caught in main function: " << f << std::endl;
    }

    std::cout << "main() returns\n";
    return 0;
}

Result:
Guard() called
throw314() called
Exception caught in main function: 3.14
main() returns

Works again, though dlopen() was called in the "try" block, and not through a function.
So why?

5.
Let's add a local variable:

// Compile with:
// g++ -Wl,-rpath,$PWD catch_load_exception_ABORTS_in_place_w_guard_object_w_awkward.cpp -ldl
// ABORTS with terminate()
#include <iostream>
#include <dlfcn.h>
#include <unordered_map>

struct Guard
{
    Guard()
    {
        std::cout << "Guard() called\n";
    }
};

int main() {

    try {
        Guard g;
        
        std::unordered_map<int, int> awkward;
        
        void* handle = dlopen("throw_in_static_init.so", RTLD_LAZY);
        std::cout << "Lib loading: " << (handle ? "successfull" : "failed") << "\n";
    } catch (float f) {
        std::cout << "Exception caught in main function: " << f << std::endl;
    }

    std::cout << "main() returns\n";
    return 0;
}

Result:
Guard() called
throw314() called
terminate called after throwing an instance of 'float'
Aborted (core dumped)

FAILS Again!

What did this "awkward" variable do to the code, so that it fails now?
According to Compiler Explorer code was generated to the try-catch block.

Can anybody please enlighten me, what is going on here?

Tested with:
gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
gcc (Ubuntu 8.4.0-1ubuntu1~18.04) 8.4.0

Any help appreciated!
Regards,
--
Leslie
Software Developer
Deep Machine Learning Realization (AMS ADAS BUD ENG RL)
Advanced Driver Assistance Systems, Autonomous Mobility and Safety
Budapest, Continental Hungary Ltd.





[Index of Archives]     [Linux C Programming]     [Linux Kernel]     [eCos]     [Fedora Development]     [Fedora Announce]     [Autoconf]     [The DWARVES Debugging Tools]     [Yosemite Campsites]     [Yosemite News]     [Linux GCC]

  Powered by Linux