changeset 7821:4bf2c76660e6

Add check for an attempt by the boot loader to load a class outside of java.base before the module system is intiializaed. Also fix an issue with setting up the boot loader's append path.
author lfoltan
date Thu, 19 Feb 2015 17:00:45 -0500
parents 8a0166d6fb79
children 583a895b3abb
files src/share/vm/classfile/classLoader.cpp src/share/vm/classfile/classLoader.hpp src/share/vm/classfile/classLoaderExt.hpp src/share/vm/classfile/moduleEntry.cpp src/share/vm/classfile/systemDictionary.cpp src/share/vm/runtime/arguments.cpp test/runtime/modules/observability1/Observability1.sh
diffstat 7 files changed, 127 insertions(+), 55 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/vm/classfile/classLoader.cpp	Thu Feb 19 14:18:54 2015 +0000
+++ b/src/share/vm/classfile/classLoader.cpp	Thu Feb 19 17:00:45 2015 -0500
@@ -874,6 +874,7 @@
   int offset = 0;
   int len = (int)strlen(class_path);
   int end = 0;
+  bool mark_append_entry = false;
 
   // Iterate over class path entries
   for (int start = 0; start < len; start = end) {
@@ -882,12 +883,24 @@
     }
     EXCEPTION_MARK;
     ResourceMark rm(THREAD);
-    bool mark_append_entry = (bootstrap_search &&
-                              (start == Arguments::bootclasspath_a_index()));
+    mark_append_entry = (mark_append_entry ||
+                         (bootstrap_search && (start == Arguments::bootclasspath_a_index())));
+
     char* path = NEW_RESOURCE_ARRAY(char, end - start + 1);
     strncpy(path, &class_path[start], end - start);
     path[end - start] = '\0';
     update_class_path_entry_list(path, false, mark_append_entry);
+
+    // Check on the state of the boot loader's append path
+    if (mark_append_entry && (_first_append_entry == NULL)) {
+      // Failure to mark the first append entry, most likely
+      // due to a non-existent path. Record the next entry
+      // as the first boot loader append entry.
+      mark_append_entry = true;
+    } else {
+      mark_append_entry = false;
+    }
+
 #if INCLUDE_CDS
     if (DumpSharedSpaces) {
       check_shared_classpath(path);
@@ -974,12 +987,6 @@
   } else {
     // Directory
     new_entry = new ClassPathDirEntry(path);
-
-    if (!ModuleEntryTable::javabase_created() &&
-        !ClassLoader::has_bootmodules_jimage() &&
-        string_ends_with(path, "java.base") && !is_override_dir(path)) {
-      process_javabase(path);
-    }
     if (TraceClassLoading) {
       tty->print_cr("[Path %s]", path);
     }
@@ -1016,14 +1023,16 @@
   return NULL;
 }
 
-// The boot class loader must adhere to specfic observability rules.
+// The boot class loader must adhere to specfic visibility rules.
 // Prior to loading a class in a named package, the package is checked
 // to see if it is in a module defined to the boot loader. If the
 // package is not in a module defined to the boot loader, the class
 // must be loaded only in the boot loader's append path, which
 // consists of [-Xbootclasspath/a]; [jvmti appended entries]
 void ClassLoader::set_first_append_entry(ClassPathEntry *new_entry) {
-  _first_append_entry = new_entry;
+  if (_first_append_entry == NULL) {
+    _first_append_entry = new_entry;
+  }
 }
 
 // returns true if entry already on class path
@@ -1425,7 +1434,6 @@
     PerfClassTraceTime vmtimer(perf_sys_class_lookup_time(),
                                ((JavaThread*) THREAD)->get_thread_stat()->perf_timers_addr(),
                                PerfClassTraceTime::CLASS_LOAD);
-
     if (search_append_only) {
       // For the boot loader append path search, must calculate
       // the starting classpath_index prior to attempting to
@@ -1657,18 +1665,42 @@
 }
 
 
