Topic: standardize C++ behavior regarding dlopen()/dlclose()?


Author: bgreen@nas.nasa.gov (Bryan Green)
Date: 23 Jun 01 02:21:00 GMT
Raw View
It seems that I'm not alone in running into trouble with the behavior
of C++ in "plugins" loaded with dlopen() calls.  Since C++ is a
language for large systems, and the use of plugins is becoming a
common approach to large system design, it seems like standard C++
should be able to "play friendly" with plugins.

I'm aware of two problems:

1) Use of static objects in a plugin.
2) Use of RTTI type_info and dynamic_cast in plugins.

1) static objects
Since the standard states that destructors for static objects are
called after a program exits, the standard effectively makes the
developer choose between a) not using static objects in a plugin, or
b) never calling dlclose() on the plugin.
But consider what a plugin developer would expect the behavior to be.
If you are expecting your plugin to be unloaded as well as loaded,
then you expect your destructors to be called when the plugin is
unloaded, in the opposite order in which they were constructed when
the plugin was loaded.  The requirement that some global order with
respect to the application as a whole be maintained just doesnt make
sense in this context.  Shouldn't the scope for
construction/destruction be a library-scope, as opposed to
application-scope?
Applying a library-scope would not change the standard behavior as
currently defined for applications, but would make C++ behave properly
in a component (plugin) architecture.

2) RTTI
If two plugins share an abstract class by which they communicate, say
class T, shouldn't each module have a comparable definition of
'typeid(T)'? (assuming T has implementation details in a shared
library they each link against, or it is purely abstract and lives
only in a header file.)
That is, if one plugin passes a pointer 'p' of type T to a second
plugin, the second plugin should be able to successfully perform the
test
'typeid(*p) == typeid(T)'.

While this has worked for me so far with g++ and Irix CC, this now
fails with g++ 3.0.  Is this a bug?  I hope so.  It could be that my
configuration is screwed up somehow.  My question is: _should_ this be
a bug?
If this is not a C++ standardization issue, in what general forum
should this issue be addressed?  (My fear is that someone will say,
its not a standard-C++ issue, so you're out of luck!)  Is it a
question of C++ ABI standardization?

Thanks,
-bryan

      [ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
      [ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]




Author: marco@technoboredom.net (Marco Manfredini)
Date: 23 Jun 2001 14:42:06 -0400
Raw View
bgreen@nas.nasa.gov (Bryan Green) wrote in
news:6faaf36e.0106212009.afe2d51@posting.google.com:

> 1) Use of static objects in a plugin.

I agree. The lifetime of a static should bounded by the lifetime of the
code/data object that contains the static.

The Standard says a "Program" consists of one or more translation that
are linked together. I'd say this is true for a shared object file, that
does not have to be -linked- with the loading process (that's the
important restriction here)


> 2) RTTI
> If two plugins share an abstract class by which they communicate,
> say class T, shouldn't each module have a comparable definition of
> 'typeid(T)'? (assuming T has implementation details in a shared
> library they each link against, or it is purely abstract and lives
> only in a header file.)
> That is, if one plugin passes a pointer 'p' of type T to a second
> plugin, the second plugin should be able to successfully perform
> the test
> 'typeid(*p) == typeid(T)'.

typeid() returns a reference to a type_info. If this has to work with a
dynamic module, a run-time linker would have to merge the type_info's
from the module with the loader (which he isn't allowed to do, for the
sake of the statics, see above) . Now suppose the module and the loader
both define a class Floxi with -different- interfaces. Either a) because
they used different interface specifications or b) by a simple naming
accident (or for example both link to a helper library, for internal
purposes, but with different versions) etc..

Not good. To precent accidents like that, one could advise, that the
compiler generates a md5 of a type's full signature which could be part
of the mangled name...etc..but I don't think that this is going into the
right direction. C++ is very delicate about what happens -outside- of
the linkage: changing a virtual member function in some base class can
break the binary compatibility of a derived class etc..In any case it
would turn out that C++ had to define a standard ABI binary
interoperability, which I don't see, since there are already Corba &
other Middleware Standards, that define General Plugin Frameworks.

--
Marco
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: James Kanze <James.Kanze@dresdner-bank.com>
Date: 26 Jun 01 02:42:10 GMT
Raw View
Bryan Green wrote:

> It seems that I'm not alone in running into trouble with the
> behavior of C++ in "plugins" loaded with dlopen() calls.  Since C++
> is a language for large systems, and the use of plugins is becoming
> a common approach to large system design, it seems like standard C++
> should be able to "play friendly" with plugins.

Standard C++ requires that the link phase precede execution.  Any use
of shared libraries is non-standard, and will depend on implementation
specific guarantees.  All I can suggest is what a quality
implementation should do.

