mirror of
git://git.savannah.gnu.org/libtool.git
synced 2024-11-21 01:40:57 +08:00
402 lines
10 KiB
Plaintext
402 lines
10 KiB
Plaintext
# exception.at -- test C++ exception handling with libtool -*- Autotest -*-
|
|
#
|
|
# Copyright (C) 2010-2019, 2021-2024 Free Software Foundation, Inc.
|
|
#
|
|
# This file is part of GNU Libtool.
|
|
#
|
|
# GNU Libtool is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License as
|
|
# published by the Free Software Foundation; either version 2 of
|
|
# the License, or (at your option) any later version.
|
|
#
|
|
# GNU Libtool is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with GNU Libtool; see the file COPYING. If not, a copy
|
|
# can be downloaded from http://www.gnu.org/licenses/gpl.html,
|
|
# or obtained by writing to the Free Software Foundation, Inc.,
|
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
####
|
|
|
|
AT_SETUP([C++ exception handling])
|
|
AT_KEYWORDS([libtool])
|
|
AT_KEYWORDS([libltdl])
|
|
|
|
: ${LTDLINCL="-I$abs_top_srcdir/libltdl"}
|
|
: ${LIBLTDL="$abs_builddir/../libltdl/libltdlc.la"}
|
|
|
|
# Skip this test when called from:
|
|
# make distcheck DISTCHECK_CONFIGURE_FLAGS=--disable-ltdl-install
|
|
AT_CHECK([case $LIBLTDL in #(
|
|
*/_inst/lib/*) test -f "$LIBLTDL" || (exit 77) ;;
|
|
esac], [], [ignore])
|
|
|
|
CPPFLAGS="$LTDLINCL $CPPFLAGS"
|
|
|
|
# Win32 (and cygwin) notes
|
|
# ------------------------
|
|
# When using C++ and Win32 DLLs, data types used in the DLL's interface
|
|
# that are other-than-POD, must have vtables, typeinfo, and other
|
|
# elements resolved when the client is linked. This includes exception
|
|
# classes. Therefore, the exception class "modexc" thrown by the
|
|
# dynamically-loaded module must be defined in a separate DLL, to which
|
|
# both that module and main must be directly linked; hence, the 'common'
|
|
# library. Not using a 'common' library in this manner represents an
|
|
# ODR violation, unless the platform's runtime loader is capable of
|
|
# rationalizing vague linkage items such as vtables, typeinfo, and
|
|
# typename elements) at runtime. The Win32 loader is not capable of
|
|
# this, but some ELF loaders appear to be.
|
|
#
|
|
# Similar treatment is not necessary for liba (e.g. the libexc
|
|
# exception class), because that library is not dynamically loaded. As a
|
|
# consequence, vague linkage items for the class libexc are resolved at
|
|
# link time using the vague linkage rules, for both Win32 and other
|
|
# (e.g. ELF) platforms.
|
|
#
|
|
# Also, when linking a C++ DLL with another C++ DLL, some versions of
|
|
# the GNU toolchain on Win32 (or cygwin) mistakenly re-export symbols
|
|
# that were imported from the other DLL, when the client DLL is linked
|
|
# using -export-all-symbols. Similar issues MAY also arise with those
|
|
# versions of the GNU toolchain if using the libtool link flags
|
|
# -export-symbols LIST or -export-symbols-regex REGEX, if any symbols
|
|
# from the dependency, rather than client, library are listed (or match
|
|
# the regex). However, in this test, none of these situations apply,
|
|
# so we don't directly address it. Otherwise, the correct mechanism
|
|
# would be to avoid all of those flags, and instead explicitly decorate
|
|
# all symbols with appropriate __declspec (dllexport) or
|
|
# __declspec (dllimport) flags when building the DLLs and the clients.
|
|
#
|
|
# For more information, see these two threads:
|
|
# http://lists.gnu.org/archive/html/bug-libtool/2010-06/msg00069.html
|
|
# http://cygwin.com/ml/cygwin/2010-06/msg00392.html
|
|
# To sum up: C++ is complicated.
|
|
AT_DATA([common.h],
|
|
[[#ifndef LIBTOOL_TEST_COMMON_HEADER
|
|
#define LIBTOOL_TEST_COMMON_HEADER
|
|
|
|
#include <exception>
|
|
#include <string>
|
|
|
|
#if defined __CYGWIN__ || defined _WIN32
|
|
# if defined DLL_EXPORT || defined USING_COMMON_DLL
|
|
# if defined LIBTOOL_TEST_IN_COMMON
|
|
# define COMMON_IMPEXP __declspec (dllexport)
|
|
# else
|
|
# define COMMON_IMPEXP __declspec (dllimport)
|
|
# endif
|
|
# else
|
|
# define COMMON_IMPEXP
|
|
# endif
|
|
#else
|
|
# define COMMON_IMPEXP
|
|
#endif
|
|
|
|
class COMMON_IMPEXP modexc : public std::exception {
|
|
public:
|
|
modexc (std::string str) : message (str) { }
|
|
~modexc () throw () { }
|
|
virtual const char *what () const throw ()
|
|
{
|
|
return message.c_str ();
|
|
}
|
|
private:
|
|
std::string message;
|
|
};
|
|
|
|
extern "C" int COMMON_IMPEXP common_dummy (void);
|
|
#endif
|
|
]])
|
|
|
|
AT_DATA([common.cpp],
|
|
[[#define LIBTOOL_TEST_IN_COMMON
|
|
#include "common.h"
|
|
|
|
extern "C"
|
|
int common_dummy (void)
|
|
{
|
|
return 0;
|
|
}
|
|
]])
|
|
|
|
AT_DATA([module.h],
|
|
[[#include "common.h"
|
|
|
|
#if defined __CYGWIN__ || defined _WIN32
|
|
# if defined DLL_EXPORT || defined USING_MODULE_DLL
|
|
# if defined LIBTOOL_TEST_IN_MODULE
|
|
# define MODULE_IMPEXP __declspec (dllexport)
|
|
# else
|
|
# define MODULE_IMPEXP __declspec (dllimport)
|
|
# endif
|
|
# else
|
|
# define MODULE_IMPEXP
|
|
# endif
|
|
#else
|
|
# define MODULE_IMPEXP
|
|
#endif
|
|
|
|
extern "C" int MODULE_IMPEXP modfoo () throw (modexc);
|
|
]])
|
|
|
|
AT_DATA([module.cpp],
|
|
[[#include <iostream>
|
|
#define LIBTOOL_TEST_IN_MODULE
|
|
#include "module.h"
|
|
|
|
int modbar (void) throw (modexc)
|
|
{
|
|
throw modexc ("exception in module");
|
|
}
|
|
|
|
extern "C"
|
|
int modfoo (void) throw (modexc)
|
|
{
|
|
try {
|
|
modbar ();
|
|
}
|
|
catch (modexc e) {
|
|
std::cerr << "caught inside module: " << e.what () << '\n';
|
|
throw modexc ("exception from module");
|
|
}
|
|
return 0;
|
|
}
|
|
]])
|
|
|
|
AT_DATA([lib.h],
|
|
[[#include <exception>
|
|
#include <string>
|
|
|
|
|
|
#if defined __CYGWIN__ || defined _WIN32
|
|
# if defined DLL_EXPORT || defined USING_LIB_DLL
|
|
# if defined LIBTOOL_TEST_IN_LIB
|
|
# define LIB_IMPEXP __declspec (dllexport)
|
|
# else
|
|
# define LIB_IMPEXP __declspec (dllimport)
|
|
# endif
|
|
# else
|
|
# define LIB_IMPEXP
|
|
# endif
|
|
#else
|
|
# define LIB_IMPEXP
|
|
#endif
|
|
|
|
class LIB_IMPEXP libexc : public std::exception {
|
|
public:
|
|
libexc (std::string str) : message (str) { }
|
|
~libexc () throw () { }
|
|
virtual const char *what () const throw ()
|
|
{
|
|
return message.c_str ();
|
|
}
|
|
private:
|
|
std::string message;
|
|
};
|
|
int LIB_IMPEXP libfoo () throw (libexc);
|
|
]])
|
|
|
|
AT_DATA([lib.cpp],
|
|
[[#include <iostream>
|
|
#define LIBTOOL_TEST_IN_LIB
|
|
#include "lib.h"
|
|
|
|
int libbar (void) throw (libexc)
|
|
{
|
|
throw libexc ("exception in library");
|
|
}
|
|
|
|
int libfoo (void) throw (libexc)
|
|
{
|
|
try {
|
|
libbar ();
|
|
}
|
|
catch (libexc e) {
|
|
std::cerr << "caught inside lib: " << e.what () << '\n';
|
|
throw libexc ("exception from library");
|
|
}
|
|
return 0;
|
|
}
|
|
]])
|
|
|
|
AT_DATA([main.cpp],
|
|
[[#include <ltdl.h>
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <exception>
|
|
#include <string>
|
|
#include "common.h"
|
|
#include "lib.h"
|
|
#include "module.h"
|
|
|
|
class exc : public std::exception {
|
|
public:
|
|
exc (std::string str) : message (str) { }
|
|
~exc () throw () { }
|
|
virtual const char *what () const throw ()
|
|
{
|
|
return message.c_str ();
|
|
}
|
|
private:
|
|
std::string message;
|
|
};
|
|
|
|
int foo (void) throw (exc)
|
|
{
|
|
throw exc ("exception in program");
|
|
return 0;
|
|
}
|
|
|
|
int exceptions_in_prog (void)
|
|
{
|
|
std::cerr << "exceptions_in_prog\n";
|
|
try {
|
|
foo ();
|
|
}
|
|
catch (exc e) {
|
|
std::cerr << "caught: " << e.what () << '\n';
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int exceptions_in_lib (void)
|
|
{
|
|
std::cerr << "exceptions_in_lib\n";
|
|
try {
|
|
libfoo ();
|
|
}
|
|
catch (libexc e) {
|
|
std::cerr << "caught: " << e.what () << '\n';
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int exceptions_in_module (void)
|
|
{
|
|
std::cerr << "exceptions_in_module\n";
|
|
|
|
if (lt_dlinit ())
|
|
{
|
|
std::cerr << "init error: " << lt_dlerror () << '\n';
|
|
return 1;
|
|
}
|
|
|
|
// Some systems need RTLD_GLOBAL for exceptions to work in modules.
|
|
lt_dladvise advise;
|
|
if (lt_dladvise_init (&advise) || lt_dladvise_global (&advise))
|
|
{
|
|
std::cerr << "error setting advise global\n";
|
|
return 1;
|
|
}
|
|
|
|
lt_dlhandle handle = lt_dlopenadvise ("module.la", advise);
|
|
if (handle == NULL)
|
|
{
|
|
std::cerr << "dlopen failed: " << lt_dlerror () << '\n';
|
|
return 1;
|
|
}
|
|
lt_dladvise_destroy (&advise);
|
|
|
|
typedef int (*pfun) (void);
|
|
pfun pf = (pfun) lt_dlsym (handle, "modfoo");
|
|
if (pf == NULL)
|
|
{
|
|
std::cerr << "dlsym failed: " << lt_dlerror () << '\n';
|
|
return 1;
|
|
}
|
|
|
|
bool exception_caught = false;
|
|
try {
|
|
(*pf) ();
|
|
}
|
|
catch (modexc e) {
|
|
std::cerr << "caught: " << e.what () << '\n';
|
|
exception_caught = true;
|
|
}
|
|
|
|
/* Only close the module after all of its objects have gone out of scope. */
|
|
if (exception_caught)
|
|
{
|
|
if (lt_dlclose (handle))
|
|
{
|
|
std::cerr << "dlclose failed: " << lt_dlerror () << '\n';
|
|
return 1;
|
|
}
|
|
if (lt_dlexit ())
|
|
{
|
|
std::cerr << "lt_dlexit failed: " << lt_dlerror () << '\n';
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int main (void)
|
|
{
|
|
|
|
LTDL_SET_PRELOADED_SYMBOLS();
|
|
|
|
if (exceptions_in_prog ())
|
|
return 1;
|
|
if (exceptions_in_lib ())
|
|
return 1;
|
|
if (exceptions_in_module ())
|
|
return 1;
|
|
return 0;
|
|
}
|
|
]])
|
|
|
|
inst=`pwd`/inst
|
|
libdir=$inst/lib
|
|
bindir=$inst/bin
|
|
moddir=$inst/mod
|
|
mkdir l m $inst $libdir $bindir $moddir
|
|
|
|
# If the C++ compiler isn't capable, don't bother.
|
|
AT_CHECK([$CXX $CPPFLAGS $CXXFLAGS -DUSING_COMMON_DLL -DUSING_MODULE_DLL -DUSING_LIB_DLL -c main.cpp || exit 77], [], [ignore], [ignore])
|
|
|
|
AT_CHECK([$LIBTOOL --mode=compile --tag=CXX $CXX $CPPFLAGS $CXXFLAGS -c common.cpp],
|
|
[], [ignore], [ignore])
|
|
AT_CHECK([$LIBTOOL --mode=compile --tag=CXX $CXX $CPPFLAGS $CXXFLAGS -c lib.cpp],
|
|
[], [ignore], [ignore])
|
|
AT_CHECK([$LIBTOOL --mode=compile --tag=CXX $CXX $CPPFLAGS $CXXFLAGS -DUSING_COMMON_DLL -c module.cpp],
|
|
[], [ignore], [ignore])
|
|
|
|
AT_CHECK([$LIBTOOL --mode=link --tag=CXX $CXX $CXXFLAGS $LDFLAGS -o l/libcommon.la ]dnl
|
|
[common.lo -no-undefined -version-info 1:0:0 -rpath $libdir],
|
|
[], [ignore], [ignore])
|
|
AT_CHECK([$LIBTOOL --mode=link --tag=CXX $CXX $CXXFLAGS $LDFLAGS -o l/liba.la ]dnl
|
|
[lib.lo -no-undefined -version-info 1:0:0 -rpath $libdir],
|
|
[], [ignore], [ignore])
|
|
AT_CHECK([$LIBTOOL --mode=link --tag=CXX $CXX $CXXFLAGS $LDFLAGS -o m/module.la ]dnl
|
|
[module.lo l/libcommon.la -module -avoid-version -no-undefined -rpath $moddir],
|
|
[], [ignore], [ignore])
|
|
|
|
# We need -export-dynamic for the exception handling in modules to work.
|
|
AT_CHECK([$LIBTOOL --mode=link --tag=CXX $CXX $CXXFLAGS $LDFLAGS -o main$EXEEXT ]dnl
|
|
[main.$OBJEXT l/liba.la l/libcommon.la -dlopen m/module.la $LIBLTDL -export-dynamic],
|
|
[], [ignore], [ignore])
|
|
|
|
LT_AT_NOINST_EXEC_CHECK([./main], [-dlopen m/module.la], [], [ignore], [ignore])
|
|
|
|
AT_CHECK([$LIBTOOL --mode=install cp l/libcommon.la $libdir],
|
|
[], [ignore], [ignore])
|
|
AT_CHECK([$LIBTOOL --mode=install cp l/liba.la $libdir],
|
|
[], [ignore], [ignore])
|
|
AT_CHECK([$LIBTOOL --mode=install cp m/module.la $moddir],
|
|
[], [ignore], [ignore])
|
|
AT_CHECK([$LIBTOOL --mode=install cp main$EXEEXT $bindir],
|
|
[], [ignore], [ignore])
|
|
rm -rf l m main$EXEEXT
|
|
|
|
LTDL_LIBRARY_PATH=$moddir
|
|
export LTDL_LIBRARY_PATH
|
|
LT_AT_EXEC_CHECK([$bindir/main], [], [ignore], [ignore])
|
|
|
|
AT_CLEANUP
|