-void ClassLoader::process_jimage_file() {
-  ClassPathEntry* entry = _first_entry;
-  while (entry != NULL) {
-    ImageFile *image = entry->image();
-    if (image != NULL && string_ends_with(entry->name(), "bootmodules.jimage")) {
+void ClassLoader::define_javabase() {
+  ClassPathEntry* e = _first_entry;
+  ClassPathEntry* last_e = _first_append_entry;
+
+  assert(!ModuleEntryTable::javabase_created() && !ClassLoader::has_bootmodules_jimage(),
+         "java.base has already been processed");
+
+  // When looking for the jimage file, only
+  // search the boot loader's module path which
+  // can consist of [-Xoverride]; exploded build | bootmodules.jimage
+  // Do not search the boot loader's append path.
+  while ((e != NULL) && (e != last_e)) {
+    ImageFile *image = e->image();
+    if (image != NULL && string_ends_with(e->name(), "bootmodules.jimage")) {
+      process_javabase(image);
       set_has_bootmodules_jimage(true);
-      process_javabase(image);
       return;
     }
-    entry = entry->next();
+    e = e->next();
   }
-  set_has_bootmodules_jimage(false);
+
+  // If bootmodules.jimage has not been located,
+  // assume an exploded build.
+  e = _first_entry;
+  while ((e != NULL) && (e != last_e)) {
+    const char *path = e->name();
+    if (string_ends_with(path, "java.base") &&
+        !is_override_dir(path) &&
+        !e->is_jar_file()) {
+      process_javabase(path);
+      return;
+    }
+    e = e->next();
+  }
+
+  vm_exit_during_initialization("Unable to correctly define java.base from either the exploded build or bootmodules.jimage");
 }
 
 #ifndef PRODUCT
--- a/src/share/vm/classfile/classLoader.hpp	Thu Feb 19 14:18:54 2015 +0000
+++ b/src/share/vm/classfile/classLoader.hpp	Thu Feb 19 17:00:45 2015 -0500
@@ -215,8 +215,7 @@
   static PerfCounter* _load_instance_class_failCounter;
 
   // First entry in linked list of ClassPathEntry instances.
-  // Marks the start of the boot loader's overall observability
-  // set.  This consists of entries made up by:
+  // This consists of entries made up by:
   //   - boot loader modules
   //     [-Xoverride]; exploded build | bootmodules.jimage;
   //   - boot loader append path
@@ -236,7 +235,7 @@
   static PackageHashtable* _package_hash_table;
   static const char* _shared_archive;
 
-  // True if classpath has a bootmodules.jimage
+  // True if the boot path has a bootmodules.jimage
   static bool _has_bootmodules_jimage;
 
   // Info used by CDS
@@ -344,8 +343,8 @@
   static bool has_bootmodules_jimage() { return _has_bootmodules_jimage; }
 
   // Read the packages for module java.base from the bootmodules.jimage
-  // file, if it exists.
-  static void process_jimage_file();
+  // file, if it exists. If it does not, assume exploded build.
+  static void define_javabase();
 
   // Load individual .class file
   static instanceKlassHandle load_classfile(Symbol* h_name, bool search_append_only, TRAPS);
--- a/src/share/vm/classfile/classLoaderExt.hpp	Thu Feb 19 14:18:54 2015 +0000
+++ b/src/share/vm/classfile/classLoaderExt.hpp	Thu Feb 19 17:00:45 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -65,6 +65,13 @@
   }
   static void append_boot_classpath(ClassPathEntry* new_entry) {
     ClassLoader::add_to_list(new_entry);
+    // During jvmti live phase an entry can be appended to the boot
+    // loader's ClassPathEntry instances.  Need to mark the start
+    // of the boot loader's append path in case there was no reason
+    // to mark it initially in setup_bootstrap_search_path.
+    if (ClassLoader::_first_append_entry == NULL) {
+      ClassLoader::set_first_append_entry(new_entry);
+    }
   }
   static void setup_search_paths() {}
 };
--- a/src/share/vm/classfile/moduleEntry.cpp	Thu Feb 19 14:18:54 2015 +0000
+++ b/src/share/vm/classfile/moduleEntry.cpp	Thu Feb 19 17:00:45 2015 -0500
@@ -36,7 +36,7 @@
 #include "utilities/growableArray.hpp"
 #include "utilities/hashtable.inline.hpp"
 
-bool ModuleEntryTable::_javabase_created;
+bool ModuleEntryTable::_javabase_created = false;
 
 // Returns true if this module can read module m
 bool ModuleEntry::can_read(ModuleEntry* m) const {
--- a/src/share/vm/classfile/systemDictionary.cpp	Thu Feb 19 14:18:54 2015 +0000
+++ b/src/share/vm/classfile/systemDictionary.cpp	Thu Feb 19 17:00:45 2015 -0500
@@ -47,6 +47,7 @@
 #include "oops/objArrayKlass.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/oop.inline2.hpp"
+#include "oops/symbol.hpp"
 #include "oops/typeArrayKlass.hpp"
 #include "prims/jvmtiEnvBase.hpp"
 #include "prims/methodHandles.hpp"
@@ -1266,31 +1267,60 @@
   instanceKlassHandle nh = instanceKlassHandle(); // null Handle
 
   if (class_loader.is_null()) {
+    int length;
+    TempNewSymbol pkg_name = NULL;
+    PackageEntry* pkg_entry = NULL;
     bool search_only_bootloader_append = false;
-
-    // Check observability boundaries for the boot class loader.
-    if (Universe::is_module_initialized()) {
-      // Check if the class' package is in a module defined to the boot loader.
-      // Check must occur after the module system has been initialized.
-      int length;
-      const jbyte* pkg_string = InstanceKlass::package_from_name(class_name, length);
-
-      if (pkg_string != NULL) {
-        TempNewSymbol pkg_name = SymbolTable::new_symbol((const char*)pkg_string, length, CHECK_(nh));
-        // Find in boot class loader's package entry table.
-        ClassLoaderData* loader_data = class_loader_data(class_loader);
-        PackageEntry* package_entry = loader_data->packages()->lookup_only(pkg_name);
-        if (package_entry == NULL || package_entry->in_unnamed_module()) {
-          // Limit the scope of the observability to locate the class only
-          // in the boot loader's append path, -Xbootclasspath\a + jvmti
-          search_only_bootloader_append = true;
+    ClassLoaderData *loader_data = class_loader_data(class_loader);
+
+    // Find the package in the boot loader's package entry table.
+    const jbyte* pkg_string = InstanceKlass::package_from_name(class_name, length);
+    if (pkg_string != NULL) {
+      pkg_name = SymbolTable::new_symbol((const char*)pkg_string, length, CHECK_(nh));
+      pkg_entry = loader_data->packages()->lookup_only(pkg_name);
+    }
+
+    // Prior to attempting to load the class, enforce the boot loader's
+    // visibility boundaries.
+    if (!Universe::is_module_initialized()) {
+      // During bootstrapping, prior to module initialization, any
+      // class attempting to be loaded must be checked against the
+      // java.base packages in the boot loader's PackageEntryTable.
+      // No class outside of java.base is allowed to be loaded during
+      // this bootstrapping window.
+      if (pkg_entry == NULL || pkg_entry->in_unnamed_module()) {
+        // Class is either in the unnamed package or in
+        // a named package within the unnamed module.  Either
+        // case is outside of java.base, do not attempt to
+        // load the class.
+        return nh;
+      } else {
+        // Check that the class' package is defined within java.base.
+        ModuleEntry* mod_entry = pkg_entry->module();
+        Symbol* mod_entry_name = mod_entry->name();
+        if (mod_entry_name->fast_compare(vmSymbols::java_base()) != 0) {
+          return nh;
         }
-      } else {
-        // unnamed package, search only the boot loader's append path
+      }
+    } else {
+      // After the module system has been initialized, check if the class'
+      // package is in a module defined to the boot loader.
+      if (pkg_string == NULL || pkg_entry == NULL || pkg_entry->in_unnamed_module()) {
+        // Class is either in the unnamed package, in a named package
+        // within a module not defined to the boot loader or in a
+        // a named package within the unnamed module.  In all cases
+        // limit visibility to search for the class only in the boot
+        // loader's append path.
         search_only_bootloader_append = true;
       }
     }
 
+    // Prior to bootstrapping's module initialization, never load a class outside
+    // of the boot loader's module path
+    assert(Universe::is_module_initialized() ||
+           (!Universe::is_module_initialized() && !search_only_bootloader_append),
+           "Attempt to load a class outside of boot loader's module path");
+
     // Search the shared system dictionary for classes preloaded into the
     // shared spaces.
     instanceKlassHandle k;
@@ -1901,10 +1931,10 @@
 void SystemDictionary::initialize_preloaded_classes(TRAPS) {
   assert(WK_KLASS(Object_klass) == NULL, "preloaded classes should only be initialized once");
 
-  // Get packages for module java.base from jimage file, if one exists.  This
-  // call needs to be done here, after vmsSymbols::initialize() is called but
-  // before any classes are pre-loaded.
-  ClassLoader::process_jimage_file();
+  // Define packages for module java.base from either the exploded build or the
+  // bootmodules.jimage file. This call needs to be done here, after vmSymbols::initialize()
+  // is called but before any classes are pre-loaded.
+  ClassLoader::define_javabase();
 
   // Preload commonly used klasses
   WKID scan = FIRST_WKID;
--- a/src/share/vm/runtime/arguments.cpp	Thu Feb 19 14:18:54 2015 +0000
+++ b/src/share/vm/runtime/arguments.cpp	Thu Feb 19 17:00:45 2015 -0500
@@ -473,12 +473,11 @@
   // Get the lengths.
   int i;
   for (i = 0; i < _scp_nitems; ++i) {
+    if (i == _scp_suffix) {
+      // Record index of boot loader's append path.
+      Arguments::set_bootclasspath_a_index((int)total_len);
+    }
     if (_items[i] != NULL) {
-      // Record index of -Xbootclasspath/a, used to set the
-      // boot loader's append path observability boundary.
-      if (i == _scp_suffix) {
-        Arguments::set_bootclasspath_a_index((int)total_len);
-      }
       lengths[i] = strlen(_items[i]);
       // Include space for the separator char (or a NULL for the last item).
       total_len += lengths[i] + 1;
@@ -3462,6 +3461,11 @@
   if (scp_assembly_required) {
     // Assemble the bootclasspath elements into the final path.
     Arguments::set_sysclasspath(scp_p->combined_path());
+  } else {
+    // At this point in sysclasspath processing anything
+    // added would be considered in the boot loader's append path.
+    // Record this index, including +1 for the file separator character.
+    Arguments::set_bootclasspath_a_index(((int)strlen(Arguments::get_sysclasspath()))+1);
   }
 
   // This must be done after all arguments have been processed.
--- a/test/runtime/modules/observability1/Observability1.sh	Thu Feb 19 14:18:54 2015 +0000
+++ b/test/runtime/modules/observability1/Observability1.sh	Thu Feb 19 17:00:45 2015 -0500
@@ -45,5 +45,5 @@
 ${JAVAC} -d mods1 $TESTSRC/p2/Observability1_C.java
 ${JAVAC} -cp mods1 -d $TESTCLASSES $TESTSRC/Observability1_A.java
 
-${JAVA} -Xbootclasspath/a:mods1 -cp $TESTCLASSES Observability1_A
+${JAVA} -Xbootclasspath/a:nonexistent.jar -Xbootclasspath/a:mods1 -cp $TESTCLASSES Observability1_A
 exit $?