egcs 2.95.1 exceptions don't work in .so (Solaris) and possible solution

Peter Ludemann ludemann@inxight.com
Sat Oct 9 09:23:00 GMT 1999


Martin:

Could you please send me the modified Makefile you used to get my
example to work?  I tried duplicating your description of how to make
it work and failed (on both Solaris 2.6 and 7; my only Solaris 2.5.1
system has a hopelessly out-of-date gcc).

Using my interpretation of your changes, I did get exceptions *within*
a .so to work, but not across a .so boundary.  Attached are my test
results and the test source (I've possibly added some more tests since
I posted my bug report).

The cost of sjlj exceptions isn't significant for my application,
because exceptions aren't supposed to happen and because my try/catch
is only at the top level.

BTW, there's a note at Cygnus's web pages to not use binutils on
Solaris, so I'm using Sun's linker and assembler.

Thank-you for your help.  If you can point me at more documentation on
shared libraries and exceptions, I will investigate this more on my
own.

- peter

> Date: Sat, 9 Oct 1999 11:19:00 +0200
> X-Authentication-Warning: mira.isdn.cs.tu-berlin.de: martin set sender to martin@mira.isdn.cs.tu-berlin.de using -f
> From: "Martin v. Loewis" <martin@mira.isdn.cs.tu-berlin.de>
> CC: gcc-bugs@gcc.gnu.org
> User-Agent: SEMI/1.13.3 (Komaiko) FLIM/1.12.5 (Hirahata) Emacs/20.4 (i586-pc-linux-gnu) MULE/4.0 (HANANOEN)
> Content-Type: text/plain; charset=US-ASCII
> 
> > Exception-handling code that works fine with everything linked
> > together fails when the code is put into a Solaris shared-object
> > (.so).  The solution appears to be to use "-fsjlj-exceptions", which
> > unfortunately isn't documented anywhere outside of gcc/except.c.
> > 
> > If -fsjlj-exceptions is the correct solution on Solaris 7, could a
> > comment about that please be put into the gcc documentation.  If
> > it's not the correct solution, what is?
> > 
> > Also, it would be nice if the documentation mentioned that "-shared"
> > by itself isn't enough for creating a .so; "-nostdlib" needs to also
> > be specified (unless (a) you want to also specify -mimpure-text and
> > (b) you really do want to have the gcc library in your .so).
> 
> Thanks for your bug report. I got your example working on Solaris
> 2.5.1, by making the following changes to your Makefile:
> 
> - Do not use -dynamic to link the executable
> - Do not use -nostdlib to create the shared library
> - Link shared library with "gcc" driver, instead of "g++" driver
> 
> You absolutely *need* libgcc.a linked in your .so, or exception
> handling will not work.
> 
> Using sjlj exceptions has a number of drawbacks: you have to recompile
> all libraries to use it, and it has a significant runtime overhead. It
> is auto-selected on platforms that don't support DWARF2 exception
> handling, and therefore not intended to be used in an application
> makefile. Perhaps that is the reason it is not documented.
> 
> Please note that I was using binutils, I don't know what would happen
> with the system linker. Also, I could not test this on a more recent
> version of Solaris.
> 
> Hope this helps,
> Martin
> 
> 

-------------- test results ---------------

cd ~/src/test-so/
gmake clean default
rm -f *.o *.a *.so *~ test-dynamic test-static
g++  -g -Wall -fPIC -mno-app-regs  -o test.o -c test.cpp
g++  -g -Wall -fPIC -mno-app-regs  -o testlib.o -c testlib.cpp
ar -rcv libtestlib.a testlib.o
a - testlib.o
ar: writing libtestlib.a
g++  -g -Wall -fPIC -mno-app-regs  -o test-static test.o libtestlib.a
ldd test-static
	libm.so.1 =>	 /usr/lib/libm.so.1
	libc.so.1 =>	 /usr/lib/libc.so.1
	libdl.so.1 =>	 /usr/lib/libdl.so.1
gcc   -o libtestlib.so -shared testlib.o -lgcc
ldd libtestlib.so
pwd
/home/ludemann/src/test-so
echo /bin/sh R`pwd`
/bin/sh R/home/ludemann/src/test-so
g++  -g -Wall -fPIC -mno-app-regs  -o test-dynamic test.o -L. -ltestlib -R`pwd`
ldd test-dynamic
	libtestlib.so =>	 /home/ludemann/src/test-so/libtestlib.so
	libm.so.1 =>	 /usr/lib/libm.so.1
	libc.so.1 =>	 /usr/lib/libc.so.1
	libdl.so.1 =>	 /usr/lib/libdl.so.1
