From 4deb9a08c1d3bb8b0c0e95394f8859ff9bcb0f7d Mon Sep 17 00:00:00 2001 From: dgp Date: Fri, 22 Feb 2002 22:36:09 +0000 Subject: * generic/tclInt.h: * generic/tclObj.c: renamed global variable emptyString -> tclEmptyString because it is no longer static. * generic/tclPkg.c: Fix for panic when library is loaded on a platform without backlinking without proper use of stubs. [Bug 476537] --- ChangeLog | 8 ++++++ generic/tclInt.h | 3 ++- generic/tclObj.c | 6 ++--- generic/tclPkg.c | 82 +++++++++++++++++++++++++++++++++++++++++++------------- 4 files changed, 76 insertions(+), 23 deletions(-) diff --git a/ChangeLog b/ChangeLog index b3571f6..e44c9aa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2002-02-22 Don Porter + + * generic/tclInt.h: + * generic/tclObj.c: renamed global variable emptyString -> + tclEmptyString because it is no longer static. + * generic/tclPkg.c: Fix for panic when library is loaded on a + platform without backlinking without proper use of stubs. [Bug 476537] + 2002-02-22 Jeff Hobbs * tests/regexpComp.test: updated regexp-11.[1-4] to match changes diff --git a/generic/tclInt.h b/generic/tclInt.h index 209991d..5c9ffbc 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -12,7 +12,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclInt.h,v 1.80 2002/02/15 14:28:49 dkf Exp $ + * RCS: @(#) $Id: tclInt.h,v 1.81 2002/02/22 22:36:09 dgp Exp $ */ #ifndef _TCLINT @@ -1629,6 +1629,7 @@ extern long tclObjsShared[TCL_MAX_SHARED_OBJ_STATS]; */ extern char * tclEmptyStringRep; +extern char tclEmptyString; /* *---------------------------------------------------------------- diff --git a/generic/tclObj.c b/generic/tclObj.c index c5f7f12..a2eb282 100644 --- a/generic/tclObj.c +++ b/generic/tclObj.c @@ -11,7 +11,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclObj.c,v 1.29 2002/02/15 14:28:49 dkf Exp $ + * RCS: @(#) $Id: tclObj.c,v 1.30 2002/02/22 22:36:09 dgp Exp $ */ #include "tclInt.h" @@ -47,8 +47,8 @@ Tcl_Mutex tclObjMutex; * is shared by all new objects allocated by Tcl_NewObj. */ -static char emptyString; -char *tclEmptyStringRep = &emptyString; +char tclEmptyString = '\0'; +char *tclEmptyStringRep = &tclEmptyString; /* * Prototypes for procedures defined later in this file: diff --git a/generic/tclPkg.c b/generic/tclPkg.c index 919b1b9..43d859b 100644 --- a/generic/tclPkg.c +++ b/generic/tclPkg.c @@ -9,7 +9,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclPkg.c,v 1.8 2002/01/17 04:37:33 dgp Exp $ + * RCS: @(#) $Id: tclPkg.c,v 1.9 2002/02/22 22:36:09 dgp Exp $ */ #include "tclInt.h" @@ -187,7 +187,7 @@ Tcl_PkgRequireEx(interp, name, version, exact, clientDataPtr) Tcl_DString command; /* - * If an attempt is being made to load this into a standalong executable + * If an attempt is being made to load this into a standalone executable * on a platform where backlinking is not supported then this must be * a shared version of Tcl (Otherwise the load would have failed). * Detect this situation by checking that this library has been correctly @@ -195,7 +195,67 @@ Tcl_PkgRequireEx(interp, name, version, exact, clientDataPtr) * work. */ - if (!tclEmptyStringRep) { + if (tclEmptyStringRep == NULL) { + + /* + * OK, so what's going on here? + * + * First, what are we doing? We are performing a check on behalf of + * one particular caller, Tcl_InitStubs(). When a package is + * stub-enabled, it is statically linked to libtclstub.a, which + * contains a copy of Tcl_InitStubs(). When a stub-enabled package + * is loaded, its *_Init() function is supposed to call + * Tcl_InitStubs() before calling any other functions in the Tcl + * library. The first Tcl function called by Tcl_InitStubs() through + * the stub table is Tcl_PkgRequireEx(), so this code right here is + * the first code that is part of the original Tcl library in the + * executable that gets executed on behalf of a newly loaded + * stub-enabled package. + * + * One easy error for the developer/builder of a stub-enabled package + * to make is to forget to define USE_TCL_STUBS when compiling the + * package. When that happens, the package will contain symbols + * that are references to the Tcl library, rather than function + * pointers referencing the stub table. On platforms that lack + * backlinking, those unresolved references may cause the loading + * of the package to also load a second copy of the Tcl library, + * leading to all kinds of trouble. We would like to catch that + * error and report a useful message back to the user. That's + * what we're doing. + * + * Second, how does this work? If we reach this point, then the + * global variable tclEmptyStringRep has the value NULL. Compare + * that with the definition of tclEmptyStringRep near the top of + * the file generic/tclObj.c. It clearly should not have the value + * NULL; it should point to the char tclEmptyString. If we see it + * having the value NULL, then somehow we are seeing a Tcl library + * that isn't completely initialized, and that's an indicator for the + * error condition described above. (Further explanation is welcome.) + * + * Third, so what do we do about it? This situation indicates + * the package we just loaded wasn't properly compiled to be + * stub-enabled, yet it thinks it is stub-enabled (it called + * Tcl_InitStubs()). We want to report that the package just + * loaded is broken, so we want to place an error message in + * the interpreter result and return NULL to indicate failure + * to Tcl_InitStubs() so that it will also fail. (Further + * explanation why we don't want to Tcl_Panic() is welcome. + * After all, two Tcl libraries can't be a good thing!) + * + * Trouble is that's going to be tricky. We're now using a Tcl + * library that's not fully initialized. In particular, it + * doesn't have a proper value for tclEmptyStringRep. The + * Tcl_Obj system heavily depends on the value of tclEmptyStringRep + * and all of Tcl depends (increasingly) on the Tcl_Obj system, we + * need to correct that flaw before making the calls to set the + * interpreter result to the error message. That's the only flaw + * corrected; other problems with initialization of the Tcl library + * are not remedied, so be very careful about adding any other calls + * here without checking how they behave when initialization is + * incomplete. + */ + + tclEmptyStringRep = &tclEmptyString; Tcl_AppendResult(interp, "Cannot load package \"", name, "\" in standalone executable: This package is not ", "compiled with stub support", NULL); @@ -387,22 +447,6 @@ Tcl_PkgPresentEx(interp, name, version, exact, clientDataPtr) Package *pkgPtr; int satisfies, result; - /* - * If an attempt is being made to load this into a standalone executable - * on a platform where backlinking is not supported then this must be - * a shared version of Tcl (Otherwise the load would have failed). - * Detect this situation by checking that this library has been correctly - * initialised. If it has not been then return immediately as nothing will - * work. - */ - - if (!tclEmptyStringRep) { - Tcl_AppendResult(interp, "Cannot load package \"", name, - "\" in standalone executable: This package is not ", - "compiled with stub support", NULL); - return NULL; - } - hPtr = Tcl_FindHashEntry(&iPtr->packageTable, name); if (hPtr) { pkgPtr = (Package *) Tcl_GetHashValue(hPtr); -- cgit v0.12