Skip to content

Commit 427f506

Browse files
committedOct 25, 2022
8279366: CDS should allow alternative locations for JAR files in classpath
Reviewed-by: iklam, coleenp
1 parent 3a873d3 commit 427f506

File tree

10 files changed

+492
-23
lines changed

10 files changed

+492
-23
lines changed
 

‎src/hotspot/share/cds/cdsConstants.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ CDSConst CDSConstants::offsets[] = {
3535
{ "GenericCDSFileMapHeader::_crc", offset_of(GenericCDSFileMapHeader, _crc) },
3636
{ "GenericCDSFileMapHeader::_version", offset_of(GenericCDSFileMapHeader, _version) },
3737
{ "GenericCDSFileMapHeader::_header_size", offset_of(GenericCDSFileMapHeader, _header_size) },
38+
{ "GenericCDSFileMapHeader::_common_app_classpath_prefix_size", offset_of(GenericCDSFileMapHeader, _common_app_classpath_prefix_size) },
3839
{ "GenericCDSFileMapHeader::_base_archive_name_offset", offset_of(GenericCDSFileMapHeader, _base_archive_name_offset) },
3940
{ "GenericCDSFileMapHeader::_base_archive_name_size", offset_of(GenericCDSFileMapHeader, _base_archive_name_size) },
4041
{ "CDSFileMapHeaderBase::_space[0]", offset_of(CDSFileMapHeaderBase, _space) },

‎src/hotspot/share/cds/filemap.cpp

+84-14
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ void FileMapInfo::populate_header(size_t core_region_alignment) {
204204
size_t header_size;
205205
size_t base_archive_name_size = 0;
206206
size_t base_archive_name_offset = 0;
207+
size_t longest_common_prefix_size = 0;
207208
if (is_static()) {
208209
c_header_size = sizeof(FileMapHeader);
209210
header_size = c_header_size;
@@ -221,24 +222,30 @@ void FileMapInfo::populate_header(size_t core_region_alignment) {
221222
}
222223
FREE_C_HEAP_ARRAY(const char, default_base_archive_name);
223224
}
225+
ResourceMark rm;
226+
GrowableArray<const char*>* app_cp_array = create_dumptime_app_classpath_array();
227+
int len = app_cp_array->length();
228+
longest_common_prefix_size = longest_common_app_classpath_prefix_len(len, app_cp_array);
224229
_header = (FileMapHeader*)os::malloc(header_size, mtInternal);
225230
memset((void*)_header, 0, header_size);
226231
_header->populate(this,
227232
core_region_alignment,
228233
header_size,
229234
base_archive_name_size,
230-
base_archive_name_offset);
235+
base_archive_name_offset,
236+
longest_common_prefix_size);
231237
}
232238

