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 | |