> I'm aware of two problems:

> 1) Use of static objects in a plugin.
> 2) Use of RTTI type_info and dynamic_cast in plugins.

> 1) static objects
> Since the standard states that destructors for static objects are
> called after a program exits, the standard effectively makes the
> developer choose between a) not using static objects in a plugin, or
> b) never calling dlclose() on the plugin.

Since the standard doesn't address the issue, an implementation can
actually do whatever it wants.  IMHO, a quality implementation will
call the destructors for the static objects when the shared library is
unloaded.  But you'll have to check the implementation for any real
guarantees.

> But consider what a plugin developer would expect the behavior to
> be.  If you are expecting your plugin to be unloaded as well as
> loaded, then you expect your destructors to be called when the
> plugin is unloaded, in the opposite order in which they were
> constructed when the plugin was loaded.

This is what I would expect, yes.

> The requirement that some global order with respect to the
> application as a whole be maintained just doesnt make sense in this
> context.  Shouldn't the scope for construction/destruction be a
> library-scope, as opposed to application-scope?  Applying a
> library-scope would not change the standard behavior as currently
> defined for applications, but would make C++ behave properly in a
> component (plugin) architecture.

As long as shared libraries aren't recognized by the standard, no
change is needed.  If shared libraries were to be recognized by a
future version of the standard, I think that the rule should be that
1) destructors for objects in the library are called when the library
is unloaded, and 2) order is maintained only within separate shared
libraries.

> 2) RTTI

> If two plugins share an abstract class by which they communicate,
> say class T, shouldn't each module have a comparable definition of
> 'typeid(T)'? (assuming T has implementation details in a shared
> library they each link against, or it is purely abstract and lives
> only in a header file.)  That is, if one plugin passes a pointer 'p'
> of type T to a second plugin, the second plugin should be able to
> successfully perform the test

> 'typeid(*p) == typeid(T)'.

> While this has worked for me so far with g++ and Irix CC, this now
> fails with g++ 3.0.  Is this a bug?  I hope so.  It could be that my
> configuration is screwed up somehow.  My question is: _should_ this
> be a bug?

Again, the standard doesn't address the issue directly.  On the other
hand, I see no reason why the behavior of typeid should depend on
whether the objects/types were loaded in the same shared library or
not.  I cannot imagine a reasonable implementation where the above
caused problems.

One point where one really has to pay attention with shared libraries,
however, is the one definition rule.  The definition of a class must
be the same in *all* of the libraries loaded, or you have undefined
behavior.  Also, it will occasionnally occur that different versions
of the compiler are incompatible; in such cases, all shared libraries
must be compiled with the same or compatible versions.

If you have not violated the one definition rule somewhere, and you
have recompiled all of the files, I imagine that a failure in the
above test would be treated as an error by the g++ development team.
(And let's face it, 3.0 is a version ending in .0.  I know that large
parts have been rewritten, and realistically, it would be a miricle if
they haven't introduced a few new errors.)

> If this is not a C++ standardization issue, in what general forum
> should this issue be addressed?  (My fear is that someone will say,
> its not a standard-C++ issue, so you're out of luck!)  Is it a
> question of C++ ABI standardization?

I'm not even sure that it would be covered by the ABI.  If you cannot
find the necessary information in the documentation of the
implementation, however, you should definitly raise the question in
the platform specific news group.

--
James Kanze                               mailto:kanze@gabi-soft.de
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
Ziegelh   ttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627


      [ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
      [ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]




Author: James Kanze <James.Kanze@dresdner-bank.com>
Date: 26 Jun 01 02:42:36 GMT
Raw View
Marco Manfredini wrote:

> typeid() returns a reference to a type_info. If this has to work
> with a dynamic module, a run-time linker would have to merge the
> type_info's from the module with the loader (which he isn't allowed
> to do, for the sake of the statics, see above).

Not at all.  The standard doesn't require that a given type only have
one instance of the typeid.  It requires that all instances of a given
type compare equal.  This can be done by ensuring that there is only
one instance for a given type, or by comparing some internal data
(complete class name, etc.)  If shared libraries makes ensuring a
single instance difficult, then the compiler must opt for the second
solution.

IMHO, a much more difficult problem is with templates.  How does the
compiler ensure that the addresses of the same function compare equal,
or that all instances of the same function share a common local static
variable?

> Now suppose the module and the loader both define a class Floxi with
> -different- interfaces.

The program has undefined behavior.  It doesn't matter what the
compiler does.

--
James Kanze                               mailto:kanze@gabi-soft.de
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
Ziegelh   ttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627


      [ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
      [ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]