0

I need to fix a huge commercial C++ project under Linux. It is build with GCC (specifically g++), under customly written build system in a company. It is built and run under Raspberry Pi 4B.

When it is built dynamically (without -static flag) then everything works fine and program runs correctly and gives correct results.

But when it is built statically (with -static flag) then program crashes on any first exception. To check that program is static I run ldd ./prog and it shows not a dynamic executable. If to do same ldd command for dynamical build then it shows a bunch of dynamical libraries.

I decided to leave whole build config totally same but in main file just replaced main() function with following simple code:

int main() {
    std::cout << "Before exc" << std::endl;
    try {
        throw std::runtime_error("Hello, World!");
    } catch (std::exception const & ex) {
        std::cout << "Thrown '" << ex.what() << "'." << std::endl;
    }
    std::cout << "After exc" << std::endl;
}

If whole project with main() from above is built in dynamic then it correctly shows following, as expected:

Before exc
Thrown 'Hello, World!'.
After exc

And if I build exactly same thing statically then output is such:

Before exc
terminate called after throwing an instance of 'std::runtime_error'
terminate called recursively
Aborted

In other words message Before exc is shown, then exception is thrown, but instead of catching it terminate is called and program aborted.

If with same build system I build not the whole project, but statically build just a single file main.cpp with code above, then it outputs expected thing. So the problem here with all the project files and linked libraries, not with the build system (probably).

Maybe some global variables of the project when get initialized modify program in such a way that throwing an exception causes termination. But all global variables were initialized correctly because main() runs correctly till point where exception is thrown. Global variables only made static build exception-unfriendly.

Does anyone have any idea what is happenning and when C++ exception calls terminate at the very point of throw? Does there exist any program config that can set exception throwing to call std::terminate()?

Also anything else is working correctly, only exceptions suffer. In other words statical program runs correctly for some time and does correct actions, but crashes on very first exception.

Build command that was used is below, all names modified intentionally, also only 2-3 object and library files are left, in real example there are dozens of same .a libraries and dozens of .o objects. First goes compile command that compiles .cpp to .o, second goes archive command that aggregates .o into .a, finally goes link command that links all .o and .a files into final single-file program:

"ccache" "g++" "-finput-charset=UTF-8" "-fexec-charset=UTF-8" "-std=c++2a" "-fpic" "-c" "-static" "-static-libgcc" "-static-libstdc++" "-Iinc_dir/" "-I." "-g" "-march=armv6zk+fp" "-ffast-math" "-xc++" "-O0" "-DSOME_DEFINE" "-omisc.o" "misc.cpp"

"ar" "rs" "lib1.a" "lib1.o"

"ccache" "g++" "-finput-charset=UTF-8" "-fexec-charset=UTF-8" "-std=c++2a" "-fpic" "-static" "-static-libgcc" "-static-libstdc++" "-oprog" "main.o" "misc.o" "lib1.a" "lib2.a"  "-L./lib_dir/" "-lboost_program_options" "-lboost_system" "-lpthread" "-lc" "-lgcc" "-ldl" "-lstdc++" "-lstdc++fs"

By removing parts of project through binary search just figured out that reason is inside misc.cpp. Somehow if I include it into build then exceptions crash, if I don't include then exceptions don't crash.

As far as I understand the only reason why misc.cpp can make exceptions crash is only due to some global variables inside main.cpp doing such settings of a program that make exceptions crashable. To remind, I included throw.cpp into project that throws toy exception as shown in code above. misc.cpp is not used at all from throw.cpp. Also main.cpp is not included in build. Basically I left only misc.cpp file from project, and only it is causing errors. All the rest of project build config are only external libraries that don't cause crashing.

Right now I made a minimal reproducible example. Now my project consists of throw.cpp and misc.cpp plus a bunch of external linked libraries. This throw.cpp contains just main() with throwing toy code that I mentioned in the beginning of my question, it has nothing else, it doesn't include anything except iostream and stdexcept. misc.cpp has no main(), and misc.cpp is not used at all, just compiled and linked. So now if I remove misc.cpp from linking then exception are thrown in main() of throw.cpp correctly without error. If I link misc.cpp (that I don't use at all, just link) then exceptions thrown in main() of throw.cpp make program crash, only in static link.