./test-static "a bb" ccc
./test-static: 2 arguments ...
      1: a bb
      2: ccc
global = 4
global2 = 5
Caught exception in main: Global::throwUp exception
./test-dynamic "a bb" ccc
./test-dynamic: 2 arguments ...
      1: a bb
      2: ccc
global = 4
global2 = 5
Segmentation Fault
gmake: *** [run] Error 139

Compilation exited abnormally with code 2 at Sat Oct  9 09:16:34

-------------- end of test results ---------------


-------------- test files ---------------

mkdir -p test-so
cat >test-so/Makefile <<'---end---'
# $Id: Makefile,v 1.9 1999/10/09 16:18:24 ludemann Exp $

CXX=g++
CC=gcc

# CXX=/shared/local/bin/g++
# CXX=/shared/solaris2.6/bin/g++

# It's not clear whether or not we need -mno-app-regs or -ffreestanding,
# but they don't seem to cause harm.
# The following aren't needed:  # -fno-gnu-linker -fexceptions -fnew-exceptions -fguiding-decls # -fcheck-memory-usageb # -v
#    -nostartfiles -nodefaultlibs # -mimpure-text -Xlinker -ztextoff  # -Wl,soname,$@

# CXXFLAGS2=-fsjlj-exceptions -mno-app-regs -ffreestanding

CXXFLAGS+=-g -Wall -fPIC -mno-app-regs $(CXXFLAGS2)

PURIFY=
# PURIFY=purify

# The default rule

default: test-static test-dynamic run

.PHONY: run clean tar cpio shar ci

RUNARGS="a bb" ccc

run::
	./test-static $(RUNARGS)

run::
	./test-dynamic $(RUNARGS)

run::
	@echo
	@echo "Tests succeeded"

clean::
	$(RM) *.o *.a *.so *~ test-dynamic test-static

test-static: test.o libtestlib.a Makefile
	$(PURIFY) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ test.o libtestlib.a
	-ldd $@

test-dynamic: test.o libtestlib.so Makefile
	pwd
	echo $(SHELL) R`pwd`
	@# $(PURIFY) $(CXX) $(CXXFLAGS) -dynamic -o $@ test.o -L. -ltestlib -R`pwd`
	$(PURIFY) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ test.o -L. -ltestlib -R`pwd`
	-ldd $@

%.o: %.cpp testlib.hpp Makefile
	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ -c $*.cpp

libtestlib.a: testlib.o
	$(AR) -rcv $@ $+

libtestlib.so: testlib.o
	@# $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ -shared -nostdlib $+
	@# note use of gcc instead of g++ and the addition of -lgcc:
	$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -shared $+ -lgcc
	-ldd $@

TARSRC=README Makefile test.cpp testlib.cpp testlib.hpp

tar: $(TMPDIR)/test-so.tgz-uuencode

$(TMPDIR)/test-so.tgz: $(TARSRC) Makefile
	cd ..; tar cvvzf $@ $(TARSRC:%=test-so/%)

$(TMPDIR)/test-so.tgz-uuencode: $(TMPDIR)/test-so.tgz Makefile
	cd $(TMPDIR); uuencode test-so.tgz test-so.tgz > $@

cpio: $(TMPDIR)/test-so.cpio

$(TMPDIR)/test-so.cpio: $(TARSRC) Makefile
	cd ..; ls -1 $(TARSRC:%=test-so/%) | cpio -ocv > $@

shar: $(TMPDIR)/test-so.shar

$(TMPDIR)/test-so.shar: $(TARSRC) Makefile
	$(RM) $@
	echo "mkdir -p test-so" >> $@
	for i in $(TARSRC); do					\
	    echo "cat >test-so/$$i <<'---end---'" >> $@;	\
	    cat $$i >> $@;					\
	    echo "---end---" >> $@;				\
	done

ci:
	ci -u $(TARSRC)
---end---
cat >test-so/test.cpp <<'---end---'
// $Id: test.cpp,v 1.4 1999/10/04 21:28:13 ludemann Exp $

#include <iostream>
#include <string>
#include <cstdlib>

#include "testlib.hpp"

