This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
Re: egcs 2.95.1 exceptions don't work in .so (Solaris) and possible solution
- To: martin at mira dot isdn dot cs dot tu-berlin dot de
- Subject: Re: egcs 2.95.1 exceptions don't work in .so (Solaris) and possible solution
- From: Peter Ludemann <ludemann at inxight dot com>
- Date: Sat, 9 Oct 1999 09:22:39 -0700 (PDT)
- Cc: ludemann at inxight dot com, gcc-bugs at gcc dot gnu dot org
- Organization: Inxight Software, Inc. http://www.inxight.com
- Phone: +1.650.813.6806
- References: <199910042206.PAA13968@elbe.inxight.pa.xerox.com> <199910090919.LAA12081@mira.isdn.cs.tu-berlin.de>
- Reply-to: ludemann at inxight dot com
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 ---------------