Arty
  • 14,883
  • 6
  • 36
  • 69
  • can you post the full compile command? – Bernd Nov 20 '21 at 17:27
  • 1
    Did you try to debug the code? – Quimby Nov 20 '21 at 17:31
  • @Bernd Just added to the very end of my Question a dump of build commands, names modified intentionally. Please put a look. – Arty Nov 20 '21 at 17:40
  • @Quimby No, didn't debug this yet, just observed a crash in static build, while dynamic build with totally same configs and project files runs correctly for hours. – Arty Nov 20 '21 at 17:41
  • Maybe relevant? https://stackoverflow.com/questions/70048236/throwing-any-c-exception-terminates-program-in-static-build-under-linux-gcc?noredirect=1#comment123826264_70048236 – Quimby Nov 20 '21 at 17:48
  • @Quimby Your link above in previous comment just links to my comment, click that link. It doesn't link to relevant other question/solution. – Arty Nov 20 '21 at 17:50
  • @Quimby By doing binary search on removing parts of project I figured out that removing `misc.cpp` makes exception non-crashable, just adding `misc.cpp` make them crashing. Hooray! Now the idea is to somehow figure out how `misc.cpp` that has no main() can influence exceptions in main() of other file. Only by some global variables that modify state of a program to make it non-workable. – Arty Nov 20 '21 at 17:52
  • 1
    I'm guessing you are mixing standard libraries, if you throw a `std::exception` from one standard library and then try catching it in another it might not be caught. All parts of your project need to be built with the same compiler and compiler settings – Alan Birtles Nov 20 '21 at 17:57
  • @AlanBirtles Right now I made a minimal reproducible example. Now my project consists of `throw.cpp` and `misc.cpp` plus a bunch of external linked libraries. This `throw.cpp` contains just main() with throwing toy code that I mentioned in the beginning of my answer,it has nothing else, it doesn't include anythin except `iostream` and `stdexcept`. `misc.cpp` has no main(), and `misc.cpp` is not used at all, just compiled and linked.So now if I remove `misc.cpp` from linking then exception are thrown in main() correctly without error.If I link `misc.cpp` (that I don't use) then exceptions crash – Arty Nov 20 '21 at 18:02
  • @Arty Sorry, copied wrong tab I guess, I meant https://stackoverflow.com/questions/57476533/why-is-statically-linking-glibc-discouraged – Quimby Nov 20 '21 at 18:02
  • I'd guess the `bunch of external linked libraries` are the cause of the problem then – Alan Birtles Nov 20 '21 at 18:03
  • @AlanBirtles If these bunch of libraries is still linked then program doesn't crash if I only remove `misc.cpp` from linking. But if I add `misc.cpp` then program crashes. To remind my main() is located in `throw.cpp` that doesn't use any .h/.cpp from the project, it include only `iostream` and `stdexcept`. So just linking `misc.cpp` causes crash, removing `misc.cpp` solves the issue, exceptions don't crash. – Arty Nov 20 '21 at 18:05
  • It's difficult to help without a [mre] – Alan Birtles Nov 20 '21 at 18:08
  • @AlanBirtles As far as I understand then the only reason why including any file into linking like `misc.cpp` makes program crash, is only because some global variables inside `misc.cpp` when initialized at program start set a program into some bad state. I can't imagine how just linking some file may cause problems except for global variables running some functions that make program into bad state. – Arty Nov 20 '21 at 18:08
  • If you have an ODR violation then you are in the realms of undefined behaviour. With UB, weird stuff happens including making seemingly unrelated changes changing the behaviour – Alan Birtles Nov 20 '21 at 18:10
  • @AlanBirtles Minimal example is far from being available, first of all because `misc.cpp` is huge. Secondly is because it is commercial. Now I have to binary search by removing some parts of `misc.cpp`, especially global variables, till I figure out what's causing problem. By creating this Question on stack-overflow I just wanted to find out maybe there is some well known reason when exception may terminate program in static build. Unless there exist such well known reason the only way for me then is to by myself remove parts of a `misc.cpp` until I find crashing code. – Arty Nov 20 '21 at 18:11
  • @AlanBirtles Do you know by any chance, if it is possible anyhow in C++ to set a custom function-handler on even of throwing any exception? Or it is not possible to register something for any exception? Just want to figure out if some function maybe had registered something crashable on thowing ANY exception. Or it is not doable? – Arty Nov 20 '21 at 18:34
  • Could be that `misc.cpp` uses something from on of those external libraries that's messing up exception handling. Linking with `misc.cpp` requires linking in all of its external dependencies, so could pull in code that wouldn't otherwise be there. – Pete Becker Nov 20 '21 at 18:34
  • @PeteBecker Yes, very possible. BTW, do you know how messing exception can be achieved at all? What kind of code (even Undefined Behaviour code) can make throwing exception in totally other parts of code make impossible, with crash? I don't know of such code that breaks only exceptions. To remind - this statical project runs perfectly well for several minutes until very first exception is thrown. So everything works fine and not crashes, crash is only caused by very first exception. – Arty Nov 20 '21 at 18:37
  • `catch (...)` should catch any exception. It definitely sounds like you are using multiple standard libraries and therefore have multiple definitions of `std::exception` and/or multiple copies of the exception handling routines, either way it's UB and weird stuff can or will happen – Alan Birtles Nov 20 '21 at 18:48
  • @AlanBirtles Found reason of crash, see [my answer](https://stackoverflow.com/a/70053026/941531). – Arty Nov 21 '21 at 08:42
  • @PeteBecker Found crash reason, put a look at [my answer](https://stackoverflow.com/a/70053026/941531). – Arty Nov 21 '21 at 08:42

1 Answers1

1

After 6 hours of search for problem, I managed to figure out that the reason is not in the project files / libraries / build system.

It appeared that the problem is in combining any C++ code with exceptions and C code with usage of sscanf() (or even maybe any C standard library function).

Probably this happens due to Raspberry Pi 4B having too old GCC/G++ version in default installation, g++ -v produces:

Target: arm-linux-gnueabihf
...
Thread model: posix
gcc version 8.3.0 (Raspbian 8.3.0-6+rpi1)

so version is 8.3.0, while there exists already release 11.2 of GCC.

I created minimal example that crashes:

File z.cpp:

#include <stdexcept>
#include <iostream>

int z2_f();

int main() {
    std::cout << "Before exc" << std::endl;
    try {
        throw std::runtime_error("Hello, World!");
    } catch (std::exception const & ex) {
        std::cout << "Thrown '" << ex.what() << "'." << std::endl;
    } catch (...) {
        std::cout << "Thrown 'Unknown'." << std::endl;
    }
    std::cout << "After exc" << std::endl;
    std::cout << z2_f() << std::endl;
}

File z2.cpp:

#include <cstdio>

int z2_f() {
    char s[] = "123";
    int x = 0;
    sscanf(s, "%d", &x);
    return x;
}

Compile commands:

"g++" "-std=c++2a" "-c" "-static" "-O0" "-oz.o" "z.cpp"

"g++" "-std=c++2a" "-c" "-static" "-O0" "-oz2.o" "z2.cpp"

"g++" "-std=c++2a" "-static" "-oprog.exe" "z.o" "z2.o"

After compiling and running prog.exe it shows in console:

Before exc
terminate called after throwing an instance of 'std::runtime_error'
terminate called recursively
Aborted

If one compiles and runs only first file z.cpp (with removed use of z2_f()), then everything works as expected without crash:

Before exc
Thrown 'Hello, World!'.
After exc

I managed to solve the issue by installing GCC/G++ version 10.1.0 according to this tutorial (WayBack mirror of page). Basically binary author's release of GCC can be obtained through git clone https://bitbucket.org/sol_prog/raspberry-pi-gcc-binary.git --depth=1.

With this version toy example above runs without crash, original large project also runs without crashes.

To remind, before I had version 8.3.0 of GCC, which was crashing on exception.

My uname -a shows:

Linux raspberrypi 5.4.51-v7l+ #1333 SMP Mon Aug 10 16:51:40 BST 2020 armv7l GNU/Linux

while lsb_release -d shows:

Raspbian GNU/Linux 10 (buster)
Arty
  • 14,883
  • 6
  • 36
  • 69