# on the commandline of the linker will determine which path
# (compiled-in or SHLIB_PATH) will be searched first.
#
+# +I initproc routine gets called at load and unload time for
+# shl_load calls, but appears to never be called for link-time
+# specified libraries.
+# +e sym exports symbol and supposedly prevents other symbols
+# from being exported, according to the man page, but the
+# latter bit doesn't actually seem to work
+# -O +dpv should display any routines eliminated as unused, but -b
+# apparently turns that off
*-*-hpux*)
- if test "$krb5_cv_prog_gcc" = yes; then
- PICFLAGS=-fPIC
- else
- PICFLAGS=+z
- fi
INSTALL_SHLIB='$(INSTALL)'
SHLIBEXT=.sl
SHLIBVEXT='.$(LIBMAJOR).$(LIBMINOR)'
SHLIBSEXT='.$(LIBMAJOR)'
RPATH_FLAG='-Wl,+b,'
if test "$krb5_cv_prog_gcc" = yes; then
+ PICFLAGS=-fPIC
SHLIB_EXPFLAGS='-Wl,+s -Wl,+b,$(SHLIB_RDIRS) $(SHLIB_DIRS) $(SHLIB_EXPLIBS)'
- LDCOMBINE='gcc -fPIC -shared -Wl,+h,lib$(LIBBASE)$(SHLIBSEXT)'
+ LDCOMBINE='gcc -fPIC -shared -Wl,+h,lib$(LIBBASE)$(SHLIBSEXT) -Wl,-c,hpux10.exports'
else
+ PICFLAGS=+z
SHLIB_EXPFLAGS='+s +b $(SHLIB_RDIRS) $(SHLIB_DIRS) $(SHLIB_EXPLIBS)'
- LDCOMBINE='ld -b +h lib$(LIBBASE)$(SHLIBSEXT)'
+ LDCOMBINE='ld -b +h lib$(LIBBASE)$(SHLIBSEXT) -c hpux10.exports'
fi
CC_LINK_SHARED='$(CC) $(PROG_LIBPATH) -Wl,+s $(RPATH_FLAG)$(PROG_RPATH) $(CFLAGS) $(LDFLAGS)'
CC_LINK_STATIC='$(CC) $(PROG_LIBPATH) $(CFLAGS) $(LDFLAGS)'
RUN_ENV='SHLIB_PATH=`echo $(PROG_LIBPATH) | sed -e "s/-L//g" -e "s/ /:/g"`; export SHLIB_PATH;'
+ SHLIB_EXPORT_FILE_DEP=hpux10.exports
+ # Do *not* set use_linker_init_option=yes here, because in the
+ # case where the library is specified at program link time, the
+ # initialization function appears not to get called, only for
+ # shl_load. But for finalization functions, the shl_load case
+ # is the one we care about.
+ #
+ # Not setting use_linker_init_option here should cause compilation
+ # failures if the user tries to disable delayed initialization.
+ use_linker_fini_option=yes
;;
mips-sgi-irix6.3) # This is a Kludge; see below
At top level, before the functions are defined or even declared:
MAKE_INIT_FUNCTION(init_fn);
MAKE_FINI_FUNCTION(fini_fn);
+ Then:
int init_fn(void) { ... }
void fini_fn(void) { if (INITIALIZER_RAN(init_fn)) ... }
-
In code, in the same file:
err = CALL_INIT_FUNCTION(init_fn);
To trigger or verify the initializer invocation from another file,
- an additional function must be created.
+ a helper function must be created.
+
+ This model handles both the load-time execution (Windows) and
+ delayed execution (pthread_once) approaches, and should be able to
+ guarantee in both cases that the init function is run once, in one
+ thread, before other stuff in the library is done; furthermore, the
+ finalization code should only run if the initialization code did.
+ (Maybe I could've made the "if INITIALIZER_RAN" test implicit, via
+ another function hidden in macros, but this is hairy enough
+ already.)
The init_fn and fini_fn names should be chosen such that any
exported names staring with those names, and optionally followed by
the library in question.
+ There's also PROGRAM_EXITING() currently always defined as zero.
+ If there's some trivial way to find out if the fini function is
+ being called because the program that the library is linked into is
+ exiting, we can just skip all the work because the resources are
+ about to be freed up anyways. Generally this is likely to be the
+ same as distinguishing whether the library was loaded dynamically
+ while the program was running, or loaded as part of program
+ startup. On most platforms, I don't think we can distinguish these
+ cases easily, and it's probably not worth expending any significant
+ effort. (Note in particular that atexit() won't do, because if the
+ library is explicitly loaded and unloaded, it would have to be able
+ to deregister the atexit callback function. Also, the system limit
+ on atexit callbacks may be small.)
+
+
Implementation outline:
Windows: MAKE_FINI_FUNCTION creates a symbol with a magic name that
just check the flag) and returns the stored error code (or the
pthread_once error).
+ (That's the basic idea. With some debugging assert() calls and
+ such, it's a bit more complicated. And we also need to handle
+ doing the pthread test at run time on systems where that works, so
+ we use the k5_once_t stuff instead.)
+
UNIX, with compiler support: MAKE_FINI_FUNCTION declares the
function as a destructor, and the run time linker support or
whatever will cause it to be invoked when the library is unloaded,
UNIX, no library finalization support: The finalization function
never runs, and we leak memory. Tough.
+ DELAY_INITIALIZER will be defined by the configure script if we
+ want to use k5_once instead of load-time initialization. That'll
+ be the preferred method on most systems except Windows, where we
+ have to initialize some mutexes.
+
+
For maximum flexibility in defining the macros, the function name
parameter should be a simple name, not even a macro defined as
another name. The function should have a unique name, and should
conform to whatever namespace is used by the library in question.
+ (We do have export lists, but (1) they're not used for all
+ platforms, and (2) they're not used for static libraries.)
If the macro expansion needs the function to have been declared, it
must include a declaration. If it is not necessary for the symbol
This is going to be compiler- and environment-specific, and may
require some support at library build time, and/or "asm"
- statements.
+ statements. But through macro expansion and auxiliary functions,
+ we should be able to handle most things except #pragma.
It's okay for this code to require that the library be built
with the same compiler and compiler options throughout, but
work, we'll only have memory leaks in a load/use/unload cycle. If
anyone (like, say, the OS vendor) complains about this, they can
tell us how to get a shared library finalization function invoked
- automatically. */
+ automatically.
+
+ Currently there's --disable-delayed-initialization for preventing
+ the initialization from being delayed on UNIX, but that's mainly
+ just for testing the linker options for initialization, and will
+ probably be removed at some point. */
/* Helper macros. */
multiple active threads mucking around in our library at this
point. So ignore the once_t object and just look at the flag.
- XXX Could we have problems with memory coherence between
- processors if we don't invoke mutex/once routines? */
+ XXX Could we have problems with memory coherence between processors
+ if we don't invoke mutex/once routines? Probably not, the
+ application code should already be coordinating things such that
+ the library code is not in use by this point, and memory
+ synchronization will be needed there. */
# define INITIALIZER_RAN(NAME) \
(JOIN__2(NAME, once).did_run && JOIN__2(NAME, once).error == 0)
/* Run initializer at load time, via GCC/C++ hook magic. */
# ifdef USE_LINKER_INIT_OPTION
+ /* Both gcc and linker option?? Favor gcc. */
# define MAYBE_DUMMY_INIT(NAME) \
void JOIN__2(NAME, auxinit) () { }
# else
matter what compiler we're using. Do it the same way
regardless. */
-# define MAKE_FINI_FUNCTION(NAME) \
+# ifdef __hpux
+
+ /* On HP-UX, we need this auxiliary function. At dynamic load or
+ unload time (but *not* program startup and termination for
+ link-time specified libraries), the linker-indicated function
+ is called with a handle on the library and a flag indicating
+ whether it's being loaded or unloaded.
+
+ The "real" fini function doesn't need to be exported, so
+ declare it static.
+
+ As usual, the final declaration is just for syntactic
+ convenience, so the top-level invocation of this macro can be
+ followed by a semicolon. */
+
+# include <dl.h>
+# define MAKE_FINI_FUNCTION(NAME) \
+ static void NAME(void); \
+ void JOIN__2(NAME, auxfini)(shl_t, int); /* silence gcc warnings */ \
+ void JOIN__2(NAME, auxfini)(shl_t h, int l) { if (!l) NAME(); } \
+ static void NAME(void)
+
+# else /* not hpux */
+
+# define MAKE_FINI_FUNCTION(NAME) \
void NAME(void)
+# endif
+
#elif defined(__GNUC__) && defined(DESTRUCTOR_ATTR_WORKS)
/* If we're using gcc, if the C++ support works, the compiler should
build executables and shared libraries that support the use of