Question

How do I get a C++ application including a loaded ada shared library to generate a core dump when crashing?

I have a C++ application which loads a ada shared library, inside the ada code I get a stack overflow error which causes program termination along with the console output:

raised STORAGE ERROR

No core dump file is generated even thou I have issued a "ulimit -c unlimited" before starting the application.

Same thing happens if I send a kill SIGSEGV to the application.

Sending kill SIGSEGV to another application that does not use the ada dll generates a core dump file just the way I want it to.

Found some information here: http://objectmix.com/ada/301203-gnat-fstack-check-does-work.html

UPDATED! As mentioned by Adrien, there is no contradiction, -s sets the stack limit while -c sets the core file limit.

Still the problem remains. I checked the flags when building the ada library and the fstack-check flag was not set, so it should generate a core dump.

Althou I haven't tried it yet, it seems somewhat strange. It mentions the -fstack-check compiler option + setting the GNAT_STACK_LIMIT variable but at the same time refers to the ulimit command which seems like a contradiction, setting "ulimit -c " is the only way I know of getting a core dump to be generated at the time of crash, if this infers with the fstack-check option then we have a catch 22.

Was it helpful?

Solution

Now, almost 2 years later (still working at the same company as Kristofer did when he asked the question), was the question raised again - and finally I think that I understands why no core-dump is generated!!

The problem is caused by the Ada run-time, which by default implements a signal handler for some POSIX-signals (for Linux: SIGABRT, SIGFPE, SIGILL, SIGSEGV and SIGBUS). For GNAT/linux the signal handler is called __gnat_error_handler in a-init.c, which looks something like this:

static void
__gnat_error_handler (int sig)
{
  struct Exception_Data *exception;
  char *msg;
  static int recurse = 0;
  ...
  switch (sig)
    {
    case SIGSEGV:

      if (recurse)
      {
        exception = &constraint_error;
        msg = "SIGSEGV";
      }
      else
      {
        ...
        msg = "stack overflow (or erroneous memory access)";
        exception = &storage_error;
      }
      break;
     }
    recurse = 0;
    Raise_From_Signal_Handler (exception, msg);
 }

This handler is "process wide", and will be called by any trigged signal, no matter from which part of the process it originates from (no matter if coded in Ada/C/C++...).

When called, the handler rises an Ada-exception and leaves it to the Ada runtime to find an appropriate exception handler - if no such handler is found (eg. when an SIGSEGV is generated by any part of the C++-code), the Ada-runtime falls back to just terminate the process and just leave a simple printout from __gnat_error_handler (eg. "stack overflow (or erroneous memory access)").

http://www2.adacore.com/gap-static/GNAT_Book/html/node25.htm

To prevent Ada-runtime from handling a POSIX-signal, and convert it to an Ada-exception, it is possible to disable the default beahviour by using

pragma Interrupt_State (Name => value, State => SYSTEM | RUNTIME | USER);,

eg. to disable handling of SIGSEGV, define

Pragma Interrupt_State(SIGSEGV, SYSTEM);

in your Ada-code - now the system's default behaviour will be trigged when a SIGSEGV is raised, and a core-dump will be generated that allows you to trace down the origin of the problem!

I think this is a quite important issue to be aware of when mixing Ada and C/C++ on *NIX-platforms, since it may mislead you to think that problems origins from the Ada-code(since the printout indicates an exception generated from Ada) when the real source of the problem lays in the C/C++-code...

Although it is probably safe to disable the Ada-runtime default handling of SIGSEGV (I guess no sane programmer using this in any "expected" error handling... Well, maybe used in aviation software or similar, when some sort of "last resort" functionallity needs to be maintained to avoid something really bad from happening..) i guess a bit caution should be taken then "overriding" the Ada-runtime handling for signals.

One issue may be the SIGFPE-signal, which also raises an Ada Constraint_Error-exception by default. This type of exception may be used by the Ada-code as an "excpected behaviour". Disabling SIGFPE by Pragma Interrupt_State may seriously affect the execution of the Ada-code, and crash your application during "normal circumstances" - on the other hand will any division by zero in the C/C++-code trig the Ada-exception handling mechanism, and leave you without any real trace of the origin of the problem...

OTHER TIPS

This looks to me like a really good use for your AdaCore support. You aren't liable to find a whole lot of folk outside that company who are that familiar with the implications of the interactions between Gnu Ada's runtime and C++'s.

I would suggest for debugging the Ada code that you try putting in a last-ditch exception handler around everything, which in turn dumps the exception stack. Most vendors have some way of doing that, usually based off of Ada.Exceptions.Exception_Information and Ada.Exceptions.Exception_Message.

I found a discussion from a security perspective (finding malware). Basically there are 10 signals that you can try, SIGSEGV is only one of them.

It seems you can simply call sigaction(SIGSEGV, 0, SIG_DFL); to restore the default signal behavior.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top