Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Try-catch doesn't work in shared library?

(This is like my other question but this one is for another thing, even if it's related)

I've got a big issue in my project. I've got a library which handle XML and can throw exception. And, using it for creating a configuration file class show my first bug : exceptions aren't handled in the library, at all, and with every exception.

In the library I wrote :

try {
    throw std::exception();
}
catch (...)
{
    printf("caught\n");
}

But, the exception isn't handled and call std::terminate immediately :

terminate called after throwing an instance of 'std::exception'
  what():  std::exception

The compilation flags are the simplest one : -fPIC -std=c++11 -g -Wall for the library, and -std=c++11 -g -Wall for the executable (plus the libraries and variant build defines). Also, I'm using G++ 5.4.0, under Linux (Linux Mint to be precise).

This is my main :

#include "ns/core/C_Configuration.hpp"

#include <iostream>

using namespace std;
using namespace ns;


int
main (int argc, char** argv)
{
    try {
        C_Configuration c ("test.xml");
        c.load ("test.xml");
    } catch (const std::exception& ex) {
        cout << ex.what () << endl;
    } catch (...) {
        cout << "Caught." << endl;
    }


    return 0;
}

The C_Configuration.hpp :

#include <string>
#include <exception>


namespace ns
{


    class C_Configuration
    {

    public:

        C_Configuration (std::string);


        bool load (std::string file);

    };


} // namespace ns

And, this is the C_Configuration.cpp :

#include "ns/core/C_Configuration.hpp"

#include <cstdio>

using namespace std;


namespace ns
{



C_Configuration::C_Configuration (string)
{ }



bool
C_Configuration::load (string file)
{

    try {
        throw exception();
    } catch (const exception& ex) {
        printf ("In C_Configuration : %s\n", ex.what ());
    } catch (...) {
        printf ("In C_Configuration : caught\n");
    }


    return true;
}


} // namespace ns

Buid commands :

g++ -m64 -g -shared -fPIC -std=c++11 -o libns_framework.so C_Configuration.cpp
g++ -m64 -g -L. -o exe main.cpp -lns_framework 

Note : I give this example, but it works as expected, the exception is caught in the library, not like in my main project. If you want to investigate more, you can check my project code here.

The problem is when :

  • The try-catch block is inside the library ;
  • The try-catch block is outside the library ;

In any case, the exception is thrown inside the library. But, exception thrown outside are caught in the executable code :

int
main (int argc, char** argv)
{
    try {
        throw 1;
    } catch (...) {
        cout << "Caught" << endl;
    }

    // Useless code

    return 0;
}

This code just write Caught in the output.

So, my question is simple : Is C++ exception not handled within libraries, or I just forgot a compilation flag ? I need to say in the executable code, the exceptions work fine.

Thanks for your help.

EDIT : Oh god, my bad. Problem solved. Into the deepest part of my build configuration, an ld took the place of g++. Now the exception is working fine. Thanks for you help.

like image 209
Nils 'Linkpy' Reid Avatar asked Feb 06 '23 09:02

Nils 'Linkpy' Reid


2 Answers

Simple. Never use ld with C++. I changed all ld commands in my project to g++ but seems I forgot for this library.

In short, I was using ld for building my library but g++ for the main executable. So the exceptions worked in the executable but not in the library because ld does not includes the C++ libraries which handle the exception system.

like image 86
Nils 'Linkpy' Reid Avatar answered Feb 09 '23 15:02

Nils 'Linkpy' Reid


According to gcc manual:

if a library or main executable is supposed to throw or catch exceptions, you must link it using the G++ or GCJ driver, as appropriate for the languages used in the program, or using the option -shared-libgcc, such that it is linked with the shared libgcc.

Shared libraries (in C++ and Java) have that flag set by default, but not main executables. In any case, you should use it on both.

Test Case:

lib.cpp:

#include "lib.hpp"
#include <string>
#include <iostream>

using namespace std;

int function_throws_int() {
    try {
        throw 2;
    }
    catch (...) {
        cout << "int throws lib" << endl;
        throw;
    }

    return -1;
}

int function_throws_string() {
    try {
        throw std::string("throw");
    }
    catch (...) {
        cout << "throws string lib" << endl;
        throw;
    }
}

lib.hpp:

int function_throws_int();
int function_throws_string();

Compile command line: g++ -m64 -g -shared-libgcc -shared -fPIC -std=c++11 -o libtest.so lib.cpp

main.cpp:

#include "lib.hpp"
#include <string>
#include <iostream>

using namespace std;

int main(int argc, char ** argv) {
    try {
        function_throws_int();
    }
    catch (const string & e) {
        cout << "string caught main" << endl;
    }
    catch (int i) {
        cout << "int caught main" << endl;
    }

    return 0;
}

Compile command line: g++ -m64 -g -shared-libgcc -o main -L. main.cpp -ltest

Execute: LD_LIBRARY_PATH=. ./main

Output:

int throws lib
int caught main
like image 43
Bruno Ferreira Avatar answered Feb 09 '23 14:02

Bruno Ferreira