233239
void FileMapHeader::populate(FileMapInfo *info, size_t core_region_alignment,
234240
size_t header_size, size_t base_archive_name_size,
235-
size_t base_archive_name_offset) {
241+
size_t base_archive_name_offset, size_t common_app_classpath_prefix_size) {
236242
// 1. We require _generic_header._magic to be at the beginning of the file
237243
// 2. FileMapHeader also assumes that _generic_header is at the beginning of the file
238244
assert(offset_of(FileMapHeader, _generic_header) == 0, "must be");
239245
set_header_size((unsigned int)header_size);
240246
set_base_archive_name_offset((unsigned int)base_archive_name_offset);
241247
set_base_archive_name_size((unsigned int)base_archive_name_size);
248+
set_common_app_classpath_prefix_size((unsigned int)common_app_classpath_prefix_size);
242249
set_magic(DynamicDumpSharedSpaces ? CDS_DYNAMIC_ARCHIVE_MAGIC : CDS_ARCHIVE_MAGIC);
243250
set_version(CURRENT_CDS_ARCHIVE_VERSION);
244251

@@ -311,6 +318,7 @@ void FileMapHeader::print(outputStream* st) {
311318
st->print_cr("- crc: 0x%08x", crc());
312319
st->print_cr("- version: 0x%x", version());
313320
st->print_cr("- header_size: " UINT32_FORMAT, header_size());
321+
st->print_cr("- common_app_classpath_size: " UINT32_FORMAT, common_app_classpath_prefix_size());
314322
st->print_cr("- base_archive_name_offset: " UINT32_FORMAT, base_archive_name_offset());
315323
st->print_cr("- base_archive_name_size: " UINT32_FORMAT, base_archive_name_size());
316324

@@ -808,6 +816,17 @@ bool FileMapInfo::check_paths_existence(const char* paths) {
808816
return exist;
809817
}
810818

819+
GrowableArray<const char*>* FileMapInfo::create_dumptime_app_classpath_array() {
820+
Arguments::assert_is_dumping_archive();
821+
GrowableArray<const char*>* path_array = new GrowableArray<const char*>(10);
822+
ClassPathEntry* cpe = ClassLoader::app_classpath_entries();
823+
while (cpe != NULL) {
824+
path_array->append(cpe->name());
825+
cpe = cpe->next();
826+
}
827+
return path_array;
828+
}
829+
811830
GrowableArray<const char*>* FileMapInfo::create_path_array(const char* paths) {
812831
GrowableArray<const char*>* path_array = new GrowableArray<const char*>(10);
813832
JavaThread* current = JavaThread::current();
@@ -842,23 +861,48 @@ bool FileMapInfo::classpath_failure(const char* msg, const char* name) {
842861
return false;
843862
}
844863

845-
bool FileMapInfo::check_paths(int shared_path_start_idx, int num_paths, GrowableArray<const char*>* rp_array) {
864+
unsigned int FileMapInfo::longest_common_app_classpath_prefix_len(int num_paths,
865+
GrowableArray<const char*>* rp_array) {
866+
if (num_paths == 0) {
867+
return 0;
868+
}
869+
unsigned int pos;
870+
for (pos = 0; ; pos++) {
871+
for (int i = 0; i < num_paths; i++) {
872+
if (rp_array->at(i)[pos] != '\0' && rp_array->at(i)[pos] == rp_array->at(0)[pos]) {
873+
continue;
874+
}
875+
876+
// search backward for the pos before the file separator char
877+
while (pos > 0 && rp_array->at(0)[--pos] != *os::file_separator());
878+
// return the file separator char position
879+
return pos + 1;
880+
}
881+
}
882+
return 0;
883+
}
884+
885+
bool FileMapInfo::check_paths(int shared_path_start_idx, int num_paths, GrowableArray<const char*>* rp_array,
886+
unsigned int dumptime_prefix_len, unsigned int runtime_prefix_len) {
846887
int i = 0;
847888
int j = shared_path_start_idx;
848-
bool mismatch = false;
849-
while (i < num_paths && !mismatch) {
889+
while (i < num_paths) {
850890
while (shared_path(j)->from_class_path_attr()) {
851891
// shared_path(j) was expanded from the JAR file attribute "Class-Path:"
852892
// during dump time. It's not included in the -classpath VM argument.
853893
j++;
854894
}
855-
if (!os::same_files(shared_path(j)->name(), rp_array->at(i))) {
856-
mismatch = true;
895+
assert(strlen(shared_path(j)->name()) > (size_t)dumptime_prefix_len, "sanity");
896+
const char* dumptime_path = shared_path(j)->name() + dumptime_prefix_len;
897+
assert(strlen(rp_array->at(i)) > (size_t)runtime_prefix_len, "sanity");
898+
const char* runtime_path = runtime_path = rp_array->at(i) + runtime_prefix_len;
899+
if (!os::same_files(dumptime_path, runtime_path)) {
900+
return true;
857901
}
858902
i++;
859903
j++;
860904
}
861-
return mismatch;
905+
return false;
862906
}
863907

864908
bool FileMapInfo::validate_boot_class_paths() {
@@ -912,7 +956,7 @@ bool FileMapInfo::validate_boot_class_paths() {
912956
// check the full runtime boot path, must match with dump time
913957
num = rp_len;
914958
}
915-
mismatch = check_paths(1, num, rp_array);
959+
mismatch = check_paths(1, num, rp_array, 0, 0);
916960
} else {
917961
// create_path_array() ignores non-existing paths. Although the dump time and runtime boot classpath lengths
918962
// are the same initially, after the call to create_path_array(), the runtime boot classpath length could become
@@ -961,9 +1005,20 @@ bool FileMapInfo::validate_app_class_paths(int shared_app_paths_len) {
9611005
// run 2: -cp x.jar:NE4:b.jar -> x.jar:b.jar -> mismatched
9621006

9631007
int j = header()->app_class_paths_start_index();
964-
mismatch = check_paths(j, shared_app_paths_len, rp_array);
1008+
mismatch = check_paths(j, shared_app_paths_len, rp_array, 0, 0);
9651009
if (mismatch) {
966-
return classpath_failure("[APP classpath mismatch, actual: -Djava.class.path=", appcp);
1010+
// To facilitate app deployment, we allow the JAR files to be moved *together* to
1011+
// a different location, as long as they are still stored under the same directory
1012+
// structure. E.g., the following is OK.
1013+
// java -Xshare:dump -cp /a/Foo.jar:/a/b/Bar.jar ...
1014+
// java -Xshare:auto -cp /x/y/Foo.jar:/x/y/b/Bar.jar ...
1015+
unsigned int dumptime_prefix_len = header()->common_app_classpath_prefix_size();
1016+
unsigned int runtime_prefix_len = longest_common_app_classpath_prefix_len(shared_app_paths_len, rp_array);
1017+
mismatch = check_paths(j, shared_app_paths_len, rp_array,
1018+
dumptime_prefix_len, runtime_prefix_len);
1019+
if (mismatch) {
1020+
return classpath_failure("[APP classpath mismatch, actual: -Djava.class.path=", appcp);
1021+
}
9671022
}
9681023
}
9691024
return true;
@@ -991,7 +1046,7 @@ bool FileMapInfo::check_module_paths() {
9911046
}
9921047
ResourceMark rm;
9931048
GrowableArray<const char*>* rp_array = create_path_array(rp);
994-
return check_paths(header()->app_module_paths_start_index(), num_paths, rp_array);
1049+
return check_paths(header()->app_module_paths_start_index(), num_paths, rp_array, 0, 0);
9951050
}
9961051

9971052
bool FileMapInfo::validate_shared_path_table() {
@@ -1205,6 +1260,10 @@ class FileHeaderHelper {
12051260
return false;
12061261
}
12071262

1263+
if (!check_common_app_classpath_prefix_len()) {
1264+
return false;
1265+
}
1266+
12081267
// All fields in the GenericCDSFileMapHeader has been validated.
12091268
_is_valid = true;
12101269
return true;
@@ -1282,6 +1341,16 @@ class FileHeaderHelper {
12821341
_base_archive_name = name;
12831342
}
12841343
}
1344+
1345+
return true;
1346+
}
1347+
1348+
bool check_common_app_classpath_prefix_len() {
1349+
int common_path_size = _header->_common_app_classpath_prefix_size;
1350+
if (common_path_size < 0) {
1351+
FileMapInfo::fail_continue("common app classpath prefix len < 0");
1352+
return false;
1353+
}
12851354
return true;
12861355
}
12871356
};
@@ -1364,8 +1433,9 @@ bool FileMapInfo::init_from_file(int fd) {
13641433
if (base_offset != 0 && name_size != 0) {
13651434
if (header_size != base_offset + name_size) {
13661435
log_info(cds)("_header_size: " UINT32_FORMAT, header_size);
1367-
log_info(cds)("base_archive_name_size: " UINT32_FORMAT, name_size);
1368-
log_info(cds)("base_archive_name_offset: " UINT32_FORMAT, base_offset);
1436+
log_info(cds)("common_app_classpath_size: " UINT32_FORMAT, header()->common_app_classpath_prefix_size());
1437+
log_info(cds)("base_archive_name_size: " UINT32_FORMAT, header()->base_archive_name_size());
1438+
log_info(cds)("base_archive_name_offset: " UINT32_FORMAT, header()->base_archive_name_offset());
13691439
FileMapInfo::fail_continue("The shared archive file has an incorrect header size.");
13701440
return false;
13711441
}

‎src/hotspot/share/cds/filemap.hpp

+11-2
Original file line numberDiff line numberDiff line change
@@ -245,13 +245,15 @@ class FileMapHeader: private CDSFileMapHeaderBase {
245245
unsigned int header_size() const { return _generic_header._header_size; }
246246
unsigned int base_archive_name_offset() const { return _generic_header._base_archive_name_offset; }
247247
unsigned int base_archive_name_size() const { return _generic_header._base_archive_name_size; }
248+
unsigned int common_app_classpath_prefix_size() const { return _generic_header._common_app_classpath_prefix_size; }
248249

249250
void set_magic(unsigned int m) { _generic_header._magic = m; }
250251
void set_crc(int crc_value) { _generic_header._crc = crc_value; }
251252
void set_version(int v) { _generic_header._version = v; }
252253
void set_header_size(unsigned int s) { _generic_header._header_size = s; }
253254
void set_base_archive_name_offset(unsigned int s) { _generic_header._base_archive_name_offset = s; }
254255
void set_base_archive_name_size(unsigned int s) { _generic_header._base_archive_name_size = s; }
256+
void set_common_app_classpath_prefix_size(unsigned int s) { _generic_header._common_app_classpath_prefix_size = s; }
255257

256258
size_t core_region_alignment() const { return _core_region_alignment; }
257259
int obj_alignment() const { return _obj_alignment; }
@@ -311,7 +313,8 @@ class FileMapHeader: private CDSFileMapHeaderBase {
311313
}
312314

313315
void populate(FileMapInfo *info, size_t core_region_alignment, size_t header_size,
314-
size_t base_archive_name_size, size_t base_archive_name_offset);
316+
size_t base_archive_name_size, size_t base_archive_name_offset,
317+
size_t common_app_classpath_size);
315318
static bool is_valid_region(int region) {
316319
return (0 <= region && region < NUM_CDS_REGIONS);
317320
}
@@ -556,10 +559,16 @@ class FileMapInfo : public CHeapObj<mtInternal> {
556559
char* skip_first_path_entry(const char* path) NOT_CDS_RETURN_(NULL);
557560
int num_paths(const char* path) NOT_CDS_RETURN_(0);
558561
bool check_paths_existence(const char* paths) NOT_CDS_RETURN_(false);
562+
GrowableArray<const char*>* create_dumptime_app_classpath_array() NOT_CDS_RETURN_(NULL);
559563
GrowableArray<const char*>* create_path_array(const char* path) NOT_CDS_RETURN_(NULL);
560564
bool classpath_failure(const char* msg, const char* name) NOT_CDS_RETURN_(false);
565+
unsigned int longest_common_app_classpath_prefix_len(int num_paths,
566+
GrowableArray<const char*>* rp_array)
567+
NOT_CDS_RETURN_(0);
561568
bool check_paths(int shared_path_start_idx, int num_paths,
562-
GrowableArray<const char*>* rp_array) NOT_CDS_RETURN_(false);
569+
GrowableArray<const char*>* rp_array,
570+
unsigned int dumptime_prefix_len,
571+
unsigned int runtime_prefix_len) NOT_CDS_RETURN_(false);
563572
bool validate_boot_class_paths() NOT_CDS_RETURN_(false);
564573
bool validate_app_class_paths(int shared_app_paths_len) NOT_CDS_RETURN_(false);
565574
bool map_heap_regions(int first, int max, bool is_open_archive,

‎src/hotspot/share/include/cds.h

+3
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ typedef struct GenericCDSFileMapHeader {
7373
int _crc; // header crc checksum, start from _base_archive_name_offset
7474
int _version; // CURRENT_CDS_ARCHIVE_VERSION of the jdk that dumped the this archive
7575
unsigned int _header_size; // total size of the header, in bytes
76+
unsigned int _common_app_classpath_prefix_size; // size of the common prefix of app class paths
77+
// 0 if no common prefix exists
7678
unsigned int _base_archive_name_offset; // offset where the base archive name is stored
7779
// static archive: 0
7880
// dynamic archive:
@@ -85,6 +87,7 @@ typedef struct GenericCDSFileMapHeader {
8587
// dynamic:
8688
// 0 for default base archive
8789
// non-zero for non-default base archive
90+
8891
} GenericCDSFileMapHeader;
8992

9093
// This type is used by the Serviceability Agent to access the contents of
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/*
2+
* Copyright (c) 2022, 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+
/*
26+
* @test
27+
* @bug 8279366
28+
* @summary Test app class paths checking with the longest common path taken into account.
29+
* @requires vm.cds
30+
* @library /test/lib
31+
* @compile test-classes/Hello.java
32+
* @compile test-classes/HelloMore.java
33+
* @run driver CommonAppClasspath
34+
*/
35+
36+
import java.io.File;
37+
import java.nio.file.Path;
38+
import java.nio.file.Paths;
39+
import jdk.test.lib.cds.CDSTestUtils;
40+
41+
public class CommonAppClasspath {
42+
43+
private static final Path USER_DIR = Paths.get(CDSTestUtils.getOutputDir());
44+
private static final String failedMessage = "APP classpath mismatch";
45+
private static final String successMessage1 = "Hello source: shared objects file";
46+
private static final String successMessage2 = "HelloMore source: shared objects file";
47+
48+
private static void runtimeTest(String classPath, String mainClass, int expectedExitValue,
49+
String ... checkMessages) throws Exception {
50+
CDSTestUtils.Result result = TestCommon.run(
51+
"-Xshare:on",
52+
"-XX:SharedArchiveFile=" + TestCommon.getCurrentArchiveName(),
53+
"-cp", classPath,
54+
"-Xlog:class+load=trace,class+path=info",
55+
mainClass);
56+
if (expectedExitValue == 0) {
57+
result.assertNormalExit( output -> {
58+
for (String s : checkMessages) {
59+
output.shouldContain(s);
60+
}
61+
});
62+
} else {
63+
result.assertAbnormalExit( output -> {
64+
for (String s : checkMessages) {
65+
output.shouldContain(s);
66+
}
67+
});
68+
}
69+
}
70+
71+
public static void main(String[] args) throws Exception {
72+
String appJar = JarBuilder.getOrCreateHelloJar();
73+
String appJar2 = JarBuilder.build("AppendClasspath_HelloMore", "HelloMore");
74+
// dump an archive with only appJar in the original location
75+
String jars = appJar;
76+
TestCommon.testDump(jars, TestCommon.list("Hello"));
77+
78+
// copy hello.jar to tmp dir
79+
Path destPath = CDSTestUtils.copyFile(appJar, System.getProperty("java.io.tmpdir"));
80+
81+
// Run with appJar relocated to tmp dir - should PASS
82+
runtimeTest(destPath.toString(), "Hello", 0, successMessage1);
83+
84+
// dump an archive with both jars in the original location
85+
jars = appJar + File.pathSeparator + appJar2;
86+
TestCommon.testDump(jars, TestCommon.list("Hello", "HelloMore"));
87+
88+
// copy hello.jar to USER_DIR/deploy
89+
String newDir = USER_DIR.toString() + File.separator + "deploy";
90+
destPath = CDSTestUtils.copyFile(appJar, newDir);
91+
92+
// copy AppendClasspath_HelloMore.jar to USER_DIR/deploy
93+
Path destPath2 = CDSTestUtils.copyFile(appJar2, newDir);
94+
95+
// Run with both jars relocated to USER_DIR/dpeloy - should PASS
96+
runtimeTest(destPath.toString() + File.pathSeparator + destPath2.toString(),
97+
"HelloMore", 0, successMessage1, successMessage2);
98+
99+
// Run with relocation of only the second jar - should FAIL
100+
runtimeTest(appJar + File.pathSeparator + destPath2.toString(),
101+
"HelloMore", 1, failedMessage);
102+
103+
// Run with relocation of only the first jar - should FAIL
104+
runtimeTest(destPath.toString() + File.pathSeparator + appJar2,
105+
"HelloMore", 1, failedMessage);
106+
107+
// Dump CDS archive with the first jar relocated.
108+
jars = destPath.toString() + File.pathSeparator + appJar2;
109+
TestCommon.testDump(jars, TestCommon.list("Hello", "HelloMore"));
110+
111+
// Run with first jar relocated - should PASS
112+
runtimeTest(destPath.toString() + File.pathSeparator + appJar2,
113+
"HelloMore", 0, successMessage1, successMessage2);
114+
115+
// Run with both jars relocated - should FAIL
116+
runtimeTest(destPath.toString() + File.pathSeparator + destPath2.toString(),
117+
"HelloMore", 1, failedMessage);
118+
119+
// Copy hello.jar to USER_DIR/a
120+
destPath = CDSTestUtils.copyFile(appJar, USER_DIR.toString() + File.separator + "a");
121+
122+
// copy AppendClasspath_HelloMore.jar to USER_DIR/aa
123+
destPath2 = CDSTestUtils.copyFile(appJar2, USER_DIR.toString() + File.separator + "aa");
124+
125+
// Dump CDS archive with the both jar files relocated
126+
// appJar to USER_DIR/a
127+
// appJar2 to USER_DIR/aa
128+
jars = destPath.toString() + File.pathSeparator + destPath2.toString();
129+
TestCommon.testDump(jars, TestCommon.list("Hello", "HelloMore"));
130+
131+
// Copy hello.jar to USER_DIR/x/a
132+
Path runPath = CDSTestUtils.copyFile(appJar, USER_DIR.toString() + File.separator + "x" + File.separator + "a");
133+
134+
// copy AppendClasspath_HelloMore.jar to USER_DIR/x/aa
135+
Path runPath2= CDSTestUtils.copyFile(appJar2, USER_DIR.toString() + File.separator + "x" + File.separator + "aa");
136+
137+
// Run with both jars relocated to USER_DIR/x/a and USER_DIR/x/aa dirs - should PASS
138+
runtimeTest(runPath.toString() + File.pathSeparator + runPath2.toString(),
139+
"HelloMore", 0, successMessage1, successMessage2);
140+
141+
// copy AppendClasspath_HelloMore.jar to USER_DIR/x/a
142+
runPath2= CDSTestUtils.copyFile(appJar2, USER_DIR.toString() + File.separator + "x" + File.separator + "a");
143+
144+
// Run with both jars relocated to USER_DIR/x/a dir - should FAIL
145+
runtimeTest(runPath.toString() + File.pathSeparator + runPath2.toString(),
146+
"HelloMore", 1, failedMessage);
147+
}
148+
}

‎test/hotspot/jtreg/runtime/cds/appcds/SharedArchiveConsistency.java

+10
Original file line numberDiff line numberDiff line change
@@ -271,5 +271,15 @@ public static void main(String... args) throws Exception {
271271
baseArchiveNameOffset = CDSArchiveUtils.baseArchiveNameOffset(copiedJsa);
272272
System.out.println("new baseArchiveNameOffset = " + baseArchiveNameOffset);
273273
testAndCheck(verifyExecArgs);
274+
275+
// modify _common_app_classpath_size
276+
String wrongCommonAppClasspathOffset = startNewArchive("wrongCommonAppClasspathOffset");
277+
copiedJsa = CDSArchiveUtils.copyArchiveFile(orgJsaFile, wrongCommonAppClasspathOffset);
278+
int commonAppClasspathPrefixSize = CDSArchiveUtils.commonAppClasspathPrefixSize(copiedJsa);
279+
System.out.println(" commonAppClasspathPrefixSize = " + commonAppClasspathPrefixSize);
280+
CDSArchiveUtils.writeData(copiedJsa, CDSArchiveUtils.offsetCommonAppClasspathPrefixSize(), commonAppClasspathPrefixSize * 2);
281+
commonAppClasspathPrefixSize = CDSArchiveUtils.commonAppClasspathPrefixSize(copiedJsa);
282+
System.out.println("new commonAppClasspathPrefixSize = " + commonAppClasspathPrefixSize);
283+
testAndCheck(verifyExecArgs);
274284
}
275285
}

‎test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/ArchiveConsistency.java

+23-7
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,23 @@ private static void doTest(String baseArchiveName, String topArchiveName) throws
150150
appJar, mainClass, isAuto ? 0 : 1,
151151
"Base archive name is damaged");
152152

153-
startTest("5. Make base archive name not terminated with '\0'");
153+
startTest("5a. Modify common app classpath size");
154+
String wrongCommonAppClasspathOffset = getNewArchiveName("wrongCommonAppClasspathOffset");
155+
copiedJsa = CDSArchiveUtils.copyArchiveFile(jsa, wrongCommonAppClasspathOffset);
156+
int commonAppClasspathPrefixSize = CDSArchiveUtils.commonAppClasspathPrefixSize(copiedJsa);
157+
CDSArchiveUtils.writeData(copiedJsa, CDSArchiveUtils.offsetCommonAppClasspathPrefixSize(), -1);
158+
runTwo(baseArchiveName, wrongCommonAppClasspathOffset,
159+
appJar, mainClass, isAuto ? 0 : 1,
160+
"common app classpath prefix len < 0");
161+
162+
startTest("5b. Modify common app classpath size, run with -XX:-VerifySharedSpaces");
163+
VERIFY_CRC = true;
164+
runTwo(baseArchiveName, modTop,
165+
appJar, mainClass, isAuto ? 0 : 1,
166+
"Header checksum verification failed");
167+
VERIFY_CRC = false;
168+
169+
startTest("6. Make base archive name not terminated with '\0'");
154170
String wrongBaseName = getNewArchiveName("wrongBaseName");
155171
copiedJsa = CDSArchiveUtils.copyArchiveFile(jsa, wrongBaseName);
156172
baseArchiveNameOffset = CDSArchiveUtils.baseArchiveNameOffset(copiedJsa);
@@ -162,7 +178,7 @@ private static void doTest(String baseArchiveName, String topArchiveName) throws
162178
appJar, mainClass, isAuto ? 0 : 1,
163179
"Base archive name is damaged");
164180

165-
startTest("6. Modify base archive name to a file that doesn't exist");
181+
startTest("7. Modify base archive name to a file that doesn't exist");
166182
String wrongBaseName2 = getNewArchiveName("wrongBaseName2");
167183
copiedJsa = CDSArchiveUtils.copyArchiveFile(jsa, wrongBaseName2);
168184
baseArchiveNameOffset = CDSArchiveUtils.baseArchiveNameOffset(copiedJsa);
@@ -182,23 +198,23 @@ private static void doTest(String baseArchiveName, String topArchiveName) throws
182198
// -XX:SharedArchiveFile=non-exist-base.jsa:top.jsa
183199
// -XX:SharedArchiveFile=base.jsa:non-exist-top.jsa
184200
// -XX:SharedArchiveFile=non-exist-base.jsa:non-exist-top.jsa
185-
startTest("7. Non-exist base archive");
201+
startTest("8. Non-exist base archive");
186202
String nonExistBase = "non-exist-base.jsa";
187203
File nonExistBaseFile = new File(nonExistBase);
188204
nonExistBaseFile.delete();
189205
runTwo(nonExistBase, topArchiveName,
190206
appJar, mainClass, isAuto ? 0 : 1,
191207
"Specified shared archive not found (" + nonExistBase + ")");
192208

193-
startTest("8. Non-exist top archive");
209+
startTest("9. Non-exist top archive");
194210
String nonExistTop = "non-exist-top.jsa";
195211
File nonExistTopFile = new File(nonExistTop);
196212
nonExistTopFile.delete();
197213
runTwo(baseArchiveName, nonExistTop,
198214
appJar, mainClass, isAuto ? 0 : 1,
199215
"Specified shared archive not found (" + nonExistTop + ")");
200216

201-
startTest("9. nost-exist-base and non-exist-top");
217+
startTest("10. nost-exist-base and non-exist-top");
202218
runTwo(nonExistBase, nonExistTop,
203219
appJar, mainClass, isAuto ? 0 : 1,
204220
"Specified shared archive not found (" + nonExistBase + ")");
@@ -209,7 +225,7 @@ private static void doTest(String baseArchiveName, String topArchiveName) throws
209225
if (!isUseSharedSpacesDisabled()) {
210226
new File(baseArchiveName).delete();
211227

212-
startTest("10. -XX:+AutoCreateSharedArchive -XX:SharedArchiveFile=" + topArchiveName);
228+
startTest("11. -XX:+AutoCreateSharedArchive -XX:SharedArchiveFile=" + topArchiveName);
213229
run(topArchiveName,
214230
"-Xshare:auto",
215231
"-XX:+AutoCreateSharedArchive",
@@ -219,7 +235,7 @@ private static void doTest(String baseArchiveName, String topArchiveName) throws
219235
output.shouldContain("warning: -XX:+AutoCreateSharedArchive is unsupported when base CDS archive is not loaded");
220236
});
221237

222-
startTest("11. -XX:SharedArchiveFile=" + topArchiveName + " -XX:ArchiveClassesAtExit=" + getNewArchiveName("top3"));
238+
startTest("12. -XX:SharedArchiveFile=" + topArchiveName + " -XX:ArchiveClassesAtExit=" + getNewArchiveName("top3"));
223239
run(topArchiveName,
224240
"-Xshare:auto",
225241
"-XX:ArchiveClassesAtExit=" + getNewArchiveName("top3"),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/*
2+
* Copyright (c) 2022, 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+
/*
26+
* @test
27+
* @bug 8279366
28+
* @summary Test app class paths checking with the longest common path taken into account.
29+
* @requires vm.cds
30+
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds
31+
* @build jdk.test.whitebox.WhiteBox
32+
* @compile ../test-classes/Hello.java
33+
* @compile ../test-classes/HelloMore.java
34+
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
35+
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. CommonAppClasspath
36+
*/
37+
38+
import java.io.File;
39+
import java.nio.file.Path;
40+
import java.nio.file.Paths;
41+
import jdk.test.lib.cds.CDSTestUtils;
42+
43+
public class CommonAppClasspath extends DynamicArchiveTestBase {
44+
45+
private static final Path USER_DIR = Paths.get(CDSTestUtils.getOutputDir());
46+
private static final String failedMessage = "shared class paths mismatch";
47+
private static final String successMessage1 = "Hello source: shared objects file";
48+
private static final String successMessage2 = "HelloMore source: shared objects file";
49+
50+
private static void runtimeTest(String topArchiveName, String classPath,
51+
String mainClass, int expectedExitValue,
52+
String ... checkMessages) throws Exception {
53+
CDSTestUtils.Result result = run(topArchiveName,
54+
"-Xlog:class+load",
55+
"-Xlog:cds+dynamic=debug,cds=debug",
56+
"-cp", classPath, mainClass);
57+
if (expectedExitValue == 0) {
58+
result.assertNormalExit( output -> {
59+
for (String s : checkMessages) {
60+
output.shouldContain(s);
61+
}
62+
});
63+
} else {
64+
result.assertAbnormalExit( output -> {
65+
for (String s : checkMessages) {
66+
output.shouldContain(s);
67+
}
68+
});
69+
}
70+
}
71+
72+
public static void main(String[] args) throws Exception {
73+
runTest(CommonAppClasspath::testDefaultBase);
74+
}
75+
76+
static void testDefaultBase() throws Exception {
77+
String topArchiveName = getNewArchiveName("top");
78+
doTest(topArchiveName);
79+
}
80+
81+
private static void doTest(String topArchiveName) throws Exception {
82+
String mainClass = "HelloMore";
83+
String appJar = JarBuilder.getOrCreateHelloJar();
84+
String appJar2 = JarBuilder.build("AppendClasspath_HelloMore", mainClass);
85+
86+
// dump an archive with only appJar in the original location
87+
String jars = appJar;
88+
dump(topArchiveName,
89+
"-Xlog:cds",
90+
"-Xlog:cds+dynamic=debug",
91+
"-cp", jars, "Hello")
92+
.assertNormalExit(output -> {
93+
output.shouldContain("Written dynamic archive 0x");
94+
});
95+
96+
// copy hello.jar to tmp dir
97+
Path destPath = CDSTestUtils.copyFile(appJar, System.getProperty("java.io.tmpdir"));
98+
99+
// Run with appJar relocated to tmp dir - should PASS
100+
jars = destPath.toString();
101+
runtimeTest(topArchiveName, jars, "Hello", 0, successMessage1);
102+
103+
// dump an archive with both jars in the original location
104+
jars = appJar + File.pathSeparator + appJar2;
105+
dump(topArchiveName,
106+
"-Xlog:cds",
107+
"-Xlog:cds+dynamic=debug",
108+
"-cp", jars, mainClass)
109+
.assertNormalExit(output -> {
110+
output.shouldContain("Written dynamic archive 0x");
111+
});
112+
113+
// copy hello.jar to USER_DIR/deploy
114+
String newDir = USER_DIR.toString() + File.separator + "deploy";
115+
destPath = CDSTestUtils.copyFile(appJar, newDir);
116+
117+
// copy AppendClasspath_HelloMore.jar to USER_DIR/deploy
118+
Path destPath2 = CDSTestUtils.copyFile(appJar2, newDir);
119+
120+
// Run with both jars relocated to USER_DIR/dpeloy - should PASS
121+
jars = destPath.toString() + File.pathSeparator + destPath2.toString();
122+
runtimeTest(topArchiveName, jars, mainClass, 0, successMessage1, successMessage2);
123+
124+
// Run with relocation of only the second jar - should FAIL
125+
jars = appJar + File.pathSeparator + destPath2.toString();
126+
runtimeTest(topArchiveName, jars, mainClass, 1, failedMessage);
127+
128+
// Run with relocation of only the first jar - should FAIL
129+
jars = destPath.toString() + File.pathSeparator + appJar2;
130+
runtimeTest(topArchiveName, jars, mainClass, 1, failedMessage);
131+
132+
// Dump CDS archive with the first jar relocated.
133+
jars = destPath.toString() + File.pathSeparator + appJar2;
134+
dump(topArchiveName,
135+
"-Xlog:cds",
136+
"-Xlog:cds+dynamic=debug",
137+
"-cp", jars, mainClass)
138+
.assertNormalExit(output -> {
139+
output.shouldContain("Written dynamic archive 0x");
140+
});
141+
142+
// Run with first jar relocated - should PASS
143+
jars = destPath.toString() + File.pathSeparator + appJar2;
144+
runtimeTest(topArchiveName, jars, mainClass, 0, successMessage1, successMessage2);
145+
146+
// Run with both jars relocated - should FAIL
147+
jars = destPath.toString() + File.pathSeparator + destPath2.toString();
148+
runtimeTest(topArchiveName, jars, mainClass, 1, failedMessage);
149+
150+
// Copy hello.jar to USER_DIR/a
151+
destPath = CDSTestUtils.copyFile(appJar, USER_DIR.toString() + File.separator + "a");
152+
153+
// copy AppendClasspath_HelloMore.jar to USER_DIR/aa
154+
destPath2 = CDSTestUtils.copyFile(appJar2, USER_DIR.toString() + File.separator + "aa");
155+
156+
// Dump CDS archive with the both jar files relocated
157+
// appJar to USER_DIR/a
158+
// appJar2 to USER_DIR/aa
159+
jars = destPath.toString() + File.pathSeparator + destPath2.toString();
160+
dump(topArchiveName,
161+
"-Xlog:cds",
162+
"-Xlog:cds+dynamic=debug",
163+
"-cp", jars, mainClass)
164+
.assertNormalExit(output -> {
165+
output.shouldContain("Written dynamic archive 0x");
166+
});
167+
168+
// Copy hello.jar to USER_DIR/x/a
169+
Path runPath = CDSTestUtils.copyFile(appJar, USER_DIR.toString() + File.separator + "x" + File.separator + "a");
170+
171+
// copy AppendClasspath_HelloMore.jar to USER_DIR/x/aa
172+
Path runPath2= CDSTestUtils.copyFile(appJar2, USER_DIR.toString() + File.separator + "x" + File.separator + "aa");
173+
174+
// Run with both jars relocated to USER_DIR/x/a and USER_DIR/x/aa dirs - should PASS
175+
jars = runPath.toString() + File.pathSeparator + runPath2.toString();
176+
runtimeTest(topArchiveName, jars, mainClass, 0, successMessage1, successMessage2);
177+
178+
// copy AppendClasspath_HelloMore.jar to USER_DIR/x/a
179+
runPath2= CDSTestUtils.copyFile(appJar2, USER_DIR.toString() + File.separator + "x" + File.separator + "a");
180+
181+
// Run with both jars relocated to USER_DIR/x/a dir - should FAIL
182+
jars = runPath.toString() + File.pathSeparator + runPath2.toString();
183+
runtimeTest(topArchiveName, jars, mainClass, 1, failedMessage);
184+
}
185+
}

‎test/lib/jdk/test/lib/cds/CDSArchiveUtils.java

+7
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public class CDSArchiveUtils {
5353
private static int offsetCrc; // offset of GenericCDSFileMapHeader::_crc
5454
private static int offsetVersion; // offset of GenericCDSFileMapHeader::_version
5555
private static int offsetHeaderSize; // offset of GenericCDSFileMapHeader::_header_size
56+
private static int offsetCommonAppClasspathPrefixSize;// offset of GenericCDSFileMapHeader::_common_app_classpath_size
5657
private static int offsetBaseArchiveNameOffset;// offset of GenericCDSFileMapHeader::_base_archive_name_offset
5758
private static int offsetBaseArchiveNameSize; // offset of GenericCDSFileMapHeader::_base_archive_name_size
5859
private static int offsetJvmIdent; // offset of FileMapHeader::_jvm_ident
@@ -93,6 +94,7 @@ public class CDSArchiveUtils {
9394
offsetCrc = wb.getCDSOffsetForName("GenericCDSFileMapHeader::_crc");
9495
offsetVersion = wb.getCDSOffsetForName("GenericCDSFileMapHeader::_version");
9596
offsetHeaderSize = wb.getCDSOffsetForName("GenericCDSFileMapHeader::_header_size");
97+
offsetCommonAppClasspathPrefixSize = wb.getCDSOffsetForName("GenericCDSFileMapHeader::_common_app_classpath_prefix_size");
9698
offsetBaseArchiveNameOffset = wb.getCDSOffsetForName("GenericCDSFileMapHeader::_base_archive_name_offset");
9799
offsetBaseArchiveNameSize = wb.getCDSOffsetForName("GenericCDSFileMapHeader::_base_archive_name_size");
98100
offsetJvmIdent = wb.getCDSOffsetForName("FileMapHeader::_jvm_ident");
@@ -131,6 +133,7 @@ public class CDSArchiveUtils {
131133
public static int offsetCrc() { return offsetCrc; }
132134
public static int offsetVersion() { return offsetVersion; }
133135
public static int offsetHeaderSize() { return offsetHeaderSize; }
136+
public static int offsetCommonAppClasspathPrefixSize() { return offsetCommonAppClasspathPrefixSize; }
134137
public static int offsetBaseArchiveNameOffset() { return offsetBaseArchiveNameOffset; }
135138
public static int offsetBaseArchiveNameSize() { return offsetBaseArchiveNameSize; }
136139
public static int offsetJvmIdent() { return offsetJvmIdent; }
@@ -158,6 +161,10 @@ public static long fileHeaderSizeAligned(File jsaFile) throws Exception {
158161
return alignUpWithAlignment(size);
159162
}
160163

164+
public static int commonAppClasspathPrefixSize(File jsaFile) throws Exception {
165+
return (int)readInt(jsaFile, offsetCommonAppClasspathPrefixSize, 4);
166+
}
167+
161168
public static int baseArchiveNameOffset(File jsaFile) throws Exception {
162169
return (int)readInt(jsaFile, offsetBaseArchiveNameOffset, 4);
163170
}

‎test/lib/jdk/test/lib/cds/CDSTestUtils.java

+20
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@
2828
import java.io.PrintStream;
2929
import java.nio.file.Files;
3030
import java.nio.file.CopyOption;
31+
import java.nio.file.Path;
32+
import java.nio.file.Paths;
3133
import java.nio.file.StandardCopyOption;
34+
import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES;
35+
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
3236
import java.text.SimpleDateFormat;
3337
import java.util.ArrayList;
3438
import java.util.Date;
@@ -761,4 +765,20 @@ public static ProcessBuilder makeBuilder(String... args) throws Exception {
761765
System.out.println(" ]");
762766
return new ProcessBuilder(args);
763767
}
768+
769+
public static Path copyFile(String srcFile, String destDir) throws Exception {
770+
int idx = srcFile.lastIndexOf(File.separator);
771+
String jarName = srcFile.substring(idx + 1);
772+
Path srcPath = Paths.get(jarName);
773+
Path newPath = Paths.get(destDir);
774+
Path newDir;
775+
if (!Files.exists(newPath)) {
776+
newDir = Files.createDirectories(newPath);
777+
} else {
778+
newDir = newPath;
779+
}
780+
Path destPath = newDir.resolve(jarName);
781+
Files.copy(srcPath, destPath, REPLACE_EXISTING, COPY_ATTRIBUTES);
782+
return destPath;
783+
}
764784
}

0 commit comments

Comments
 (0)
Please sign in to comment.