using namespace std;

extern const char test_id[] = "@(#) $Id: test.cpp,v 1.4 1999/10/04 21:28:13 ludemann Exp $";

Global* globalPtr = new GlobalPlusOne(3);
Global& global = *globalPtr;
GlobalPlusOne global2 ( 4 );

void testV(const string& name, int v, int shouldBe)
{
  cout << name << " = " << v << endl;
  if (v != shouldBe) {
    cout << "*** Global value not " << shouldBe << ": " << v << endl;
    exit(1);
  }
}

int main(int argc, const char * const argv[])
{
  Args args(argc, argv);
  args.display(cout);

  testV("global", global.getValue(), 4);
  testV("global2", global2.getValue(), 5);

  if (1) {			// testing an exception across .so boundary
    try {
      global2.throwUp();
    } catch (const AnError& err) {
      cout << "Caught exception in main: " << err.msg << endl;
    }
  }

  delete globalPtr;
  globalPtr = 0;

  return 0;
}
---end---
cat >test-so/testlib.cpp <<'---end---'
// $Id: testlib.cpp,v 1.4 1999/10/04 21:28:13 ludemann Exp $

#include <iostream>

#include "testlib.hpp"

using namespace std;

extern const char testlib_id[] = "@(#) $Id: testlib.cpp,v 1.4 1999/10/04 21:28:13 ludemann Exp $";

Args::Args(int argc, const char * const * argv)
  :
  argc_(argc),
  argv_(argv)
{
  // cout << "--- Args::Args(" << argc << ")" << endl;
}

Args::~Args()
{
  // cout << "--- Args::~Args()" << endl;
}

void Args::display(ostream& ostr)
{
  ostr << argv_[0] << ": " << argc_-1 << " arguments ..." << endl;
  for (int i = 1; i < argc_; ++i) {
    ostr << "      " << i << ": " << argv_[i] << endl;
  }
}

Global::Global(int value)
  :
  value_(value)
{
  // cout << "--- Global::Global(" << value << ")" << endl;
}

Global::~Global()
{
  // cout << "--- Global::~Global(value[actual]=" << value_ << ")" << endl;
}

int Global::getValue()
{
  return value_;
}

void Global::throwUp()
{
  throw AnError("Global::throwUp exception");
}

GlobalPlusOne::GlobalPlusOne(int value)
  :
  Global(value)
{
  // cout << "-- GlobalPlusOne::GlobalPlusOne(" << value << ")" << endl;
}

GlobalPlusOne::~GlobalPlusOne()
{
  // cout << "--- GlobalPlusOne::~GlobalPlusOne(value[actual]=" << super::getValue() << ")" << endl;
}

int GlobalPlusOne::getValue()
{
  if (1) {
    try {
      // throwUp(); // xThrowUp();
    } catch (const AnError& err) {
      cout << "Caught exception in GlobalPlusOne::getValue(): `" << err.msg << "'" << endl;
      // throw;
    }
  }

  return super::getValue() + 1;
}


void xThrowUp()
{
   throw AnError("xThrowUp exception");
}


AnError::AnError(const string& the_msg)
  :
  msg(the_msg)
{
  // cout << "--- AnError(" << msg << ")" << endl;
}
---end---
cat >test-so/testlib.hpp <<'---end---'
// $Id: testlib.hpp,v 1.3 1999/10/04 18:47:33 ludemann Exp $

#ifndef testlib_INCLUDED

#define testlib_INCLUDED

#include <iostream>
#include <string>

class Args
{
public:
  Args(int argc, const char * const * argv);
  virtual ~Args();
  virtual void display(ostream& ostr);
private:
  Args();
  Args(const Args&);
  Args& operator = (const Args&);
protected:
  int argc_;
  const char * const * argv_;
};

class AnError
{
public:
  AnError(const string& the_msg);
  string msg;
};

class Global
{
public:
  Global(int value);
  virtual ~Global();
  virtual int getValue();
  virtual void throwUp();
private:
  Global();
  Global(const Global&);
  Global& operator = (const Global&);
protected:
  typedef Global super;
private:
  int value_;
};

class GlobalPlusOne : public Global
{
public:
  GlobalPlusOne(int value);
  virtual ~GlobalPlusOne();
  virtual int getValue();
};

void xThrowUp();

#endif
---end---

-------------- end of test files ---------------


More information about the Gcc-bugs mailing list