This is the mail archive of the
java-patches@gcc.gnu.org
mailing list for the Java project.
Patch: RFC: version compiled classes
- From: Tom Tromey <tromey at redhat dot com>
- To: Java Patch List <java-patches at gcc dot gnu dot org>
- Date: 13 Jan 2005 19:22:02 -0700
- Subject: Patch: RFC: version compiled classes
- Reply-to: tromey at redhat dot com
This patch adds versioning to compiled classes. If a class' version
is not recognized, we don't register it. This prevents bad classes
from being accidentally found and should lessen the difficulties
associated with duplicate class registration.
On irc Bryce suggested that we should instead version .so files, not
single classes. I do think that would be better, I just didn't see a
way to go about doing it. There are 3 cases:
* GNU ld systems that use .jcr. I think we would have to modify the
linker for this. It would have to reject attempts to link together
classes with different versions. It seems like somehow it would
have to be backward compatible as well, since a given system
ordinarily ships a single 'ld' but may ship multiple versions of
gcj.
* Non-GNU ld systems. On these systems we register classes using
static constructors. I couldn't think of any way to accommodate
these systems; they would always have to be per-class.
* Darwin. I'm told Darwin uses its own ld but also handles .jcr. So
apparently we would have to discuss this with Apple.
Right now the code simply refuses to register a class with an
unrecognized version. Another approach would be to throw
UnsupportedClassVersionError instead. When doing dlopen() we could
catch this, dlclose(), and (possibly) continue.
I used different numbers for the C++ ABI and the BC ABI. And, I left
some room in there for future oddities.
Let me know what you think of this. I think it is fairly important
for this to show up in GCC 4.0; we've had plenty of problems in the
last release cycle which boil down to loading the wrong class.
I haven't run this through the test suite, but I did rebuild rhug to
try eclipse, and I did test that loading a gcj3.3-compiled .so from
/usr/lib didn't cause its classes to be registered.
Tom
Index: gcc/java/ChangeLog
from Tom Tromey <tromey@redhat.com>
* java-tree.h (gcj_abi_version): Declare.
* class.c (make_class_data): Push gcj_abi_version into "next"
field.
* decl.c (gcj_abi_version): New global.
(parse_version): New function.
(java_init_decl_processing): Call it.
Include version.h.
(GCJ_BINARYCOMPAT_ADDITION): New define.
Index: gcc/java/class.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/java/class.c,v
retrieving revision 1.219
diff -u -r1.219 class.c
--- gcc/java/class.c 13 Dec 2004 06:57:13 -0000 1.219
+++ gcc/java/class.c 14 Jan 2005 02:07:11 -0000
@@ -1,5 +1,5 @@
/* Functions related to building classes and their related objects.
- Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+ Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
Free Software Foundation, Inc.
This file is part of GCC.
@@ -1741,7 +1741,7 @@
FINISH_RECORD_CONSTRUCTOR (temp);
START_RECORD_CONSTRUCTOR (cons, class_type_node);
PUSH_SUPER_VALUE (cons, temp);
- PUSH_FIELD_VALUE (cons, "next", null_pointer_node);
+ PUSH_FIELD_VALUE (cons, "next", gcj_abi_version);
PUSH_FIELD_VALUE (cons, "name", build_utf8_ref (DECL_NAME (type_decl)));
PUSH_FIELD_VALUE (cons, "accflags",
build_int_cst (NULL_TREE,
Index: gcc/java/decl.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/java/decl.c,v
retrieving revision 1.206
diff -u -r1.206 decl.c
--- gcc/java/decl.c 27 Nov 2004 20:48:16 -0000 1.206
+++ gcc/java/decl.c 14 Jan 2005 02:07:12 -0000
@@ -1,6 +1,6 @@
/* Process declarations and variables for the GNU compiler for the
Java(TM) language.
- Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+ Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
Free Software Foundation, Inc.
This file is part of GCC.
@@ -47,6 +47,7 @@
#include "cgraph.h"
#include "tree-inline.h"
#include "target.h"
+#include "version.h"
#if defined (DEBUG_JAVA_BINDING_LEVELS)
extern void indent (void);
@@ -58,6 +59,13 @@
static struct binding_level *make_binding_level (void);
static tree create_primitive_vtable (const char *);
static tree check_local_unnamed_variable (tree, tree, tree);
+static void parse_version (void);
+
+/* Used when computing the ABI version. */
+#define GCJ_BINARYCOMPAT_ADDITION 5
+
+/* The ABI version number. */
+tree gcj_abi_version;
/* Name of the Cloneable class. */
tree java_lang_cloneable_identifier_node;
@@ -559,6 +567,48 @@
return t;
}
+/* Parse the version string and compute the ABI version number. */
+static void
+parse_version (void)
+{
+ const char *p = version_string;
+ unsigned int major = 0, minor = 0;
+ unsigned int abi_version;
+
+ /* Skip leading junk. */
+ while (*p && !ISDIGIT (*p))
+ ++p;
+ gcc_assert (*p);
+
+ /* Extract major version. */
+ while (ISDIGIT (*p))
+ {
+ major = major * 10 + *p - '0';
+ ++p;
+ }
+
+ gcc_assert (*p == '.' || ISDIGIT (p[1]));
+ ++p;
+
+ /* Extract minor version. */
+ while (ISDIGIT (*p))
+ {
+ minor = minor * 10 + *p - '0';
+ ++p;
+ }
+
+ /* Implicit in this computation is the idea that we won't break the
+ old-style binary ABI in a minor release. */
+ abi_version = 10000 * major + 10 * minor;
+ /* It is helpful to distinguish BC ABI from ordinary ABI at this
+ level, since at some point we will recognize a variety of BC ABIs
+ (objects generated by different version of gcj), but will
+ probably always require strict matching for ordinary ABI. */
+ if (flag_indirect_dispatch)
+ abi_version += GCJ_BINARYCOMPAT_ADDITION;
+
+ gcj_abi_version = build_int_cstu (ptr_type_node, abi_version);
+}
void
java_init_decl_processing (void)
@@ -1100,6 +1150,8 @@
#if 0
soft_fmodf_node = built_in_decls[BUILT_IN_FMODF];
#endif
+
+ parse_version ();
}
Index: gcc/java/java-tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/java/java-tree.h,v
retrieving revision 1.224
diff -u -r1.224 java-tree.h
--- gcc/java/java-tree.h 6 Dec 2004 15:32:37 -0000 1.224
+++ gcc/java/java-tree.h 14 Jan 2005 02:07:13 -0000
@@ -1,6 +1,6 @@
/* Definitions for parsing and type checking for the GNU compiler for
the Java(TM) language.
- Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+ Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
Free Software Foundation, Inc.
This file is part of GCC.
@@ -274,6 +274,7 @@
extern GTY(()) tree java_lang_cloneable_identifier_node;
extern GTY(()) tree java_io_serializable_identifier_node;
+extern GTY(()) tree gcj_abi_version;
enum java_tree_index
{
Index: gcc/java/parse.y
===================================================================
RCS file: /cvs/gcc/gcc/gcc/java/parse.y,v
retrieving revision 1.526
diff -u -r1.526 parse.y
--- gcc/java/parse.y 9 Jan 2005 13:54:34 -0000 1.526
+++ gcc/java/parse.y 14 Jan 2005 02:07:22 -0000
@@ -4188,6 +4188,13 @@
CLASS_COMPLETE_P (decl) = 1;
add_superinterfaces (decl, interfaces);
+ if (! CPC_INNER_P ())
+ {
+ tree cclass = TREE_TYPE (decl);
+ TYPE_IMPORT_LIST (TREE_TYPE (cclass)) = ctxp->import_list;
+ TYPE_IMPORT_DEMAND_LIST (TREE_TYPE (cclass)) = ctxp->import_demand_list;
+ }
+
/* TYPE_VFIELD' is a compiler-generated field used to point to
virtual function tables. In gcj, every class has a common base
virtual function table in java.lang.object. */
@@ -7010,8 +7017,14 @@
static void
find_in_imports (tree enclosing_type, tree class_type)
{
- tree import = (enclosing_type ? TYPE_IMPORT_LIST (enclosing_type) :
- ctxp->import_list);
+ tree import;
+ if (enclosing_type)
+ import = TYPE_IMPORT_LIST (enclosing_type);
+ else if (TREE_TYPE (class_type))
+ import = TYPE_IMPORT_LIST (TREE_TYPE (class_type));
+ else
+ import = ctxp->import_list;
+
while (import)
{
if (TREE_VALUE (import) == TYPE_NAME (class_type))
@@ -7169,8 +7182,14 @@
find_in_imports_on_demand (tree enclosing_type, tree class_type)
{
tree class_type_name = TYPE_NAME (class_type);
- tree import = (enclosing_type ? TYPE_IMPORT_DEMAND_LIST (enclosing_type) :
- ctxp->import_demand_list);
+ tree import;
+ if (enclosing_type)
+ import = TYPE_IMPORT_DEMAND_LIST (enclosing_type);
+ else if (TREE_TYPE (class_type))
+ import = TYPE_IMPORT_DEMAND_LIST (TREE_TYPE (class_type));
+ else
+ import = ctxp->import_demand_list;
+
tree cl = NULL_TREE;
int seen_once = -1; /* -1 when not set, 1 if seen once, >1 otherwise. */
int to_return = -1; /* -1 when not set, 0 or 1 otherwise */
Index: libjava/ChangeLog
from Tom Tromey <tromey@redhat.com>
* java/lang/Class.h (next): Updated documentation.
* java/lang/natClassLoader.cc (_Jv_RegisterClasses): Use
_Jv_AbiRecognized.
(_Jv_RegisterClasses_Counted): Likewise.
(_Jv_RegisterClassHookDefault): Fix starting condition of
duplicate-checking loop.
* include/jvm.h (GCJ_VERSION): New define.
(GCJ_BINARYCOMPAT_ADDITION): Likewise.
(_Jv_AbiRecognized): New function.
Index: libjava/include/jvm.h
===================================================================
RCS file: /cvs/gcc/gcc/libjava/include/jvm.h,v
retrieving revision 1.73
diff -u -r1.73 jvm.h
--- libjava/include/jvm.h 10 Jan 2005 19:39:25 -0000 1.73
+++ libjava/include/jvm.h 14 Jan 2005 02:07:27 -0000
@@ -563,4 +563,19 @@
extern void _Jv_RegisterBootstrapPackages ();
+
+// This is used to find ABI versions we recognize.
+#define GCJ_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 10)
+#define GCJ_BINARYCOMPAT_ADDITION 5
+
+inline bool
+_Jv_AbiRecognized (unsigned long value)
+{
+ // For this release, recognize just our defined C++ ABI and our
+ // defined BC ABI. (In the future we may recognize past BC ABIs as
+ // well.)
+ return (value == GCJ_VERSION
+ || value == (GCJ_VERSION + GCJ_BINARYCOMPAT_ADDITION));
+}
+
#endif /* __JAVA_JVM_H__ */
Index: libjava/java/lang/Class.h
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/Class.h,v
retrieving revision 1.75
diff -u -r1.75 Class.h
--- libjava/java/lang/Class.h 10 Jan 2005 19:39:25 -0000 1.75
+++ libjava/java/lang/Class.h 14 Jan 2005 02:07:28 -0000
@@ -488,7 +488,9 @@
friend void ::_Jv_sharedlib_register_hook (jclass klass);
- // Chain for class pool.
+ // Chain for class pool. This also doubles as the ABI version
+ // number. It is only used for this purpose at class registration
+ // time, and only for precompiled classes.
jclass next;
// Name of class.
_Jv_Utf8Const *name;
Index: libjava/java/lang/natClassLoader.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/natClassLoader.cc,v
retrieving revision 1.74
diff -u -r1.74 natClassLoader.cc
--- libjava/java/lang/natClassLoader.cc 10 Jan 2005 19:39:26 -0000 1.74
+++ libjava/java/lang/natClassLoader.cc 14 Jan 2005 02:07:28 -0000
@@ -122,7 +122,8 @@
{
jclass klass = *classes;
- (*_Jv_RegisterClassHook) (klass);
+ if (_Jv_AbiRecognized ((unsigned long) klass->next))
+ (*_Jv_RegisterClassHook) (klass);
}
}
@@ -135,7 +136,8 @@
{
jclass klass = classes[i];
- (*_Jv_RegisterClassHook) (klass);
+ if (_Jv_AbiRecognized ((unsigned long) klass->next))
+ (*_Jv_RegisterClassHook) (klass);
}
}
@@ -144,12 +146,10 @@
{
jint hash = HASH_UTF (klass->name);
- // The BC ABI makes this check unnecessary: we always resolve all
- // data references via the appropriate class loader, so the kludge
- // that required this check has gone.
// If the class is already registered, don't re-register it.
- jclass check_class = klass->next;
- while (check_class != NULL)
+ for (jclass check_class = loaded_classes[hash];
+ check_class != NULL;
+ check_class = check_class->next)
{
if (check_class == klass)
{
@@ -170,8 +170,6 @@
throw new java::lang::VirtualMachineError (str);
}
}
-
- check_class = check_class->next;
}
// FIXME: this is really bogus!