| 1 | /* |
| 2 | * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. |
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| 4 | * |
| 5 | * This code is free software; you can redistribute it and/or modify it |
| 6 | * under the terms of the GNU General Public License version 2 only, as |
| 7 | * published by the Free Software Foundation. |
| 8 | * |
| 9 | * This code is distributed in the hope that it will be useful, but WITHOUT |
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 12 | * version 2 for more details (a copy is included in the LICENSE file that |
| 13 | * accompanied this code). |
| 14 | * |
| 15 | * You should have received a copy of the GNU General Public License version |
| 16 | * 2 along with this work; if not, write to the Free Software Foundation, |
| 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| 18 | * |
| 19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| 20 | * or visit www.oracle.com if you need additional information or have any |
| 21 | * questions. |
| 22 | * |
| 23 | */ |
| 24 | |
| 25 | #include "precompiled.hpp" |
| 26 | #include "classfile/classFileParser.hpp" |
| 27 | #include "classfile/classFileStream.hpp" |
| 28 | #include "classfile/classLoader.inline.hpp" |
| 29 | #include "classfile/classLoaderExt.hpp" |
| 30 | #include "classfile/classLoaderData.inline.hpp" |
| 31 | #include "classfile/klassFactory.hpp" |
| 32 | #include "classfile/modules.hpp" |
| 33 | #include "classfile/sharedPathsMiscInfo.hpp" |
| 34 | #include "classfile/systemDictionaryShared.hpp" |
| 35 | #include "classfile/vmSymbols.hpp" |
| 36 | #include "memory/allocation.inline.hpp" |
| 37 | #include "memory/filemap.hpp" |
| 38 | #include "memory/resourceArea.hpp" |
| 39 | #include "oops/instanceKlass.hpp" |
| 40 | #include "oops/oop.inline.hpp" |
| 41 | #include "oops/symbol.hpp" |
| 42 | #include "runtime/arguments.hpp" |
| 43 | #include "runtime/handles.inline.hpp" |
| 44 | #include "runtime/java.hpp" |
| 45 | #include "runtime/javaCalls.hpp" |
| 46 | #include "runtime/os.hpp" |
| 47 | #include "services/threadService.hpp" |
| 48 | #include "utilities/stringUtils.hpp" |
| 49 | |
| 50 | jshort ClassLoaderExt::_app_class_paths_start_index = ClassLoaderExt::max_classpath_index; |
| 51 | jshort ClassLoaderExt::_app_module_paths_start_index = ClassLoaderExt::max_classpath_index; |
| 52 | jshort ClassLoaderExt::_max_used_path_index = 0; |
| 53 | bool ClassLoaderExt::_has_app_classes = false; |
| 54 | bool ClassLoaderExt::_has_platform_classes = false; |
| 55 | |
| 56 | void ClassLoaderExt::append_boot_classpath(ClassPathEntry* new_entry) { |
| 57 | if (UseSharedSpaces) { |
| 58 | warning("Sharing is only supported for boot loader classes because bootstrap classpath has been appended" ); |
| 59 | FileMapInfo::current_info()->header()->set_has_platform_or_app_classes(false); |
| 60 | } |
| 61 | ClassLoader::add_to_boot_append_entries(new_entry); |
| 62 | } |
| 63 | |
| 64 | void ClassLoaderExt::setup_app_search_path() { |
| 65 | assert(DumpSharedSpaces || DynamicDumpSharedSpaces, |
| 66 | "this function is only used at CDS dump time" ); |
| 67 | _app_class_paths_start_index = ClassLoader::num_boot_classpath_entries(); |
| 68 | char* app_class_path = os::strdup(Arguments::get_appclasspath()); |
| 69 | |
| 70 | if (strcmp(app_class_path, "." ) == 0) { |
| 71 | // This doesn't make any sense, even for AppCDS, so let's skip it. We |
| 72 | // don't want to throw an error here because -cp "." is usually assigned |
| 73 | // by the launcher when classpath is not specified. |
| 74 | trace_class_path("app loader class path (skipped)=" , app_class_path); |
| 75 | } else { |
| 76 | trace_class_path("app loader class path=" , app_class_path); |
| 77 | shared_paths_misc_info()->add_app_classpath(app_class_path); |
| 78 | ClassLoader::setup_app_search_path(app_class_path); |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | void ClassLoaderExt::process_module_table(ModuleEntryTable* met, TRAPS) { |
| 83 | ResourceMark rm(THREAD); |
| 84 | for (int i = 0; i < met->table_size(); i++) { |
| 85 | for (ModuleEntry* m = met->bucket(i); m != NULL;) { |
| 86 | char* path = m->location()->as_C_string(); |
| 87 | if (strncmp(path, "file:" , 5) == 0) { |
| 88 | path = ClassLoader::skip_uri_protocol(path); |
| 89 | ClassLoader::setup_module_search_path(path, THREAD); |
| 90 | } |
| 91 | m = m->next(); |
| 92 | } |
| 93 | } |
| 94 | } |
| 95 | void ClassLoaderExt::setup_module_paths(TRAPS) { |
| 96 | assert(DumpSharedSpaces || DynamicDumpSharedSpaces, |
| 97 | "this function is only used with CDS dump time" ); |
| 98 | _app_module_paths_start_index = ClassLoader::num_boot_classpath_entries() + |
| 99 | ClassLoader::num_app_classpath_entries(); |
| 100 | Handle system_class_loader (THREAD, SystemDictionary::java_system_loader()); |
| 101 | ModuleEntryTable* met = Modules::get_module_entry_table(system_class_loader); |
| 102 | process_module_table(met, THREAD); |
| 103 | } |
| 104 | |
| 105 | char* ClassLoaderExt::read_manifest(ClassPathEntry* entry, jint *manifest_size, bool clean_text, TRAPS) { |
| 106 | const char* name = "META-INF/MANIFEST.MF" ; |
| 107 | char* manifest; |
| 108 | jint size; |
| 109 | |
| 110 | assert(entry->is_jar_file(), "must be" ); |
| 111 | manifest = (char*) ((ClassPathZipEntry*)entry )->open_entry(name, &size, true, CHECK_NULL); |
| 112 | |
| 113 | if (manifest == NULL) { // No Manifest |
| 114 | *manifest_size = 0; |
| 115 | return NULL; |
| 116 | } |
| 117 | |
| 118 | |
| 119 | if (clean_text) { |
| 120 | // See http://docs.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#JAR%20Manifest |
| 121 | // (1): replace all CR/LF and CR with LF |
| 122 | StringUtils::replace_no_expand(manifest, "\r\n" , "\n" ); |
| 123 | |
| 124 | // (2) remove all new-line continuation (remove all "\n " substrings) |
| 125 | StringUtils::replace_no_expand(manifest, "\n " , "" ); |
| 126 | } |
| 127 | |
| 128 | *manifest_size = (jint)strlen(manifest); |
| 129 | return manifest; |
| 130 | } |
| 131 | |
| 132 | char* ClassLoaderExt::get_class_path_attr(const char* jar_path, char* manifest, jint manifest_size) { |
| 133 | const char* tag = "Class-Path: " ; |
| 134 | const int tag_len = (int)strlen(tag); |
| 135 | char* found = NULL; |
| 136 | char* line_start = manifest; |
| 137 | char* end = manifest + manifest_size; |
| 138 | |
| 139 | assert(*end == 0, "must be nul-terminated" ); |
| 140 | |
| 141 | while (line_start < end) { |
| 142 | char* line_end = strchr(line_start, '\n'); |
| 143 | if (line_end == NULL) { |
| 144 | // JAR spec require the manifest file to be terminated by a new line. |
| 145 | break; |
| 146 | } |
| 147 | if (strncmp(tag, line_start, tag_len) == 0) { |
| 148 | if (found != NULL) { |
| 149 | // Same behavior as jdk/src/share/classes/java/util/jar/Attributes.java |
| 150 | // If duplicated entries are found, the last one is used. |
| 151 | tty->print_cr("Warning: Duplicate name in Manifest: %s.\n" |
| 152 | "Ensure that the manifest does not have duplicate entries, and\n" |
| 153 | "that blank lines separate individual sections in both your\n" |
| 154 | "manifest and in the META-INF/MANIFEST.MF entry in the jar file:\n%s\n" , tag, jar_path); |
| 155 | } |
| 156 | found = line_start + tag_len; |
| 157 | assert(found <= line_end, "sanity" ); |
| 158 | *line_end = '\0'; |
| 159 | } |
| 160 | line_start = line_end + 1; |
| 161 | } |
| 162 | return found; |
| 163 | } |
| 164 | |
| 165 | void ClassLoaderExt::process_jar_manifest(ClassPathEntry* entry, |
| 166 | bool check_for_duplicates) { |
| 167 | Thread* THREAD = Thread::current(); |
| 168 | ResourceMark rm(THREAD); |
| 169 | jint manifest_size; |
| 170 | char* manifest = read_manifest(entry, &manifest_size, CHECK); |
| 171 | |
| 172 | if (manifest == NULL) { |
| 173 | return; |
| 174 | } |
| 175 | |
| 176 | if (strstr(manifest, "Extension-List:" ) != NULL) { |
| 177 | vm_exit_during_cds_dumping(err_msg("-Xshare:dump does not support Extension-List in JAR manifest: %s" , entry->name())); |
| 178 | } |
| 179 | |
| 180 | char* cp_attr = get_class_path_attr(entry->name(), manifest, manifest_size); |
| 181 | |
| 182 | if (cp_attr != NULL && strlen(cp_attr) > 0) { |
| 183 | trace_class_path("found Class-Path: " , cp_attr); |
| 184 | |
| 185 | char sep = os::file_separator()[0]; |
| 186 | const char* dir_name = entry->name(); |
| 187 | const char* dir_tail = strrchr(dir_name, sep); |
| 188 | int dir_len; |
| 189 | if (dir_tail == NULL) { |
| 190 | dir_len = 0; |
| 191 | } else { |
| 192 | dir_len = dir_tail - dir_name + 1; |
| 193 | } |
| 194 | |
| 195 | // Split the cp_attr by spaces, and add each file |
| 196 | char* file_start = cp_attr; |
| 197 | char* end = file_start + strlen(file_start); |
| 198 | |
| 199 | while (file_start < end) { |
| 200 | char* file_end = strchr(file_start, ' '); |
| 201 | if (file_end != NULL) { |
| 202 | *file_end = 0; |
| 203 | file_end += 1; |
| 204 | } else { |
| 205 | file_end = end; |
| 206 | } |
| 207 | |
| 208 | size_t name_len = strlen(file_start); |
| 209 | if (name_len > 0) { |
| 210 | ResourceMark rm(THREAD); |
| 211 | size_t libname_len = dir_len + name_len; |
| 212 | char* libname = NEW_RESOURCE_ARRAY(char, libname_len + 1); |
| 213 | int n = os::snprintf(libname, libname_len + 1, "%.*s%s" , dir_len, dir_name, file_start); |
| 214 | assert((size_t)n == libname_len, "Unexpected number of characters in string" ); |
| 215 | trace_class_path("library = " , libname); |
| 216 | ClassLoader::update_class_path_entry_list(libname, true, false); |
| 217 | } |
| 218 | |
| 219 | file_start = file_end; |
| 220 | } |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | void ClassLoaderExt::setup_search_paths() { |
| 225 | shared_paths_misc_info()->record_app_offset(); |
| 226 | ClassLoaderExt::setup_app_search_path(); |
| 227 | } |
| 228 | |
| 229 | void ClassLoaderExt::record_result(const s2 classpath_index, |
| 230 | InstanceKlass* result, |
| 231 | TRAPS) { |
| 232 | assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "Sanity" ); |
| 233 | |
| 234 | // We need to remember where the class comes from during dumping. |
| 235 | oop loader = result->class_loader(); |
| 236 | s2 classloader_type = ClassLoader::BOOT_LOADER; |
| 237 | if (SystemDictionary::is_system_class_loader(loader)) { |
| 238 | classloader_type = ClassLoader::APP_LOADER; |
| 239 | ClassLoaderExt::set_has_app_classes(); |
| 240 | } else if (SystemDictionary::is_platform_class_loader(loader)) { |
| 241 | classloader_type = ClassLoader::PLATFORM_LOADER; |
| 242 | ClassLoaderExt::set_has_platform_classes(); |
| 243 | } |
| 244 | if (classpath_index > ClassLoaderExt::max_used_path_index()) { |
| 245 | ClassLoaderExt::set_max_used_path_index(classpath_index); |
| 246 | } |
| 247 | result->set_shared_classpath_index(classpath_index); |
| 248 | result->set_class_loader_type(classloader_type); |
| 249 | } |
| 250 | |
| 251 | void ClassLoaderExt::finalize_shared_paths_misc_info() { |
| 252 | if (!_has_app_classes) { |
| 253 | shared_paths_misc_info()->pop_app(); |
| 254 | } |
| 255 | } |
| 256 | |
| 257 | // Load the class of the given name from the location given by path. The path is specified by |
| 258 | // the "source:" in the class list file (see classListParser.cpp), and can be a directory or |
| 259 | // a JAR file. |
| 260 | InstanceKlass* ClassLoaderExt::load_class(Symbol* name, const char* path, TRAPS) { |
| 261 | assert(name != NULL, "invariant" ); |
| 262 | assert(DumpSharedSpaces, "this function is only used with -Xshare:dump" ); |
| 263 | ResourceMark rm(THREAD); |
| 264 | const char* class_name = name->as_C_string(); |
| 265 | |
| 266 | const char* file_name = file_name_for_class_name(class_name, |
| 267 | name->utf8_length()); |
| 268 | assert(file_name != NULL, "invariant" ); |
| 269 | |
| 270 | // Lookup stream for parsing .class file |
| 271 | ClassFileStream* stream = NULL; |
| 272 | ClassPathEntry* e = find_classpath_entry_from_cache(path, CHECK_NULL); |
| 273 | if (e == NULL) { |
| 274 | return NULL; |
| 275 | } |
| 276 | { |
| 277 | PerfClassTraceTime vmtimer(perf_sys_class_lookup_time(), |
| 278 | ((JavaThread*) THREAD)->get_thread_stat()->perf_timers_addr(), |
| 279 | PerfClassTraceTime::CLASS_LOAD); |
| 280 | stream = e->open_stream(file_name, CHECK_NULL); |
| 281 | } |
| 282 | |
| 283 | if (NULL == stream) { |
| 284 | tty->print_cr("Preload Warning: Cannot find %s" , class_name); |
| 285 | return NULL; |
| 286 | } |
| 287 | |
| 288 | assert(stream != NULL, "invariant" ); |
| 289 | stream->set_verify(true); |
| 290 | |
| 291 | ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); |
| 292 | Handle protection_domain; |
| 293 | |
| 294 | InstanceKlass* result = KlassFactory::create_from_stream(stream, |
| 295 | name, |
| 296 | loader_data, |
| 297 | protection_domain, |
| 298 | NULL, // unsafe_anonymous_host |
| 299 | NULL, // cp_patches |
| 300 | THREAD); |
| 301 | |
| 302 | if (HAS_PENDING_EXCEPTION) { |
| 303 | tty->print_cr("Preload Error: Failed to load %s" , class_name); |
| 304 | return NULL; |
| 305 | } |
| 306 | return result; |
| 307 | } |
| 308 | |
| 309 | struct CachedClassPathEntry { |
| 310 | const char* _path; |
| 311 | ClassPathEntry* _entry; |
| 312 | }; |
| 313 | |
| 314 | static GrowableArray<CachedClassPathEntry>* cached_path_entries = NULL; |
| 315 | |
| 316 | ClassPathEntry* ClassLoaderExt::find_classpath_entry_from_cache(const char* path, TRAPS) { |
| 317 | // This is called from dump time so it's single threaded and there's no need for a lock. |
| 318 | assert(DumpSharedSpaces, "this function is only used with -Xshare:dump" ); |
| 319 | if (cached_path_entries == NULL) { |
| 320 | cached_path_entries = new (ResourceObj::C_HEAP, mtClass) GrowableArray<CachedClassPathEntry>(20, /*c heap*/ true); |
| 321 | } |
| 322 | CachedClassPathEntry ccpe; |
| 323 | for (int i=0; i<cached_path_entries->length(); i++) { |
| 324 | ccpe = cached_path_entries->at(i); |
| 325 | if (strcmp(ccpe._path, path) == 0) { |
| 326 | if (i != 0) { |
| 327 | // Put recent entries at the beginning to speed up searches. |
| 328 | cached_path_entries->remove_at(i); |
| 329 | cached_path_entries->insert_before(0, ccpe); |
| 330 | } |
| 331 | return ccpe._entry; |
| 332 | } |
| 333 | } |
| 334 | |
| 335 | struct stat st; |
| 336 | if (os::stat(path, &st) != 0) { |
| 337 | // File or directory not found |
| 338 | return NULL; |
| 339 | } |
| 340 | ClassPathEntry* new_entry = NULL; |
| 341 | |
| 342 | new_entry = create_class_path_entry(path, &st, false, false, CHECK_NULL); |
| 343 | if (new_entry == NULL) { |
| 344 | return NULL; |
| 345 | } |
| 346 | ccpe._path = strdup(path); |
| 347 | ccpe._entry = new_entry; |
| 348 | cached_path_entries->insert_before(0, ccpe); |
| 349 | return new_entry; |
| 350 | } |
| 351 | |