diff --git a/8143925-enhancing-CounterMode.crypt-for-AESCrypt.patch b/8143925-enhancing-CounterMode.crypt-for-AESCrypt.patch new file mode 100644 index 0000000000000000000000000000000000000000..81acb5c4ec2e49c9990e1cf2ed85d1ecad7211ac --- /dev/null +++ b/8143925-enhancing-CounterMode.crypt-for-AESCrypt.patch @@ -0,0 +1,3938 @@ +From 02b097417275acaad294d71a852c2def2222be25 Mon Sep 17 00:00:00 2001 +From: kuenking111 +Date: Sat, 3 Sep 2022 14:17:50 +0000 +Subject: [PATCH 1/6] 8143925-enhancing-CounterMode.crypt-for-AESCrypt + +--- + .../src/cpu/aarch64/vm/assembler_aarch64.hpp | 35 +- + .../cpu/aarch64/vm/macroAssembler_aarch64.hpp | 17 + + .../aarch64/vm/macroAssembler_aarch64_aes.cpp | 685 ++++++++++++++++++ + .../cpu/aarch64/vm/stubGenerator_aarch64.cpp | 324 ++++++++- + .../cpu/aarch64/vm/stubRoutines_aarch64.hpp | 2 +- + .../src/cpu/aarch64/vm/vm_version_aarch64.cpp | 13 +- + hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp | 5 + + hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp | 5 + + hotspot/src/cpu/x86/vm/assembler_x86.cpp | 74 +- + hotspot/src/cpu/x86/vm/assembler_x86.hpp | 12 + + .../src/cpu/x86/vm/stubGenerator_x86_32.cpp | 344 +++++++++ + .../src/cpu/x86/vm/stubGenerator_x86_64.cpp | 340 ++++++++- + hotspot/src/cpu/x86/vm/stubRoutines_x86.cpp | 1 + + hotspot/src/cpu/x86/vm/stubRoutines_x86.hpp | 5 + + .../src/cpu/x86/vm/stubRoutines_x86_32.hpp | 2 +- + .../src/cpu/x86/vm/stubRoutines_x86_64.hpp | 2 +- + hotspot/src/cpu/x86/vm/vm_version_x86.cpp | 36 + + hotspot/src/share/vm/classfile/vmSymbols.hpp | 4 + + hotspot/src/share/vm/opto/escape.cpp | 1 + + hotspot/src/share/vm/opto/library_call.cpp | 174 +++++ + hotspot/src/share/vm/opto/runtime.cpp | 29 + + hotspot/src/share/vm/opto/runtime.hpp | 1 + + hotspot/src/share/vm/runtime/globals.hpp | 3 + + hotspot/src/share/vm/runtime/stubRoutines.cpp | 1 + + hotspot/src/share/vm/runtime/stubRoutines.hpp | 2 + + hotspot/src/share/vm/runtime/vmStructs.cpp | 1 + + .../test/compiler/7184394/TestAESBase.java | 4 +- + .../test/compiler/7184394/TestAESMain.java | 7 + + .../com/sun/crypto/provider/CounterMode.java | 11 +- + .../classes/com/sun/crypto/provider/GCTR.java | 89 +-- + .../com/sun/crypto/provider/GHASH.java | 20 +- + .../sun/security/ssl/SSLSocketImpl.java | 14 +- + .../security/ssl/SSLSocketInputRecord.java | 215 +++--- + .../sun/security/ssl/SSLTransport.java | 4 + + .../bench/javax/crypto/full/AESGCMBench.java | 128 ++++ + .../javax/crypto/full/AESGCMByteBuffer.java | 163 +++++ + .../bench/javax/crypto/full/CryptoBase.java | 102 +++ + .../bench/javax/crypto/small/AESGCMBench.java | 36 + + .../javax/crypto/small/AESGCMByteBuffer.java | 36 + + .../ssl/SSLSocketImpl/ClientTimeout.java | 3 +- + .../SSLSocketImpl/SSLExceptionForIOIssue.java | 4 +- + 41 files changed, 2738 insertions(+), 216 deletions(-) + create mode 100644 hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64_aes.cpp + create mode 100644 jdk/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMBench.java + create mode 100644 jdk/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMByteBuffer.java + create mode 100644 jdk/test/micro/org/openjdk/bench/javax/crypto/full/CryptoBase.java + create mode 100644 jdk/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMBench.java + create mode 100644 jdk/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMByteBuffer.java + +diff --git a/hotspot/src/cpu/aarch64/vm/assembler_aarch64.hpp b/hotspot/src/cpu/aarch64/vm/assembler_aarch64.hpp +index b0fa9b5fc..9202e61f8 100644 +--- a/hotspot/src/cpu/aarch64/vm/assembler_aarch64.hpp ++++ b/hotspot/src/cpu/aarch64/vm/assembler_aarch64.hpp +@@ -146,6 +146,21 @@ REGISTER_DECLARATION(Register, esp, r20); + + #define assert_cond(ARG1) assert(ARG1, #ARG1) + ++// In many places we've added C-style casts to silence compiler ++// warnings, for example when truncating a size_t to an int when we ++// know the size_t is a small struct. Such casts are risky because ++// they effectively disable useful compiler warnings. We can make our ++// lives safer with this function, which ensures that any cast is ++// reversible without loss of information. It doesn't check ++// everything: it isn't intended to make sure that pointer types are ++// compatible, for example. ++template ++T2 checked_cast(T1 thing) { ++ T2 result = static_cast(thing); ++ assert(static_cast(result) == thing, "must be"); ++ return result; ++} ++ + namespace asm_util { + uint32_t encode_logical_immediate(bool is32, uint64_t imm); + }; +@@ -193,7 +208,7 @@ public: + static inline uint32_t extract(uint32_t val, int msb, int lsb) { + int nbits = msb - lsb + 1; + assert_cond(msb >= lsb); +- uint32_t mask = (1U << nbits) - 1; ++ uint32_t mask = checked_cast(right_n_bits(nbits)); + uint32_t result = val >> lsb; + result &= mask; + return result; +@@ -208,7 +223,7 @@ public: + int nbits = msb - lsb + 1; + guarantee(val < (1U << nbits), "Field too big for insn"); + assert_cond(msb >= lsb); +- unsigned mask = (1U << nbits) - 1; ++ unsigned mask = checked_cast(right_n_bits(nbits)); + val <<= lsb; + mask <<= lsb; + unsigned target = *(unsigned *)a; +@@ -222,7 +237,7 @@ public: + long chk = val >> (nbits - 1); + guarantee (chk == -1 || chk == 0, "Field too big for insn"); + unsigned uval = val; +- unsigned mask = (1U << nbits) - 1; ++ unsigned mask = checked_cast(right_n_bits(nbits)); + uval &= mask; + uval <<= lsb; + mask <<= lsb; +@@ -234,9 +249,9 @@ public: + + void f(unsigned val, int msb, int lsb) { + int nbits = msb - lsb + 1; +- guarantee(val < (1U << nbits), "Field too big for insn"); ++ guarantee(val < (1ULL << nbits), "Field too big for insn"); + assert_cond(msb >= lsb); +- unsigned mask = (1U << nbits) - 1; ++ unsigned mask = checked_cast(right_n_bits(nbits)); + val <<= lsb; + mask <<= lsb; + insn |= val; +@@ -255,7 +270,7 @@ public: + long chk = val >> (nbits - 1); + guarantee (chk == -1 || chk == 0, "Field too big for insn"); + unsigned uval = val; +- unsigned mask = (1U << nbits) - 1; ++ unsigned mask = checked_cast(right_n_bits(nbits)); + uval &= mask; + f(uval, lsb + nbits - 1, lsb); + } +@@ -280,7 +295,7 @@ public: + + unsigned get(int msb = 31, int lsb = 0) { + int nbits = msb - lsb + 1; +- unsigned mask = ((1U << nbits) - 1) << lsb; ++ unsigned mask = checked_cast(right_n_bits(nbits)) << lsb; + assert_cond((bits & mask) == mask); + return (insn & mask) >> lsb; + } +@@ -1991,21 +2006,21 @@ public: + starti; + f(0,31), f((int)T & 1, 30); + f(op1, 29, 21), f(0, 20, 16), f(op2, 15, 12); +- f((int)T >> 1, 11, 10), rf(Xn, 5), rf(Vt, 0); ++ f((int)T >> 1, 11, 10), srf(Xn, 5), rf(Vt, 0); + } + void ld_st(FloatRegister Vt, SIMD_Arrangement T, Register Xn, + int imm, int op1, int op2) { + starti; + f(0,31), f((int)T & 1, 30); + f(op1 | 0b100, 29, 21), f(0b11111, 20, 16), f(op2, 15, 12); +- f((int)T >> 1, 11, 10), rf(Xn, 5), rf(Vt, 0); ++ f((int)T >> 1, 11, 10), srf(Xn, 5), rf(Vt, 0); + } + void ld_st(FloatRegister Vt, SIMD_Arrangement T, Register Xn, + Register Xm, int op1, int op2) { + starti; + f(0,31), f((int)T & 1, 30); + f(op1 | 0b100, 29, 21), rf(Xm, 16), f(op2, 15, 12); +- f((int)T >> 1, 11, 10), rf(Xn, 5), rf(Vt, 0); ++ f((int)T >> 1, 11, 10), srf(Xn, 5), rf(Vt, 0); + } + + void ld_st(FloatRegister Vt, SIMD_Arrangement T, Address a, int op1, int op2) { +diff --git a/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64.hpp b/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64.hpp +index 0ca694038..d334f1b69 100644 +--- a/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64.hpp ++++ b/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64.hpp +@@ -1240,6 +1240,23 @@ public: + void multiply_to_len(Register x, Register xlen, Register y, Register ylen, Register z, + Register zlen, Register tmp1, Register tmp2, Register tmp3, + Register tmp4, Register tmp5, Register tmp6, Register tmp7); ++ void ghash_multiply(FloatRegister result_lo, FloatRegister result_hi, ++ FloatRegister a, FloatRegister b, FloatRegister a1_xor_a0, ++ FloatRegister tmp1, FloatRegister tmp2, FloatRegister tmp3); ++ void ghash_reduce(FloatRegister result, FloatRegister lo, FloatRegister hi, ++ FloatRegister p, FloatRegister z, FloatRegister t1); ++ void ghash_processBlocks_wide(address p, Register state, Register subkeyH, ++ Register data, Register blocks, int unrolls); ++ void ghash_modmul (FloatRegister result, ++ FloatRegister result_lo, FloatRegister result_hi, FloatRegister b, ++ FloatRegister a, FloatRegister vzr, FloatRegister a1_xor_a0, FloatRegister p, ++ FloatRegister t1, FloatRegister t2, FloatRegister t3); ++ ++ void aesenc_loadkeys(Register key, Register keylen); ++ void aesecb_encrypt(Register from, Register to, Register keylen, ++ FloatRegister data = v0, int unrolls = 1); ++ void aesecb_decrypt(Register from, Register to, Register key, Register keylen); ++ void aes_round(FloatRegister input, FloatRegister subkey); + // ISB may be needed because of a safepoint + void maybe_isb() { isb(); } + +diff --git a/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64_aes.cpp b/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64_aes.cpp +new file mode 100644 +index 000000000..1db79c97a +--- /dev/null ++++ b/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64_aes.cpp +@@ -0,0 +1,685 @@ ++/* ++ * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2014, 2021, Red Hat Inc. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++ ++#include "asm/assembler.hpp" ++#include "asm/assembler.inline.hpp" ++#include "macroAssembler_aarch64.hpp" ++#include "memory/resourceArea.hpp" ++#include "runtime/stubRoutines.hpp" ++ ++void MacroAssembler::aesecb_decrypt(Register from, Register to, Register key, Register keylen) { ++ Label L_doLast; ++ ++ ld1(v0, T16B, from); // get 16 bytes of input ++ ++ ld1(v5, T16B, post(key, 16)); ++ rev32(v5, T16B, v5); ++ ++ ld1(v1, v2, v3, v4, T16B, post(key, 64)); ++ rev32(v1, T16B, v1); ++ rev32(v2, T16B, v2); ++ rev32(v3, T16B, v3); ++ rev32(v4, T16B, v4); ++ aesd(v0, v1); ++ aesimc(v0, v0); ++ aesd(v0, v2); ++ aesimc(v0, v0); ++ aesd(v0, v3); ++ aesimc(v0, v0); ++ aesd(v0, v4); ++ aesimc(v0, v0); ++ ++ ld1(v1, v2, v3, v4, T16B, post(key, 64)); ++ rev32(v1, T16B, v1); ++ rev32(v2, T16B, v2); ++ rev32(v3, T16B, v3); ++ rev32(v4, T16B, v4); ++ aesd(v0, v1); ++ aesimc(v0, v0); ++ aesd(v0, v2); ++ aesimc(v0, v0); ++ aesd(v0, v3); ++ aesimc(v0, v0); ++ aesd(v0, v4); ++ aesimc(v0, v0); ++ ++ ld1(v1, v2, T16B, post(key, 32)); ++ rev32(v1, T16B, v1); ++ rev32(v2, T16B, v2); ++ ++ cmpw(keylen, 44); ++ br(Assembler::EQ, L_doLast); ++ ++ aesd(v0, v1); ++ aesimc(v0, v0); ++ aesd(v0, v2); ++ aesimc(v0, v0); ++ ++ ld1(v1, v2, T16B, post(key, 32)); ++ rev32(v1, T16B, v1); ++ rev32(v2, T16B, v2); ++ ++ cmpw(keylen, 52); ++ br(Assembler::EQ, L_doLast); ++ ++ aesd(v0, v1); ++ aesimc(v0, v0); ++ aesd(v0, v2); ++ aesimc(v0, v0); ++ ++ ld1(v1, v2, T16B, post(key, 32)); ++ rev32(v1, T16B, v1); ++ rev32(v2, T16B, v2); ++ ++ bind(L_doLast); ++ ++ aesd(v0, v1); ++ aesimc(v0, v0); ++ aesd(v0, v2); ++ ++ eor(v0, T16B, v0, v5); ++ ++ st1(v0, T16B, to); ++ ++ // Preserve the address of the start of the key ++ sub(key, key, keylen, LSL, exact_log2(sizeof (jint))); ++} ++ ++// Load expanded key into v17..v31 ++void MacroAssembler::aesenc_loadkeys(Register key, Register keylen) { ++ Label L_loadkeys_44, L_loadkeys_52; ++ cmpw(keylen, 52); ++ br(Assembler::LO, L_loadkeys_44); ++ br(Assembler::EQ, L_loadkeys_52); ++ ++ ld1(v17, v18, T16B, post(key, 32)); ++ rev32(v17, T16B, v17); ++ rev32(v18, T16B, v18); ++ bind(L_loadkeys_52); ++ ld1(v19, v20, T16B, post(key, 32)); ++ rev32(v19, T16B, v19); ++ rev32(v20, T16B, v20); ++ bind(L_loadkeys_44); ++ ld1(v21, v22, v23, v24, T16B, post(key, 64)); ++ rev32(v21, T16B, v21); ++ rev32(v22, T16B, v22); ++ rev32(v23, T16B, v23); ++ rev32(v24, T16B, v24); ++ ld1(v25, v26, v27, v28, T16B, post(key, 64)); ++ rev32(v25, T16B, v25); ++ rev32(v26, T16B, v26); ++ rev32(v27, T16B, v27); ++ rev32(v28, T16B, v28); ++ ld1(v29, v30, v31, T16B, post(key, 48)); ++ rev32(v29, T16B, v29); ++ rev32(v30, T16B, v30); ++ rev32(v31, T16B, v31); ++ ++ // Preserve the address of the start of the key ++ sub(key, key, keylen, LSL, exact_log2(sizeof (jint))); ++} ++ ++// NeoverseTM N1Software Optimization Guide: ++// Adjacent AESE/AESMC instruction pairs and adjacent AESD/AESIMC ++// instruction pairs will exhibit the performance characteristics ++// described in Section 4.6. ++void MacroAssembler::aes_round(FloatRegister input, FloatRegister subkey) { ++ aese(input, subkey); aesmc(input, input); ++} ++ ++// KernelGenerator ++// ++// The abstract base class of an unrolled function generator. ++// Subclasses override generate(), length(), and next() to generate ++// unrolled and interleaved functions. ++// ++// The core idea is that a subclass defines a method which generates ++// the base case of a function and a method to generate a clone of it, ++// shifted to a different set of registers. KernelGenerator will then ++// generate several interleaved copies of the function, with each one ++// using a different set of registers. ++ ++// The subclass must implement three methods: length(), which is the ++// number of instruction bundles in the intrinsic, generate(int n) ++// which emits the nth instruction bundle in the intrinsic, and next() ++// which takes an instance of the generator and returns a version of it, ++// shifted to a new set of registers. ++ ++class KernelGenerator: public MacroAssembler { ++protected: ++ const int _unrolls; ++public: ++ KernelGenerator(Assembler *as, int unrolls) ++ : MacroAssembler(as->code()), _unrolls(unrolls) { } ++ virtual void generate(int index) = 0; ++ virtual int length() = 0; ++ virtual KernelGenerator *next() = 0; ++ int unrolls() { return _unrolls; } ++ void unroll(); ++}; ++ ++void KernelGenerator::unroll() { ++ ResourceMark rm; ++ KernelGenerator **generators ++ = NEW_RESOURCE_ARRAY(KernelGenerator *, unrolls()); ++ ++ generators[0] = this; ++ for (int i = 1; i < unrolls(); i++) { ++ generators[i] = generators[i-1]->next(); ++ } ++ ++ for (int j = 0; j < length(); j++) { ++ for (int i = 0; i < unrolls(); i++) { ++ generators[i]->generate(j); ++ } ++ } ++} ++ ++// An unrolled and interleaved generator for AES encryption. ++class AESKernelGenerator: public KernelGenerator { ++ Register _from, _to; ++ const Register _keylen; ++ FloatRegister _data; ++ const FloatRegister _subkeys; ++ bool _once; ++ Label _rounds_44, _rounds_52; ++ ++public: ++ AESKernelGenerator(Assembler *as, int unrolls, ++ Register from, Register to, Register keylen, FloatRegister data, ++ FloatRegister subkeys, bool once = true) ++ : KernelGenerator(as, unrolls), ++ _from(from), _to(to), _keylen(keylen), _data(data), ++ _subkeys(subkeys), _once(once) { ++ } ++ ++ virtual void generate(int index) { ++ switch (index) { ++ case 0: ++ if (_from != noreg) { ++ ld1(_data, T16B, _from); // get 16 bytes of input ++ } ++ break; ++ case 1: ++ if (_once) { ++ cmpw(_keylen, 52); ++ br(Assembler::LO, _rounds_44); ++ br(Assembler::EQ, _rounds_52); ++ } ++ break; ++ case 2: aes_round(_data, _subkeys + 0); break; ++ case 3: aes_round(_data, _subkeys + 1); break; ++ case 4: ++ if (_once) bind(_rounds_52); ++ break; ++ case 5: aes_round(_data, _subkeys + 2); break; ++ case 6: aes_round(_data, _subkeys + 3); break; ++ case 7: ++ if (_once) bind(_rounds_44); ++ break; ++ case 8: aes_round(_data, _subkeys + 4); break; ++ case 9: aes_round(_data, _subkeys + 5); break; ++ case 10: aes_round(_data, _subkeys + 6); break; ++ case 11: aes_round(_data, _subkeys + 7); break; ++ case 12: aes_round(_data, _subkeys + 8); break; ++ case 13: aes_round(_data, _subkeys + 9); break; ++ case 14: aes_round(_data, _subkeys + 10); break; ++ case 15: aes_round(_data, _subkeys + 11); break; ++ case 16: aes_round(_data, _subkeys + 12); break; ++ case 17: aese(_data, _subkeys + 13); break; ++ case 18: eor(_data, T16B, _data, _subkeys + 14); break; ++ case 19: ++ if (_to != noreg) { ++ st1(_data, T16B, _to); ++ } ++ break; ++ default: ShouldNotReachHere(); ++ } ++ } ++ ++ virtual KernelGenerator *next() { ++ return new AESKernelGenerator(this, _unrolls, ++ _from, _to, _keylen, ++ _data + 1, _subkeys, /*once*/false); ++ } ++ ++ virtual int length() { return 20; } ++}; ++ ++// Uses expanded key in v17..v31 ++// Returns encrypted values in inputs. ++// If to != noreg, store value at to; likewise from ++// Preserves key, keylen ++// Increments from, to ++// Input data in v0, v1, ... ++// unrolls controls the number of times to unroll the generated function ++void MacroAssembler::aesecb_encrypt(Register from, Register to, Register keylen, ++ FloatRegister data, int unrolls) { ++ AESKernelGenerator(this, unrolls, from, to, keylen, data, v17) .unroll(); ++} ++ ++// ghash_multiply and ghash_reduce are the non-unrolled versions of ++// the GHASH function generators. ++void MacroAssembler::ghash_multiply(FloatRegister result_lo, FloatRegister result_hi, ++ FloatRegister a, FloatRegister b, FloatRegister a1_xor_a0, ++ FloatRegister tmp1, FloatRegister tmp2, FloatRegister tmp3) { ++ // Karatsuba multiplication performs a 128*128 -> 256-bit ++ // multiplication in three 128-bit multiplications and a few ++ // additions. ++ // ++ // (C1:C0) = A1*B1, (D1:D0) = A0*B0, (E1:E0) = (A0+A1)(B0+B1) ++ // (A1:A0)(B1:B0) = C1:(C0+C1+D1+E1):(D1+C0+D0+E0):D0 ++ // ++ // Inputs: ++ // ++ // A0 in a.d[0] (subkey) ++ // A1 in a.d[1] ++ // (A1+A0) in a1_xor_a0.d[0] ++ // ++ // B0 in b.d[0] (state) ++ // B1 in b.d[1] ++ ++ ext(tmp1, T16B, b, b, 0x08); ++ pmull2(result_hi, T1Q, b, a, T2D); // A1*B1 ++ eor(tmp1, T16B, tmp1, b); // (B1+B0) ++ pmull(result_lo, T1Q, b, a, T1D); // A0*B0 ++ pmull(tmp2, T1Q, tmp1, a1_xor_a0, T1D); // (A1+A0)(B1+B0) ++ ++ ext(tmp1, T16B, result_lo, result_hi, 0x08); ++ eor(tmp3, T16B, result_hi, result_lo); // A1*B1+A0*B0 ++ eor(tmp2, T16B, tmp2, tmp1); ++ eor(tmp2, T16B, tmp2, tmp3); ++ ++ // Register pair holds the result of carry-less multiplication ++ ins(result_hi, D, tmp2, 0, 1); ++ ins(result_lo, D, tmp2, 1, 0); ++} ++ ++void MacroAssembler::ghash_reduce(FloatRegister result, FloatRegister lo, FloatRegister hi, ++ FloatRegister p, FloatRegister vzr, FloatRegister t1) { ++ const FloatRegister t0 = result; ++ ++ // The GCM field polynomial f is z^128 + p(z), where p = ++ // z^7+z^2+z+1. ++ // ++ // z^128 === -p(z) (mod (z^128 + p(z))) ++ // ++ // so, given that the product we're reducing is ++ // a == lo + hi * z^128 ++ // substituting, ++ // === lo - hi * p(z) (mod (z^128 + p(z))) ++ // ++ // we reduce by multiplying hi by p(z) and subtracting the result ++ // from (i.e. XORing it with) lo. Because p has no nonzero high ++ // bits we can do this with two 64-bit multiplications, lo*p and ++ // hi*p. ++ ++ pmull2(t0, T1Q, hi, p, T2D); ++ ext(t1, T16B, t0, vzr, 8); ++ eor(hi, T16B, hi, t1); ++ ext(t1, T16B, vzr, t0, 8); ++ eor(lo, T16B, lo, t1); ++ pmull(t0, T1Q, hi, p, T1D); ++ eor(result, T16B, lo, t0); ++} ++ ++class GHASHMultiplyGenerator: public KernelGenerator { ++ FloatRegister _result_lo, _result_hi, _b, ++ _a, _vzr, _a1_xor_a0, _p, ++ _tmp1, _tmp2, _tmp3; ++ ++public: ++ GHASHMultiplyGenerator(Assembler *as, int unrolls, ++ FloatRegister result_lo, FloatRegister result_hi, ++ /* offsetted registers */ ++ FloatRegister b, ++ /* non-offsetted (shared) registers */ ++ FloatRegister a, FloatRegister a1_xor_a0, FloatRegister p, FloatRegister vzr, ++ /* offseted (temp) registers */ ++ FloatRegister tmp1, FloatRegister tmp2, FloatRegister tmp3) ++ : KernelGenerator(as, unrolls), ++ _result_lo(result_lo), _result_hi(result_hi), _b(b), ++ _a(a), _vzr(vzr), _a1_xor_a0(a1_xor_a0), _p(p), ++ _tmp1(tmp1), _tmp2(tmp2), _tmp3(tmp3) { } ++ ++ static const int register_stride = 7; ++ ++ virtual void generate(int index) { ++ // Karatsuba multiplication performs a 128*128 -> 256-bit ++ // multiplication in three 128-bit multiplications and a few ++ // additions. ++ // ++ // (C1:C0) = A1*B1, (D1:D0) = A0*B0, (E1:E0) = (A0+A1)(B0+B1) ++ // (A1:A0)(B1:B0) = C1:(C0+C1+D1+E1):(D1+C0+D0+E0):D0 ++ // ++ // Inputs: ++ // ++ // A0 in a.d[0] (subkey) ++ // A1 in a.d[1] ++ // (A1+A0) in a1_xor_a0.d[0] ++ // ++ // B0 in b.d[0] (state) ++ // B1 in b.d[1] ++ ++ switch (index) { ++ case 0: ext(_tmp1, T16B, _b, _b, 0x08); break; ++ case 1: pmull2(_result_hi, T1Q, _b, _a, T2D); // A1*B1 ++ break; ++ case 2: eor(_tmp1, T16B, _tmp1, _b); // (B1+B0) ++ break; ++ case 3: pmull(_result_lo, T1Q, _b, _a, T1D); // A0*B0 ++ break; ++ case 4: pmull(_tmp2, T1Q, _tmp1, _a1_xor_a0, T1D); // (A1+A0)(B1+B0) ++ break; ++ ++ case 5: ext(_tmp1, T16B, _result_lo, _result_hi, 0x08); break; ++ case 6: eor(_tmp3, T16B, _result_hi, _result_lo); // A1*B1+A0*B0 ++ break; ++ case 7: eor(_tmp2, T16B, _tmp2, _tmp1); break; ++ case 8: eor(_tmp2, T16B, _tmp2, _tmp3); break; ++ ++ // Register pair <_result_hi:_result_lo> holds the _result of carry-less multiplication ++ case 9: ins(_result_hi, D, _tmp2, 0, 1); break; ++ case 10: ins(_result_lo, D, _tmp2, 1, 0); break; ++ default: ShouldNotReachHere(); ++ } ++ } ++ ++ virtual KernelGenerator *next() { ++ GHASHMultiplyGenerator *result ++ = new GHASHMultiplyGenerator(this, _unrolls, _result_lo, _result_hi, ++ _b, _a, _a1_xor_a0, _p, _vzr, ++ _tmp1, _tmp2, _tmp3); ++ result->_result_lo += register_stride; ++ result->_result_hi += register_stride; ++ result->_b += register_stride; ++ result->_tmp1 += register_stride; ++ result->_tmp2 += register_stride; ++ result->_tmp3 += register_stride; ++ return result; ++ } ++ ++ virtual int length() { return 11; } ++}; ++ ++// Reduce the 128-bit product in hi:lo by the GCM field polynomial. ++// The FloatRegister argument called data is optional: if it is a ++// valid register, we interleave LD1 instructions with the ++// reduction. This is to reduce latency next time around the loop. ++class GHASHReduceGenerator: public KernelGenerator { ++ FloatRegister _result, _lo, _hi, _p, _vzr, _data, _t1; ++ int _once; ++public: ++ GHASHReduceGenerator(Assembler *as, int unrolls, ++ /* offsetted registers */ ++ FloatRegister result, FloatRegister lo, FloatRegister hi, ++ /* non-offsetted (shared) registers */ ++ FloatRegister p, FloatRegister vzr, FloatRegister data, ++ /* offseted (temp) registers */ ++ FloatRegister t1) ++ : KernelGenerator(as, unrolls), ++ _result(result), _lo(lo), _hi(hi), ++ _p(p), _vzr(vzr), _data(data), _t1(t1), _once(true) { } ++ ++ static const int register_stride = 7; ++ ++ virtual void generate(int index) { ++ const FloatRegister t0 = _result; ++ ++ switch (index) { ++ // The GCM field polynomial f is z^128 + p(z), where p = ++ // z^7+z^2+z+1. ++ // ++ // z^128 === -p(z) (mod (z^128 + p(z))) ++ // ++ // so, given that the product we're reducing is ++ // a == lo + hi * z^128 ++ // substituting, ++ // === lo - hi * p(z) (mod (z^128 + p(z))) ++ // ++ // we reduce by multiplying hi by p(z) and subtracting the _result ++ // from (i.e. XORing it with) lo. Because p has no nonzero high ++ // bits we can do this with two 64-bit multiplications, lo*p and ++ // hi*p. ++ ++ case 0: pmull2(t0, T1Q, _hi, _p, T2D); break; ++ case 1: ext(_t1, T16B, t0, _vzr, 8); break; ++ case 2: eor(_hi, T16B, _hi, _t1); break; ++ case 3: ext(_t1, T16B, _vzr, t0, 8); break; ++ case 4: eor(_lo, T16B, _lo, _t1); break; ++ case 5: pmull(t0, T1Q, _hi, _p, T1D); break; ++ case 6: eor(_result, T16B, _lo, t0); break; ++ default: ShouldNotReachHere(); ++ } ++ ++ // Sprinkle load instructions into the generated instructions ++ if (_data->is_valid() && _once) { ++ assert(length() >= unrolls(), "not enough room for inteleaved loads"); ++ if (index < unrolls()) { ++ ld1((_data + index*register_stride), T16B, post(r2, 0x10)); ++ } ++ } ++ } ++ ++ virtual KernelGenerator *next() { ++ GHASHReduceGenerator *result ++ = new GHASHReduceGenerator(this, _unrolls, ++ _result, _lo, _hi, _p, _vzr, _data, _t1); ++ result->_result += register_stride; ++ result->_hi += register_stride; ++ result->_lo += register_stride; ++ result->_t1 += register_stride; ++ result->_once = false; ++ return result; ++ } ++ ++ int length() { return 7; } ++}; ++ ++// Perform a GHASH multiply/reduce on a single FloatRegister. ++void MacroAssembler::ghash_modmul(FloatRegister result, ++ FloatRegister result_lo, FloatRegister result_hi, FloatRegister b, ++ FloatRegister a, FloatRegister vzr, FloatRegister a1_xor_a0, FloatRegister p, ++ FloatRegister t1, FloatRegister t2, FloatRegister t3) { ++ ghash_multiply(result_lo, result_hi, a, b, a1_xor_a0, t1, t2, t3); ++ ghash_reduce(result, result_lo, result_hi, p, vzr, t1); ++} ++ ++// Interleaved GHASH processing. ++// ++// Clobbers all vector registers. ++// ++void MacroAssembler::ghash_processBlocks_wide(address field_polynomial, Register state, ++ Register subkeyH, ++ Register data, Register blocks, int unrolls) { ++ int register_stride = 7; ++ ++ // Bafflingly, GCM uses little-endian for the byte order, but ++ // big-endian for the bit order. For example, the polynomial 1 is ++ // represented as the 16-byte string 80 00 00 00 | 12 bytes of 00. ++ // ++ // So, we must either reverse the bytes in each word and do ++ // everything big-endian or reverse the bits in each byte and do ++ // it little-endian. On AArch64 it's more idiomatic to reverse ++ // the bits in each byte (we have an instruction, RBIT, to do ++ // that) and keep the data in little-endian bit order throught the ++ // calculation, bit-reversing the inputs and outputs. ++ ++ assert(unrolls * register_stride < 32, "out of registers"); ++ ++ FloatRegister a1_xor_a0 = v28; ++ FloatRegister Hprime = v29; ++ FloatRegister vzr = v30; ++ FloatRegister p = v31; ++ eor(vzr, T16B, vzr, vzr); // zero register ++ ++ ldrq(p, field_polynomial); // The field polynomial ++ ++ ldrq(v0, Address(state)); ++ ldrq(Hprime, Address(subkeyH)); ++ ++ rev64(v0, T16B, v0); // Bit-reverse words in state and subkeyH ++ rbit(v0, T16B, v0); ++ rev64(Hprime, T16B, Hprime); ++ rbit(Hprime, T16B, Hprime); ++ ++ // Powers of H -> Hprime ++ ++ Label already_calculated, done; ++ { ++ // The first time around we'll have to calculate H**2, H**3, etc. ++ // Look at the largest power of H in the subkeyH array to see if ++ // it's already been calculated. ++ ldp(rscratch1, rscratch2, Address(subkeyH, 16 * (unrolls - 1))); ++ orr(rscratch1, rscratch1, rscratch2); ++ cbnz(rscratch1, already_calculated); ++ ++ orr(v6, T16B, Hprime, Hprime); // Start with H in v6 and Hprime ++ for (int i = 1; i < unrolls; i++) { ++ ext(a1_xor_a0, T16B, Hprime, Hprime, 0x08); // long-swap subkeyH into a1_xor_a0 ++ eor(a1_xor_a0, T16B, a1_xor_a0, Hprime); // xor subkeyH into subkeyL (Karatsuba: (A1+A0)) ++ ghash_modmul(/*result*/v6, /*result_lo*/v5, /*result_hi*/v4, /*b*/v6, ++ Hprime, vzr, a1_xor_a0, p, ++ /*temps*/v1, v3, v2); ++ rev64(v1, T16B, v6); ++ rbit(v1, T16B, v1); ++ strq(v1, Address(subkeyH, 16 * i)); ++ } ++ b(done); ++ } ++ { ++ bind(already_calculated); ++ ++ // Load the largest power of H we need into v6. ++ ldrq(v6, Address(subkeyH, 16 * (unrolls - 1))); ++ rev64(v6, T16B, v6); ++ rbit(v6, T16B, v6); ++ } ++ bind(done); ++ ++ orr(Hprime, T16B, v6, v6); // Move H ** unrolls into Hprime ++ ++ // Hprime contains (H ** 1, H ** 2, ... H ** unrolls) ++ // v0 contains the initial state. Clear the others. ++ for (int i = 1; i < unrolls; i++) { ++ int ofs = register_stride * i; ++ eor(ofs+v0, T16B, ofs+v0, ofs+v0); // zero each state register ++ } ++ ++ ext(a1_xor_a0, T16B, Hprime, Hprime, 0x08); // long-swap subkeyH into a1_xor_a0 ++ eor(a1_xor_a0, T16B, a1_xor_a0, Hprime); // xor subkeyH into subkeyL (Karatsuba: (A1+A0)) ++ ++ // Load #unrolls blocks of data ++ for (int ofs = 0; ofs < unrolls * register_stride; ofs += register_stride) { ++ ld1(v2+ofs, T16B, post(data, 0x10)); ++ } ++ ++ // Register assignments, replicated across 4 clones, v0 ... v23 ++ // ++ // v0: input / output: current state, result of multiply/reduce ++ // v1: temp ++ // v2: input: one block of data (the ciphertext) ++ // also used as a temp once the data has been consumed ++ // v3: temp ++ // v4: output: high part of product ++ // v5: output: low part ... ++ // v6: unused ++ // ++ // Not replicated: ++ // ++ // v28: High part of H xor low part of H' ++ // v29: H' (hash subkey) ++ // v30: zero ++ // v31: Reduction polynomial of the Galois field ++ ++ // Inner loop. ++ // Do the whole load/add/multiply/reduce over all our data except ++ // the last few rows. ++ { ++ Label L_ghash_loop; ++ bind(L_ghash_loop); ++ ++ // Prefetching doesn't help here. In fact, on Neoverse N1 it's worse. ++ // prfm(Address(data, 128), PLDL1KEEP); ++ ++ // Xor data into current state ++ for (int ofs = 0; ofs < unrolls * register_stride; ofs += register_stride) { ++ rbit((v2+ofs), T16B, (v2+ofs)); ++ eor((v2+ofs), T16B, v0+ofs, (v2+ofs)); // bit-swapped data ^ bit-swapped state ++ } ++ ++ // Generate fully-unrolled multiply-reduce in two stages. ++ ++ (new GHASHMultiplyGenerator(this, unrolls, ++ /*result_lo*/v5, /*result_hi*/v4, /*data*/v2, ++ Hprime, a1_xor_a0, p, vzr, ++ /*temps*/v1, v3, /* reuse b*/v2))->unroll(); ++ ++ // NB: GHASHReduceGenerator also loads the next #unrolls blocks of ++ // data into v0, v0+ofs, the current state. ++ (new GHASHReduceGenerator (this, unrolls, ++ /*result*/v0, /*lo*/v5, /*hi*/v4, p, vzr, ++ /*data*/v2, /*temp*/v3))->unroll(); ++ ++ sub(blocks, blocks, unrolls); ++ cmp(blocks, (unsigned char)(unrolls * 2)); ++ br(GE, L_ghash_loop); ++ } ++ ++ // Merge the #unrolls states. Note that the data for the next ++ // iteration has already been loaded into v4, v4+ofs, etc... ++ ++ // First, we multiply/reduce each clone by the appropriate power of H. ++ for (int i = 0; i < unrolls; i++) { ++ int ofs = register_stride * i; ++ ldrq(Hprime, Address(subkeyH, 16 * (unrolls - i - 1))); ++ ++ rbit(v2+ofs, T16B, v2+ofs); ++ eor(v2+ofs, T16B, ofs+v0, v2+ofs); // bit-swapped data ^ bit-swapped state ++ ++ rev64(Hprime, T16B, Hprime); ++ rbit(Hprime, T16B, Hprime); ++ ext(a1_xor_a0, T16B, Hprime, Hprime, 0x08); // long-swap subkeyH into a1_xor_a0 ++ eor(a1_xor_a0, T16B, a1_xor_a0, Hprime); // xor subkeyH into subkeyL (Karatsuba: (A1+A0)) ++ ghash_modmul(/*result*/v0+ofs, /*result_lo*/v5+ofs, /*result_hi*/v4+ofs, /*b*/v2+ofs, ++ Hprime, vzr, a1_xor_a0, p, ++ /*temps*/v1+ofs, v3+ofs, /* reuse b*/v2+ofs); ++ } ++ ++ // Then we sum the results. ++ for (int i = 0; i < unrolls - 1; i++) { ++ int ofs = register_stride * i; ++ eor(v0, T16B, v0, v0 + register_stride + ofs); ++ } ++ ++ sub(blocks, blocks, (unsigned char)unrolls); ++ ++ // And finally bit-reverse the state back to big endian. ++ rev64(v0, T16B, v0); ++ rbit(v0, T16B, v0); ++ st1(v0, T16B, state); ++} +\ No newline at end of file +diff --git a/hotspot/src/cpu/aarch64/vm/stubGenerator_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/stubGenerator_aarch64.cpp +index 2e2e8ae78..c024dec55 100644 +--- a/hotspot/src/cpu/aarch64/vm/stubGenerator_aarch64.cpp ++++ b/hotspot/src/cpu/aarch64/vm/stubGenerator_aarch64.cpp +@@ -2804,6 +2804,266 @@ class StubGenerator: public StubCodeGenerator { + return start; + } + ++ // CTR AES crypt. ++ // Arguments: ++ // ++ // Inputs: ++ // c_rarg0 - source byte array address ++ // c_rarg1 - destination byte array address ++ // c_rarg2 - K (key) in little endian int array ++ // c_rarg3 - counter vector byte array address ++ // c_rarg4 - input length ++ // c_rarg5 - saved encryptedCounter start ++ // c_rarg6 - saved used length ++ // ++ // Output: ++ // r0 - input length ++ // ++ address generate_counterMode_AESCrypt() { ++ const Register in = c_rarg0; ++ const Register out = c_rarg1; ++ const Register key = c_rarg2; ++ const Register counter = c_rarg3; ++ const Register saved_len = c_rarg4, len = r10; ++ const Register saved_encrypted_ctr = c_rarg5; ++ const Register used_ptr = c_rarg6, used = r12; ++ ++ const Register offset = r7; ++ const Register keylen = r11; ++ ++ const unsigned char block_size = 16; ++ const int bulk_width = 4; ++ // NB: bulk_width can be 4 or 8. 8 gives slightly faster ++ // performance with larger data sizes, but it also means that the ++ // fast path isn't used until you have at least 8 blocks, and up ++ // to 127 bytes of data will be executed on the slow path. For ++ // that reason, and also so as not to blow away too much icache, 4 ++ // blocks seems like a sensible compromise. ++ ++ // Algorithm: ++ // ++ // if (len == 0) { ++ // goto DONE; ++ // } ++ // int result = len; ++ // do { ++ // if (used >= blockSize) { ++ // if (len >= bulk_width * blockSize) { ++ // CTR_large_block(); ++ // if (len == 0) ++ // goto DONE; ++ // } ++ // for (;;) { ++ // 16ByteVector v0 = counter; ++ // embeddedCipher.encryptBlock(v0, 0, encryptedCounter, 0); ++ // used = 0; ++ // if (len < blockSize) ++ // break; /* goto NEXT */ ++ // 16ByteVector v1 = load16Bytes(in, offset); ++ // v1 = v1 ^ encryptedCounter; ++ // store16Bytes(out, offset); ++ // used = blockSize; ++ // offset += blockSize; ++ // len -= blockSize; ++ // if (len == 0) ++ // goto DONE; ++ // } ++ // } ++ // NEXT: ++ // out[outOff++] = (byte)(in[inOff++] ^ encryptedCounter[used++]); ++ // len--; ++ // } while (len != 0); ++ // DONE: ++ // return result; ++ // ++ // CTR_large_block() ++ // Wide bulk encryption of whole blocks. ++ ++ __ align(CodeEntryAlignment); ++ StubCodeMark mark(this, "StubRoutines", "counterMode_AESCrypt"); ++ const address start = __ pc(); ++ __ enter(); ++ ++ Label DONE, CTR_large_block, large_block_return; ++ __ ldrw(used, Address(used_ptr)); ++ __ cbzw(saved_len, DONE); ++ ++ __ mov(len, saved_len); ++ __ mov(offset, 0); ++ ++ // Compute #rounds for AES based on the length of the key array ++ __ ldrw(keylen, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT))); ++ ++ __ aesenc_loadkeys(key, keylen); ++ ++ { ++ Label L_CTR_loop, NEXT; ++ ++ __ bind(L_CTR_loop); ++ ++ __ cmp(used, block_size); ++ __ br(__ LO, NEXT); ++ ++ // Maybe we have a lot of data ++ __ subsw(rscratch1, len, bulk_width * block_size); ++ __ br(__ HS, CTR_large_block); ++ __ BIND(large_block_return); ++ __ cbzw(len, DONE); ++ ++ // Setup the counter ++ __ movi(v4, __ T4S, 0); ++ __ movi(v5, __ T4S, 1); ++ __ ins(v4, __ S, v5, 3, 3); // v4 contains { 0, 0, 0, 1 } ++ ++ __ ld1(v0, __ T16B, counter); // Load the counter into v0 ++ __ rev32(v16, __ T16B, v0); ++ __ addv(v16, __ T4S, v16, v4); ++ __ rev32(v16, __ T16B, v16); ++ __ st1(v16, __ T16B, counter); // Save the incremented counter back ++ ++ { ++ // We have fewer than bulk_width blocks of data left. Encrypt ++ // them one by one until there is less than a full block ++ // remaining, being careful to save both the encrypted counter ++ // and the counter. ++ ++ Label inner_loop; ++ __ bind(inner_loop); ++ // Counter to encrypt is in v0 ++ __ aesecb_encrypt(noreg, noreg, keylen); ++ __ st1(v0, __ T16B, saved_encrypted_ctr); ++ ++ // Do we have a remaining full block? ++ ++ __ mov(used, 0); ++ __ cmp(len, block_size); ++ __ br(__ LO, NEXT); ++ ++ // Yes, we have a full block ++ __ ldrq(v1, Address(in, offset)); ++ __ eor(v1, __ T16B, v1, v0); ++ __ strq(v1, Address(out, offset)); ++ __ mov(used, block_size); ++ __ add(offset, offset, block_size); ++ ++ __ subw(len, len, block_size); ++ __ cbzw(len, DONE); ++ ++ // Increment the counter, store it back ++ __ orr(v0, __ T16B, v16, v16); ++ __ rev32(v16, __ T16B, v16); ++ __ addv(v16, __ T4S, v16, v4); ++ __ rev32(v16, __ T16B, v16); ++ __ st1(v16, __ T16B, counter); // Save the incremented counter back ++ ++ __ b(inner_loop); ++ } ++ ++ __ BIND(NEXT); ++ ++ // Encrypt a single byte, and loop. ++ // We expect this to be a rare event. ++ __ ldrb(rscratch1, Address(in, offset)); ++ __ ldrb(rscratch2, Address(saved_encrypted_ctr, used)); ++ __ eor(rscratch1, rscratch1, rscratch2); ++ __ strb(rscratch1, Address(out, offset)); ++ __ add(offset, offset, 1); ++ __ add(used, used, 1); ++ __ subw(len, len,1); ++ __ cbnzw(len, L_CTR_loop); ++ } ++ ++ __ bind(DONE); ++ __ strw(used, Address(used_ptr)); ++ __ mov(r0, saved_len); ++ ++ __ leave(); // required for proper stackwalking of RuntimeStub frame ++ __ ret(lr); ++ ++ // Bulk encryption ++ ++ __ BIND (CTR_large_block); ++ assert(bulk_width == 4 || bulk_width == 8, "must be"); ++ ++ if (bulk_width == 8) { ++ __ sub(sp, sp, 4 * 16); ++ __ st1(v12, v13, v14, v15, __ T16B, Address(sp)); ++ } ++ __ sub(sp, sp, 4 * 16); ++ __ st1(v8, v9, v10, v11, __ T16B, Address(sp)); ++ RegSet saved_regs = (RegSet::of(in, out, offset) ++ + RegSet::of(saved_encrypted_ctr, used_ptr, len)); ++ __ push(saved_regs, sp); ++ __ andr(len, len, -16 * bulk_width); // 8/4 encryptions, 16 bytes per encryption ++ __ add(in, in, offset); ++ __ add(out, out, offset); ++ ++ // Keys should already be loaded into the correct registers ++ ++ __ ld1(v0, __ T16B, counter); // v0 contains the first counter ++ __ rev32(v16, __ T16B, v0); // v16 contains byte-reversed counter ++ ++ // AES/CTR loop ++ { ++ Label L_CTR_loop; ++ __ BIND(L_CTR_loop); ++ ++ // Setup the counters ++ __ movi(v8, __ T4S, 0); ++ __ movi(v9, __ T4S, 1); ++ __ ins(v8, __ S, v9, 3, 3); // v8 contains { 0, 0, 0, 1 } ++ ++ for (FloatRegister f = v0; f < v0 + bulk_width; f++) { ++ __ rev32(f, __ T16B, v16); ++ __ addv(v16, __ T4S, v16, v8); ++ } ++ ++ __ ld1(v8, v9, v10, v11, __ T16B, __ post(in, 4 * 16)); ++ ++ // Encrypt the counters ++ __ aesecb_encrypt(noreg, noreg, keylen, v0, bulk_width); ++ ++ if (bulk_width == 8) { ++ __ ld1(v12, v13, v14, v15, __ T16B, __ post(in, 4 * 16)); ++ } ++ ++ // XOR the encrypted counters with the inputs ++ for (int i = 0; i < bulk_width; i++) { ++ __ eor(v0 + i, __ T16B, v0 + i, v8 + i); ++ } ++ ++ // Write the encrypted data ++ __ st1(v0, v1, v2, v3, __ T16B, __ post(out, 4 * 16)); ++ if (bulk_width == 8) { ++ __ st1(v4, v5, v6, v7, __ T16B, __ post(out, 4 * 16)); ++ } ++ ++ __ subw(len, len, 16 * bulk_width); ++ __ cbnzw(len, L_CTR_loop); ++ } ++ ++ // Save the counter back where it goes ++ __ rev32(v16, __ T16B, v16); ++ __ st1(v16, __ T16B, counter); ++ ++ __ pop(saved_regs, sp); ++ ++ __ ld1(v8, v9, v10, v11, __ T16B, __ post(sp, 4 * 16)); ++ if (bulk_width == 8) { ++ __ ld1(v12, v13, v14, v15, __ T16B, __ post(sp, 4 * 16)); ++ } ++ ++ __ andr(rscratch1, len, -16 * bulk_width); ++ __ sub(len, len, rscratch1); ++ __ add(offset, offset, rscratch1); ++ __ mov(used, 16); ++ __ strw(used, Address(used_ptr)); ++ __ b(large_block_return); ++ ++ return start; ++ } ++ ++ + // Arguments: + // + // Inputs: +@@ -3677,6 +3937,56 @@ class StubGenerator: public StubCodeGenerator { + return start; + } + ++ address generate_ghash_processBlocks_wide() { ++ address small = generate_ghash_processBlocks(); ++ ++ StubCodeMark mark(this, "StubRoutines", "ghash_processBlocks_wide"); ++ __ align(wordSize * 2); ++ address p = __ pc(); ++ __ emit_int64(0x87); // The low-order bits of the field ++ // polynomial (i.e. p = z^7+z^2+z+1) ++ // repeated in the low and high parts of a ++ // 128-bit vector ++ __ emit_int64(0x87); ++ ++ __ align(CodeEntryAlignment); ++ address start = __ pc(); ++ ++ Register state = c_rarg0; ++ Register subkeyH = c_rarg1; ++ Register data = c_rarg2; ++ Register blocks = c_rarg3; ++ ++ const int unroll = 4; ++ ++ __ cmp(blocks, (unsigned char)(unroll * 2)); ++ __ br(__ LT, small); ++ ++ if (unroll > 1) { ++ // Save state before entering routine ++ __ sub(sp, sp, 4 * 16); ++ __ st1(v12, v13, v14, v15, __ T16B, Address(sp)); ++ __ sub(sp, sp, 4 * 16); ++ __ st1(v8, v9, v10, v11, __ T16B, Address(sp)); ++ } ++ ++ __ ghash_processBlocks_wide(p, state, subkeyH, data, blocks, unroll); ++ ++ if (unroll > 1) { ++ // And restore state ++ __ ld1(v8, v9, v10, v11, __ T16B, __ post(sp, 4 * 16)); ++ __ ld1(v12, v13, v14, v15, __ T16B, __ post(sp, 4 * 16)); ++ } ++ ++ __ cmp(blocks, 0u); ++ __ br(__ GT, small); ++ ++ __ ret(lr); ++ ++ return start; ++ } ++ ++ + // Continuation point for throwing of implicit exceptions that are + // not handled in the current activation. Fabricates an exception + // oop and initiates normal exception dispatching in this +@@ -4687,6 +4997,15 @@ class StubGenerator: public StubCodeGenerator { + StubRoutines::_montgomerySquare = g.generate_multiply(); + } + ++ // generate GHASH intrinsics code ++ if (UseGHASHIntrinsics) { ++ if (UseAESCTRIntrinsics) { ++ StubRoutines::_ghash_processBlocks = generate_ghash_processBlocks_wide(); ++ } else { ++ StubRoutines::_ghash_processBlocks = generate_ghash_processBlocks(); ++ } ++ } ++ + if (UseAESIntrinsics) { + StubRoutines::_aescrypt_encryptBlock = generate_aescrypt_encryptBlock(); + StubRoutines::_aescrypt_decryptBlock = generate_aescrypt_decryptBlock(); +@@ -4694,9 +5013,8 @@ class StubGenerator: public StubCodeGenerator { + StubRoutines::_cipherBlockChaining_decryptAESCrypt = generate_cipherBlockChaining_decryptAESCrypt(); + } + +- // generate GHASH intrinsics code +- if (UseGHASHIntrinsics) { +- StubRoutines::_ghash_processBlocks = generate_ghash_processBlocks(); ++ if (UseAESCTRIntrinsics) { ++ StubRoutines::_counterMode_AESCrypt = generate_counterMode_AESCrypt(); + } + + if (UseSHA1Intrinsics) { +diff --git a/hotspot/src/cpu/aarch64/vm/stubRoutines_aarch64.hpp b/hotspot/src/cpu/aarch64/vm/stubRoutines_aarch64.hpp +index d1c312ab3..05619ce7f 100644 +--- a/hotspot/src/cpu/aarch64/vm/stubRoutines_aarch64.hpp ++++ b/hotspot/src/cpu/aarch64/vm/stubRoutines_aarch64.hpp +@@ -37,7 +37,7 @@ static bool returns_to_call_stub(address return_pc) { + + enum platform_dependent_constants { + code_size1 = 19000, // simply increase if too small (assembler will crash if too small) +- code_size2 = 22000 // simply increase if too small (assembler will crash if too small) ++ code_size2 = 32000 // simply increase if too small (assembler will crash if too small) + }; + + class aarch64 { +diff --git a/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.cpp +index 9808337a0..de636fb83 100644 +--- a/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.cpp ++++ b/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.cpp +@@ -233,12 +233,21 @@ void VM_Version::get_processor_features() { + warning("UseAESIntrinsics enabled, but UseAES not, enabling"); + UseAES = true; + } ++ if (FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { ++ FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); ++ } + } else { + if (UseAES) { +- warning("UseAES specified, but not supported on this CPU"); ++ warning("AES instructions are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseAES, false); + } + if (UseAESIntrinsics) { +- warning("UseAESIntrinsics specified, but not supported on this CPU"); ++ warning("AES intrinsics are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseAESIntrinsics, false); ++ } ++ if (UseAESCTRIntrinsics) { ++ warning("AES/CTR intrinsics are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); + } + } + +diff --git a/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp b/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp +index b5ce1cfa9..fea8b1f87 100644 +--- a/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp ++++ b/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp +@@ -194,6 +194,11 @@ void VM_Version::initialize() { + FLAG_SET_DEFAULT(UseAESIntrinsics, false); + } + ++ if (UseAESCTRIntrinsics) { ++ warning("AES/CTR intrinsics are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); ++ } ++ + if (UseGHASHIntrinsics) { + warning("GHASH intrinsics are not available on this CPU"); + FLAG_SET_DEFAULT(UseGHASHIntrinsics, false); +diff --git a/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp b/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp +index bd893e138..08d7a7311 100644 +--- a/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp ++++ b/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp +@@ -319,6 +319,11 @@ void VM_Version::initialize() { + } + } + ++ if (UseAESCTRIntrinsics) { ++ warning("AES/CTR intrinsics are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); ++ } ++ + // GHASH/GCM intrinsics + if (has_vis3() && (UseVIS > 2)) { + if (FLAG_IS_DEFAULT(UseGHASHIntrinsics)) { +diff --git a/hotspot/src/cpu/x86/vm/assembler_x86.cpp b/hotspot/src/cpu/x86/vm/assembler_x86.cpp +index 1759ecdfd..ddc1acfd8 100644 +--- a/hotspot/src/cpu/x86/vm/assembler_x86.cpp ++++ b/hotspot/src/cpu/x86/vm/assembler_x86.cpp +@@ -2373,20 +2373,52 @@ void Assembler::pcmpestri(XMMRegister dst, XMMRegister src, int imm8) { + + void Assembler::pextrd(Register dst, XMMRegister src, int imm8) { + assert(VM_Version::supports_sse4_1(), ""); +- int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, false); ++ int encode = simd_prefix_and_encode(src, xnoreg, as_XMMRegister(dst->encoding()), VEX_SIMD_66, VEX_OPCODE_0F_3A, false); + emit_int8(0x16); + emit_int8((unsigned char)(0xC0 | encode)); + emit_int8(imm8); + } + ++void Assembler::pextrd(Address dst, XMMRegister src, int imm8) { ++ assert(VM_Version::supports_sse4_1(), ""); ++ simd_prefix(src, xnoreg, dst, VEX_SIMD_66, VEX_OPCODE_0F_3A, false); ++ emit_int8(0x16); ++ emit_operand(src, dst); ++ emit_int8(imm8); ++} ++ + void Assembler::pextrq(Register dst, XMMRegister src, int imm8) { + assert(VM_Version::supports_sse4_1(), ""); +- int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, true); ++ int encode = simd_prefix_and_encode(src, xnoreg, as_XMMRegister(dst->encoding()), VEX_SIMD_66, VEX_OPCODE_0F_3A, true); + emit_int8(0x16); + emit_int8((unsigned char)(0xC0 | encode)); + emit_int8(imm8); + } + ++void Assembler::pextrq(Address dst, XMMRegister src, int imm8) { ++ assert(VM_Version::supports_sse4_1(), ""); ++ simd_prefix(src, xnoreg, dst, VEX_SIMD_66, VEX_OPCODE_0F_3A, true); ++ emit_int8(0x16); ++ emit_operand(src, dst); ++ emit_int8(imm8); ++} ++ ++void Assembler::pextrw(Address dst, XMMRegister src, int imm8) { ++ assert(VM_Version::supports_sse4_1(), ""); ++ simd_prefix(src, xnoreg, dst, VEX_SIMD_66, VEX_OPCODE_0F_3A); ++ emit_int8((unsigned char)0x15); ++ emit_operand(src, dst); ++ emit_int8(imm8); ++} ++ ++void Assembler::pextrb(Address dst, XMMRegister src, int imm8) { ++ assert(VM_Version::supports_sse4_1(), ""); ++ simd_prefix(src, xnoreg, dst, VEX_SIMD_66, VEX_OPCODE_0F_3A); ++ emit_int8(0x14); ++ emit_operand(src, dst); ++ emit_int8(imm8); ++} ++ + void Assembler::pinsrd(XMMRegister dst, Register src, int imm8) { + assert(VM_Version::supports_sse4_1(), ""); + int encode = simd_prefix_and_encode(dst, dst, as_XMMRegister(src->encoding()), VEX_SIMD_66, VEX_OPCODE_0F_3A, false); +@@ -2395,6 +2427,14 @@ void Assembler::pinsrd(XMMRegister dst, Register src, int imm8) { + emit_int8(imm8); + } + ++void Assembler::pinsrd(XMMRegister dst, Address src, int imm8) { ++ assert(VM_Version::supports_sse4_1(), ""); ++ simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, false); ++ emit_int8(0x22); ++ emit_operand(dst,src); ++ emit_int8(imm8); ++} ++ + void Assembler::pinsrq(XMMRegister dst, Register src, int imm8) { + assert(VM_Version::supports_sse4_1(), ""); + int encode = simd_prefix_and_encode(dst, dst, as_XMMRegister(src->encoding()), VEX_SIMD_66, VEX_OPCODE_0F_3A, true); +@@ -2403,6 +2443,30 @@ void Assembler::pinsrq(XMMRegister dst, Register src, int imm8) { + emit_int8(imm8); + } + ++void Assembler::pinsrq(XMMRegister dst, Address src, int imm8) { ++ assert(VM_Version::supports_sse4_1(), ""); ++ simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, true); ++ emit_int8(0x22); ++ emit_operand(dst, src); ++ emit_int8(imm8); ++} ++ ++void Assembler::pinsrw(XMMRegister dst, Address src, int imm8) { ++ assert(VM_Version::supports_sse2(), ""); ++ simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F); ++ emit_int8((unsigned char)0xC4); ++ emit_operand(dst, src); ++ emit_int8(imm8); ++} ++ ++void Assembler::pinsrb(XMMRegister dst, Address src, int imm8) { ++ assert(VM_Version::supports_sse4_1(), ""); ++ simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_3A); ++ emit_int8(0x20); ++ emit_operand(dst, src); ++ emit_int8(imm8); ++} ++ + void Assembler::pmovzxbw(XMMRegister dst, Address src) { + assert(VM_Version::supports_sse4_1(), ""); + InstructionMark im(this); +@@ -3075,6 +3139,12 @@ void Assembler::xorl(Register dst, Register src) { + emit_arith(0x33, 0xC0, dst, src); + } + ++void Assembler::xorb(Register dst, Address src) { ++ InstructionMark im(this); ++ prefix(src, dst); ++ emit_int8(0x32); ++ emit_operand(dst, src); ++} + + // AVX 3-operands scalar float-point arithmetic instructions + +diff --git a/hotspot/src/cpu/x86/vm/assembler_x86.hpp b/hotspot/src/cpu/x86/vm/assembler_x86.hpp +index 5ea01311e..c2e70bc2a 100644 +--- a/hotspot/src/cpu/x86/vm/assembler_x86.hpp ++++ b/hotspot/src/cpu/x86/vm/assembler_x86.hpp +@@ -1479,10 +1479,20 @@ private: + // SSE 4.1 extract + void pextrd(Register dst, XMMRegister src, int imm8); + void pextrq(Register dst, XMMRegister src, int imm8); ++ void pextrd(Address dst, XMMRegister src, int imm8); ++ void pextrq(Address dst, XMMRegister src, int imm8); ++ void pextrb(Address dst, XMMRegister src, int imm8); ++ // SSE 2 extract ++ void pextrw(Address dst, XMMRegister src, int imm8); + + // SSE 4.1 insert + void pinsrd(XMMRegister dst, Register src, int imm8); + void pinsrq(XMMRegister dst, Register src, int imm8); ++ void pinsrd(XMMRegister dst, Address src, int imm8); ++ void pinsrq(XMMRegister dst, Address src, int imm8); ++ void pinsrb(XMMRegister dst, Address src, int imm8); ++ // SSE 2 insert ++ void pinsrw(XMMRegister dst, Address src, int imm8); + + // SSE4.1 packed move + void pmovzxbw(XMMRegister dst, XMMRegister src); +@@ -1687,6 +1697,8 @@ private: + void xorl(Register dst, Address src); + void xorl(Register dst, Register src); + ++ void xorb(Register dst, Address src); ++ + void xorq(Register dst, Address src); + void xorq(Register dst, Register src); + +diff --git a/hotspot/src/cpu/x86/vm/stubGenerator_x86_32.cpp b/hotspot/src/cpu/x86/vm/stubGenerator_x86_32.cpp +index 2e5599807..f555f3326 100644 +--- a/hotspot/src/cpu/x86/vm/stubGenerator_x86_32.cpp ++++ b/hotspot/src/cpu/x86/vm/stubGenerator_x86_32.cpp +@@ -2153,6 +2153,17 @@ class StubGenerator: public StubCodeGenerator { + return start; + } + ++ address generate_counter_shuffle_mask() { ++ __ align(16); ++ StubCodeMark mark(this, "StubRoutines", "counter_shuffle_mask"); ++ address start = __ pc(); ++ __ emit_data(0x0c0d0e0f, relocInfo::none, 0); ++ __ emit_data(0x08090a0b, relocInfo::none, 0); ++ __ emit_data(0x04050607, relocInfo::none, 0); ++ __ emit_data(0x00010203, relocInfo::none, 0); ++ return start; ++ } ++ + // Utility routine for loading a 128-bit key word in little endian format + // can optionally specify that the shuffle mask is already in an xmmregister + void load_key(XMMRegister xmmdst, Register key, int offset, XMMRegister xmm_shuf_mask=NULL) { +@@ -2178,6 +2189,31 @@ class StubGenerator: public StubCodeGenerator { + __ aesdec(xmmdst, xmmtmp); + } + ++ // Utility routine for increase 128bit counter (iv in CTR mode) ++ // XMM_128bit, D3, D2, D1, D0 ++ void inc_counter(Register reg, XMMRegister xmmdst, int inc_delta, Label& next_block) { ++ __ pextrd(reg, xmmdst, 0x0); ++ __ addl(reg, inc_delta); ++ __ pinsrd(xmmdst, reg, 0x0); ++ __ jcc(Assembler::carryClear, next_block); // jump if no carry ++ ++ __ pextrd(reg, xmmdst, 0x01); // Carry-> D1 ++ __ addl(reg, 0x01); ++ __ pinsrd(xmmdst, reg, 0x01); ++ __ jcc(Assembler::carryClear, next_block); // jump if no carry ++ ++ __ pextrd(reg, xmmdst, 0x02); // Carry-> D2 ++ __ addl(reg, 0x01); ++ __ pinsrd(xmmdst, reg, 0x02); ++ __ jcc(Assembler::carryClear, next_block); // jump if no carry ++ ++ __ pextrd(reg, xmmdst, 0x03); // Carry -> D3 ++ __ addl(reg, 0x01); ++ __ pinsrd(xmmdst, reg, 0x03); ++ ++ __ BIND(next_block); // next instruction ++ } ++ + + // Arguments: + // +@@ -2719,6 +2755,309 @@ class StubGenerator: public StubCodeGenerator { + return start; + } + ++ ++ // CTR AES crypt. ++ // In 32-bit stub, parallelize 4 blocks at a time ++ // Arguments: ++ // ++ // Inputs: ++ // c_rarg0 - source byte array address ++ // c_rarg1 - destination byte array address ++ // c_rarg2 - K (key) in little endian int array ++ // c_rarg3 - counter vector byte array address ++ // c_rarg4 - input length ++ // ++ // Output: ++ // rax - input length ++ // ++ address generate_counterMode_AESCrypt_Parallel() { ++ assert(UseAES, "need AES instructions and misaligned SSE support"); ++ __ align(CodeEntryAlignment); ++ StubCodeMark mark(this, "StubRoutines", "counterMode_AESCrypt"); ++ address start = __ pc(); ++ const Register from = rsi; // source array address ++ const Register to = rdx; // destination array address ++ const Register key = rcx; // key array address ++ const Register counter = rdi; // counter byte array initialized from initvector array address ++ ++ // and left with the results of the last encryption block ++ const Register len_reg = rbx; ++ const Register pos = rax; ++ ++ __ enter(); // required for proper stackwalking of RuntimeStub frame ++ handleSOERegisters(true /*saving*/); // save rbx, rsi, rdi ++ ++ // load registers from incoming parameters ++ const Address from_param(rbp, 8+0); ++ const Address to_param (rbp, 8+4); ++ const Address key_param (rbp, 8+8); ++ const Address rvec_param (rbp, 8+12); ++ const Address len_param (rbp, 8+16); ++ const Address saved_counter_param(rbp, 8 + 20); ++ const Address used_addr_param(rbp, 8 + 24); ++ ++ __ movptr(from , from_param); ++ __ movptr(to , to_param); ++ //__ movptr(key, key_param); ++ //__ movptr(counter, rvec_param); ++ __ movptr(len_reg , len_param); ++ //__ movptr(pos, 0); ++ ++ // Use the partially used encrpyted counter from last invocation ++ Label L_exit_preLoop, L_preLoop_start; ++ ++ // Use the registers 'counter' and 'key' here in this preloop ++ // to hold of last 2 params 'used' and 'saved_encCounter_start' ++ Register used = counter; ++ Register saved_encCounter_start = key; ++ Register used_addr = saved_encCounter_start; ++ ++ __ movptr(used_addr, used_addr_param); ++ __ movptr(used, Address(used_addr, 0)); ++ __ movptr(saved_encCounter_start, saved_counter_param); ++ ++ __ BIND(L_preLoop_start); ++ __ cmpptr(used, 16); ++ __ jcc(Assembler::aboveEqual, L_exit_preLoop); ++ __ cmpptr(len_reg, 0); ++ __ jcc(Assembler::lessEqual, L_exit_preLoop); ++ __ movb(rax, Address(saved_encCounter_start, used)); ++ __ xorb(rax, Address(from, 0)); ++ __ movb(Address(to, 0), rax); ++ __ addptr(from, 1); ++ __ addptr(to, 1); ++ __ addptr(used, 1); ++ __ subptr(len_reg, 1); ++ ++ __ jmp(L_preLoop_start); ++ ++ __ BIND(L_exit_preLoop); ++ __ movptr(used_addr, used_addr_param); ++ __ movptr(used_addr, used_addr_param); ++ __ movl(Address(used_addr, 0), used); ++ ++ // load the parameters 'key' and 'counter' ++ __ movptr(key, key_param); ++ __ movptr(counter, rvec_param); ++ ++ // xmm register assignments for the loops below ++ const XMMRegister xmm_curr_counter = xmm0; ++ const XMMRegister xmm_counter_shuf_mask = xmm1; // need to be reloaded ++ const XMMRegister xmm_key_shuf_mask = xmm2; // need to be reloaded ++ const XMMRegister xmm_key = xmm3; ++ const XMMRegister xmm_result0 = xmm4; ++ const XMMRegister xmm_result1 = xmm5; ++ const XMMRegister xmm_result2 = xmm6; ++ const XMMRegister xmm_result3 = xmm7; ++ const XMMRegister xmm_from0 = xmm1; //reuse XMM register ++ const XMMRegister xmm_from1 = xmm2; ++ const XMMRegister xmm_from2 = xmm3; ++ const XMMRegister xmm_from3 = xmm4; ++ ++ //for key_128, key_192, key_256 ++ const int rounds[3] = {10, 12, 14}; ++ Label L_singleBlockLoopTop[3]; ++ Label L_multiBlock_loopTop[3]; ++ Label L_key192_top, L_key256_top; ++ Label L_incCounter[3][4]; // 3: different key length, 4: 4 blocks at a time ++ Label L_incCounter_single[3]; //for single block, key128, key192, key256 ++ Label L_processTail_insr[3], L_processTail_4_insr[3], L_processTail_2_insr[3], L_processTail_1_insr[3], L_processTail_exit_insr[3]; ++ Label L_processTail_extr[3], L_processTail_4_extr[3], L_processTail_2_extr[3], L_processTail_1_extr[3], L_processTail_exit_extr[3]; ++ ++ Label L_exit; ++ const int PARALLEL_FACTOR = 4; //because of the limited register number ++ ++ // initialize counter with initial counter ++ __ movdqu(xmm_curr_counter, Address(counter, 0x00)); ++ __ movdqu(xmm_counter_shuf_mask, ExternalAddress(StubRoutines::x86::counter_shuffle_mask_addr())); ++ __ pshufb(xmm_curr_counter, xmm_counter_shuf_mask); //counter is shuffled for increase ++ ++ // key length could be only {11, 13, 15} * 4 = {44, 52, 60} ++ __ movdqu(xmm_key_shuf_mask, ExternalAddress(StubRoutines::x86::key_shuffle_mask_addr())); ++ __ movl(rax, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT))); ++ __ cmpl(rax, 52); ++ __ jcc(Assembler::equal, L_key192_top); ++ __ cmpl(rax, 60); ++ __ jcc(Assembler::equal, L_key256_top); ++ ++ //key128 begins here ++ __ movptr(pos, 0); // init pos before L_multiBlock_loopTop ++ ++#define CTR_DoFour(opc, src_reg) \ ++ __ opc(xmm_result0, src_reg); \ ++ __ opc(xmm_result1, src_reg); \ ++ __ opc(xmm_result2, src_reg); \ ++ __ opc(xmm_result3, src_reg); ++ ++ // k == 0 : generate code for key_128 ++ // k == 1 : generate code for key_192 ++ // k == 2 : generate code for key_256 ++ for (int k = 0; k < 3; ++k) { ++ //multi blocks starts here ++ __ align(OptoLoopAlignment); ++ __ BIND(L_multiBlock_loopTop[k]); ++ __ cmpptr(len_reg, PARALLEL_FACTOR * AESBlockSize); // see if at least PARALLEL_FACTOR blocks left ++ __ jcc(Assembler::less, L_singleBlockLoopTop[k]); ++ ++ __ movdqu(xmm_key_shuf_mask, ExternalAddress(StubRoutines::x86::key_shuffle_mask_addr())); ++ __ movdqu(xmm_counter_shuf_mask, ExternalAddress(StubRoutines::x86::counter_shuffle_mask_addr())); ++ ++ //load, then increase counters ++ CTR_DoFour(movdqa, xmm_curr_counter); ++ __ push(rbx); ++ inc_counter(rbx, xmm_result1, 0x01, L_incCounter[k][0]); ++ inc_counter(rbx, xmm_result2, 0x02, L_incCounter[k][1]); ++ inc_counter(rbx, xmm_result3, 0x03, L_incCounter[k][2]); ++ inc_counter(rbx, xmm_curr_counter, 0x04, L_incCounter[k][3]); ++ __ pop (rbx); ++ ++ load_key(xmm_key, key, 0x00, xmm_key_shuf_mask); // load Round 0 key. interleaving for better performance ++ ++ CTR_DoFour(pshufb, xmm_counter_shuf_mask); // after increased, shuffled counters back for PXOR ++ CTR_DoFour(pxor, xmm_key); //PXOR with Round 0 key ++ ++ for (int i = 1; i < rounds[k]; ++i) { ++ load_key(xmm_key, key, (0x10 * i), xmm_key_shuf_mask); ++ CTR_DoFour(aesenc, xmm_key); ++ } ++ load_key(xmm_key, key, (0x10 * rounds[k]), xmm_key_shuf_mask); ++ CTR_DoFour(aesenclast, xmm_key); ++ ++ // get next PARALLEL_FACTOR blocks into xmm_from registers ++ __ movdqu(xmm_from0, Address(from, pos, Address::times_1, 0 * AESBlockSize)); ++ __ movdqu(xmm_from1, Address(from, pos, Address::times_1, 1 * AESBlockSize)); ++ __ movdqu(xmm_from2, Address(from, pos, Address::times_1, 2 * AESBlockSize)); ++ ++ // PXOR with input text ++ __ pxor(xmm_result0, xmm_from0); //result0 is xmm4 ++ __ pxor(xmm_result1, xmm_from1); ++ __ pxor(xmm_result2, xmm_from2); ++ ++ // store PARALLEL_FACTOR results into the next 64 bytes of output ++ __ movdqu(Address(to, pos, Address::times_1, 0 * AESBlockSize), xmm_result0); ++ __ movdqu(Address(to, pos, Address::times_1, 1 * AESBlockSize), xmm_result1); ++ __ movdqu(Address(to, pos, Address::times_1, 2 * AESBlockSize), xmm_result2); ++ ++ // do it here after xmm_result0 is saved, because xmm_from3 reuse the same register of xmm_result0. ++ __ movdqu(xmm_from3, Address(from, pos, Address::times_1, 3 * AESBlockSize)); ++ __ pxor(xmm_result3, xmm_from3); ++ __ movdqu(Address(to, pos, Address::times_1, 3 * AESBlockSize), xmm_result3); ++ ++ __ addptr(pos, PARALLEL_FACTOR * AESBlockSize); // increase the length of crypt text ++ __ subptr(len_reg, PARALLEL_FACTOR * AESBlockSize); // decrease the remaining length ++ __ jmp(L_multiBlock_loopTop[k]); ++ ++ // singleBlock starts here ++ __ align(OptoLoopAlignment); ++ __ BIND(L_singleBlockLoopTop[k]); ++ __ cmpptr(len_reg, 0); ++ __ jcc(Assembler::equal, L_exit); ++ __ movdqu(xmm_key_shuf_mask, ExternalAddress(StubRoutines::x86::key_shuffle_mask_addr())); ++ __ movdqu(xmm_counter_shuf_mask, ExternalAddress(StubRoutines::x86::counter_shuffle_mask_addr())); ++ __ movdqa(xmm_result0, xmm_curr_counter); ++ load_key(xmm_key, key, 0x00, xmm_key_shuf_mask); ++ __ push(rbx);//rbx is used for increasing counter ++ inc_counter(rbx, xmm_curr_counter, 0x01, L_incCounter_single[k]); ++ __ pop (rbx); ++ __ pshufb(xmm_result0, xmm_counter_shuf_mask); ++ __ pxor(xmm_result0, xmm_key); ++ for (int i = 1; i < rounds[k]; i++) { ++ load_key(xmm_key, key, (0x10 * i), xmm_key_shuf_mask); ++ __ aesenc(xmm_result0, xmm_key); ++ } ++ load_key(xmm_key, key, (0x10 * rounds[k]), xmm_key_shuf_mask); ++ __ aesenclast(xmm_result0, xmm_key); ++ __ cmpptr(len_reg, AESBlockSize); ++ __ jcc(Assembler::less, L_processTail_insr[k]); ++ __ movdqu(xmm_from0, Address(from, pos, Address::times_1, 0 * AESBlockSize)); ++ __ pxor(xmm_result0, xmm_from0); ++ __ movdqu(Address(to, pos, Address::times_1, 0 * AESBlockSize), xmm_result0); ++ __ addptr(pos, AESBlockSize); ++ __ subptr(len_reg, AESBlockSize); ++ __ jmp(L_singleBlockLoopTop[k]); ++ ++ __ BIND(L_processTail_insr[k]); ++ __ addptr(pos, len_reg); ++ __ testptr(len_reg, 8); ++ __ jcc(Assembler::zero, L_processTail_4_insr[k]); ++ __ subptr(pos,8); ++ __ pinsrd(xmm_from0, Address(from, pos), 0); ++ __ pinsrd(xmm_from0, Address(from, pos, Address::times_1, 4), 1); ++ __ BIND(L_processTail_4_insr[k]); ++ __ testptr(len_reg, 4); ++ __ jcc(Assembler::zero, L_processTail_2_insr[k]); ++ __ subptr(pos,4); ++ __ pslldq(xmm_from0, 4); ++ __ pinsrd(xmm_from0, Address(from, pos), 0); ++ __ BIND(L_processTail_2_insr[k]); ++ __ testptr(len_reg, 2); ++ __ jcc(Assembler::zero, L_processTail_1_insr[k]); ++ __ subptr(pos, 2); ++ __ pslldq(xmm_from0, 2); ++ __ pinsrw(xmm_from0, Address(from, pos), 0); ++ __ BIND(L_processTail_1_insr[k]); ++ __ testptr(len_reg, 1); ++ __ jcc(Assembler::zero, L_processTail_exit_insr[k]); ++ __ subptr(pos, 1); ++ __ pslldq(xmm_from0, 1); ++ __ pinsrb(xmm_from0, Address(from, pos), 0); ++ __ BIND(L_processTail_exit_insr[k]); ++ ++ __ movptr(saved_encCounter_start, saved_counter_param); ++ __ movdqu(Address(saved_encCounter_start, 0), xmm_result0); ++ __ pxor(xmm_result0, xmm_from0); ++ ++ __ testptr(len_reg, 8); ++ __ jcc(Assembler::zero, L_processTail_4_extr[k]); ++ __ pextrd(Address(to, pos), xmm_result0, 0); ++ __ pextrd(Address(to, pos, Address::times_1, 4), xmm_result0, 1); ++ __ psrldq(xmm_result0, 8); ++ __ addptr(pos, 8); ++ __ BIND(L_processTail_4_extr[k]); ++ __ testptr(len_reg, 4); ++ __ jcc(Assembler::zero, L_processTail_2_extr[k]); ++ __ pextrd(Address(to, pos), xmm_result0, 0); ++ __ psrldq(xmm_result0, 4); ++ __ addptr(pos, 4); ++ __ BIND(L_processTail_2_extr[k]); ++ __ testptr(len_reg, 2); ++ __ jcc(Assembler::zero, L_processTail_1_extr[k]); ++ __ pextrb(Address(to, pos), xmm_result0, 0); ++ __ pextrb(Address(to, pos, Address::times_1, 1), xmm_result0, 1); ++ __ psrldq(xmm_result0, 2); ++ __ addptr(pos, 2); ++ __ BIND(L_processTail_1_extr[k]); ++ __ testptr(len_reg, 1); ++ __ jcc(Assembler::zero, L_processTail_exit_extr[k]); ++ __ pextrb(Address(to, pos), xmm_result0, 0); ++ ++ __ BIND(L_processTail_exit_extr[k]); ++ __ movptr(used_addr, used_addr_param); ++ __ movl(Address(used_addr, 0), len_reg); ++ __ jmp(L_exit); ++ } ++ ++ __ BIND(L_exit); ++ __ movdqu(xmm_counter_shuf_mask, ExternalAddress(StubRoutines::x86::counter_shuffle_mask_addr())); ++ __ pshufb(xmm_curr_counter, xmm_counter_shuf_mask); //counter is shuffled back. ++ __ movdqu(Address(counter, 0), xmm_curr_counter); //save counter back ++ handleSOERegisters(false /*restoring*/); ++ __ movptr(rax, len_param); // return length ++ __ leave(); // required for proper stackwalking of RuntimeStub frame ++ __ ret(0); ++ ++ __ BIND (L_key192_top); ++ __ movptr(pos, 0); // init pos before L_multiBlock_loopTop ++ __ jmp(L_multiBlock_loopTop[1]); //key192 ++ ++ __ BIND (L_key256_top); ++ __ movptr(pos, 0); // init pos before L_multiBlock_loopTop ++ __ jmp(L_multiBlock_loopTop[2]); //key192 ++ ++ return start; ++ } ++ ++ + // byte swap x86 long + address generate_ghash_long_swap_mask() { + __ align(CodeEntryAlignment); +@@ -3181,6 +3520,11 @@ class StubGenerator: public StubCodeGenerator { + StubRoutines::_cipherBlockChaining_decryptAESCrypt = generate_cipherBlockChaining_decryptAESCrypt(); + } + ++ if (UseAESCTRIntrinsics) { ++ StubRoutines::x86::_counter_shuffle_mask_addr = generate_counter_shuffle_mask(); ++ StubRoutines::_counterMode_AESCrypt = generate_counterMode_AESCrypt_Parallel(); ++ } ++ + // Generate GHASH intrinsics code + if (UseGHASHIntrinsics) { + StubRoutines::x86::_ghash_long_swap_mask_addr = generate_ghash_long_swap_mask(); +diff --git a/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp b/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp +index c5811b28b..254f63392 100644 +--- a/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp ++++ b/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp +@@ -3010,6 +3010,15 @@ class StubGenerator: public StubCodeGenerator { + return start; + } + ++ address generate_counter_shuffle_mask() { ++ __ align(16); ++ StubCodeMark mark(this, "StubRoutines", "counter_shuffle_mask"); ++ address start = __ pc(); ++ __ emit_data64(0x08090a0b0c0d0e0f, relocInfo::none); ++ __ emit_data64(0x0001020304050607, relocInfo::none); ++ return start; ++ } ++ + // Utility routine for loading a 128-bit key word in little endian format + // can optionally specify that the shuffle mask is already in an xmmregister + void load_key(XMMRegister xmmdst, Register key, int offset, XMMRegister xmm_shuf_mask=NULL) { +@@ -3021,6 +3030,18 @@ class StubGenerator: public StubCodeGenerator { + } + } + ++ // Utility routine for increase 128bit counter (iv in CTR mode) ++ void inc_counter(Register reg, XMMRegister xmmdst, int inc_delta, Label& next_block) { ++ __ pextrq(reg, xmmdst, 0x0); ++ __ addq(reg, inc_delta); ++ __ pinsrq(xmmdst, reg, 0x0); ++ __ jcc(Assembler::carryClear, next_block); // jump if no carry ++ __ pextrq(reg, xmmdst, 0x01); // Carry ++ __ addq(reg, 0x01); ++ __ pinsrq(xmmdst, reg, 0x01); //Carry end ++ __ BIND(next_block); // next instruction ++ } ++ + // Arguments: + // + // Inputs: +@@ -3639,6 +3660,320 @@ class StubGenerator: public StubCodeGenerator { + return start; + } + ++ // This is a version of CTR/AES crypt which does 6 blocks in a loop at a time ++ // to hide instruction latency ++ // ++ // Arguments: ++ // ++ // Inputs: ++ // c_rarg0 - source byte array address ++ // c_rarg1 - destination byte array address ++ // c_rarg2 - K (key) in little endian int array ++ // c_rarg3 - counter vector byte array address ++ // Linux ++ // c_rarg4 - input length ++ // c_rarg5 - saved encryptedCounter start ++ // rbp + 6 * wordSize - saved used length ++ // Windows ++ // rbp + 6 * wordSize - input length ++ // rbp + 7 * wordSize - saved encryptedCounter start ++ // rbp + 8 * wordSize - saved used length ++ // ++ // Output: ++ // rax - input length ++ // ++ address generate_counterMode_AESCrypt_Parallel() { ++ assert(UseAES, "need AES instructions and misaligned SSE support"); ++ __ align(CodeEntryAlignment); ++ StubCodeMark mark(this, "StubRoutines", "counterMode_AESCrypt"); ++ address start = __ pc(); ++ const Register from = c_rarg0; // source array address ++ const Register to = c_rarg1; // destination array address ++ const Register key = c_rarg2; // key array address ++ const Register counter = c_rarg3; // counter byte array initialized from counter array address ++ // and left with the results of the last encryption block ++#ifndef _WIN64 ++ const Register len_reg = c_rarg4; ++ const Register saved_encCounter_start = c_rarg5; ++ const Register used_addr = r10; ++ const Address used_mem(rbp, 2 * wordSize); ++ const Register used = r11; ++#else ++ const Address len_mem(rbp, 6 * wordSize); // length is on stack on Win64 ++ const Address saved_encCounter_mem(rbp, 7 * wordSize); // length is on stack on Win64 ++ const Address used_mem(rbp, 8 * wordSize); // length is on stack on Win64 ++ const Register len_reg = r10; // pick the first volatile windows register ++ const Register saved_encCounter_start = r11; ++ const Register used_addr = r13; ++ const Register used = r14; ++#endif ++ const Register pos = rax; ++ ++ const int PARALLEL_FACTOR = 6; ++ const XMMRegister xmm_counter_shuf_mask = xmm0; ++ const XMMRegister xmm_key_shuf_mask = xmm1; // used temporarily to swap key bytes up front ++ const XMMRegister xmm_curr_counter = xmm2; ++ ++ const XMMRegister xmm_key_tmp0 = xmm3; ++ const XMMRegister xmm_key_tmp1 = xmm4; ++ ++ // registers holding the four results in the parallelized loop ++ const XMMRegister xmm_result0 = xmm5; ++ const XMMRegister xmm_result1 = xmm6; ++ const XMMRegister xmm_result2 = xmm7; ++ const XMMRegister xmm_result3 = xmm8; ++ const XMMRegister xmm_result4 = xmm9; ++ const XMMRegister xmm_result5 = xmm10; ++ ++ const XMMRegister xmm_from0 = xmm11; ++ const XMMRegister xmm_from1 = xmm12; ++ const XMMRegister xmm_from2 = xmm13; ++ const XMMRegister xmm_from3 = xmm14; //the last one is xmm14. we have to preserve it on WIN64. ++ const XMMRegister xmm_from4 = xmm3; //reuse xmm3~4. Because xmm_key_tmp0~1 are useless when loading input text ++ const XMMRegister xmm_from5 = xmm4; ++ ++ //for key_128, key_192, key_256 ++ const int rounds[3] = {10, 12, 14}; ++ Label L_exit_preLoop, L_preLoop_start; ++ Label L_multiBlock_loopTop[3]; ++ Label L_singleBlockLoopTop[3]; ++ Label L__incCounter[3][6]; //for 6 blocks ++ Label L__incCounter_single[3]; //for single block, key128, key192, key256 ++ Label L_processTail_insr[3], L_processTail_4_insr[3], L_processTail_2_insr[3], L_processTail_1_insr[3], L_processTail_exit_insr[3]; ++ Label L_processTail_extr[3], L_processTail_4_extr[3], L_processTail_2_extr[3], L_processTail_1_extr[3], L_processTail_exit_extr[3]; ++ ++ Label L_exit; ++ ++ __ enter(); // required for proper stackwalking of RuntimeStub frame ++ ++#ifdef _WIN64 ++ // save the xmm registers which must be preserved 6-14 ++ const int XMM_REG_NUM_KEY_LAST = 14; ++ __ subptr(rsp, -rsp_after_call_off * wordSize); ++ for (int i = 6; i <= XMM_REG_NUM_KEY_LAST; i++) { ++ __ movdqu(xmm_save(i), as_XMMRegister(i)); ++ } ++ ++ const Address r13_save(rbp, rdi_off * wordSize); ++ const Address r14_save(rbp, rsi_off * wordSize); ++ ++ __ movptr(r13_save, r13); ++ __ movptr(r14_save, r14); ++ ++ // on win64, fill len_reg from stack position ++ __ movl(len_reg, len_mem); ++ __ movptr(saved_encCounter_start, saved_encCounter_mem); ++ __ movptr(used_addr, used_mem); ++ __ movl(used, Address(used_addr, 0)); ++#else ++ __ push(len_reg); // Save ++ __ movptr(used_addr, used_mem); ++ __ movl(used, Address(used_addr, 0)); ++#endif ++ ++ __ push(rbx); // Save RBX ++ __ movdqu(xmm_curr_counter, Address(counter, 0x00)); // initialize counter with initial counter ++ __ movdqu(xmm_counter_shuf_mask, ExternalAddress(StubRoutines::x86::counter_shuffle_mask_addr())); ++ __ pshufb(xmm_curr_counter, xmm_counter_shuf_mask); //counter is shuffled ++ __ movptr(pos, 0); ++ ++ // Use the partially used encrpyted counter from last invocation ++ __ BIND(L_preLoop_start); ++ __ cmpptr(used, 16); ++ __ jcc(Assembler::aboveEqual, L_exit_preLoop); ++ __ cmpptr(len_reg, 0); ++ __ jcc(Assembler::lessEqual, L_exit_preLoop); ++ __ movb(rbx, Address(saved_encCounter_start, used)); ++ __ xorb(rbx, Address(from, pos)); ++ __ movb(Address(to, pos), rbx); ++ __ addptr(pos, 1); ++ __ addptr(used, 1); ++ __ subptr(len_reg, 1); ++ ++ __ jmp(L_preLoop_start); ++ ++ __ BIND(L_exit_preLoop); ++ __ movl(Address(used_addr, 0), used); ++ ++ // key length could be only {11, 13, 15} * 4 = {44, 52, 60} ++ __ movdqu(xmm_key_shuf_mask, ExternalAddress(StubRoutines::x86::key_shuffle_mask_addr())); ++ __ movl(rbx, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT))); ++ __ cmpl(rbx, 52); ++ __ jcc(Assembler::equal, L_multiBlock_loopTop[1]); ++ __ cmpl(rbx, 60); ++ __ jcc(Assembler::equal, L_multiBlock_loopTop[2]); ++ ++#define CTR_DoSix(opc, src_reg) \ ++ __ opc(xmm_result0, src_reg); \ ++ __ opc(xmm_result1, src_reg); \ ++ __ opc(xmm_result2, src_reg); \ ++ __ opc(xmm_result3, src_reg); \ ++ __ opc(xmm_result4, src_reg); \ ++ __ opc(xmm_result5, src_reg); ++ ++ // k == 0 : generate code for key_128 ++ // k == 1 : generate code for key_192 ++ // k == 2 : generate code for key_256 ++ for (int k = 0; k < 3; ++k) { ++ //multi blocks starts here ++ __ align(OptoLoopAlignment); ++ __ BIND(L_multiBlock_loopTop[k]); ++ __ cmpptr(len_reg, PARALLEL_FACTOR * AESBlockSize); // see if at least PARALLEL_FACTOR blocks left ++ __ jcc(Assembler::less, L_singleBlockLoopTop[k]); ++ load_key(xmm_key_tmp0, key, 0x00, xmm_key_shuf_mask); ++ ++ //load, then increase counters ++ CTR_DoSix(movdqa, xmm_curr_counter); ++ inc_counter(rbx, xmm_result1, 0x01, L__incCounter[k][0]); ++ inc_counter(rbx, xmm_result2, 0x02, L__incCounter[k][1]); ++ inc_counter(rbx, xmm_result3, 0x03, L__incCounter[k][2]); ++ inc_counter(rbx, xmm_result4, 0x04, L__incCounter[k][3]); ++ inc_counter(rbx, xmm_result5, 0x05, L__incCounter[k][4]); ++ inc_counter(rbx, xmm_curr_counter, 0x06, L__incCounter[k][5]); ++ CTR_DoSix(pshufb, xmm_counter_shuf_mask); // after increased, shuffled counters back for PXOR ++ CTR_DoSix(pxor, xmm_key_tmp0); //PXOR with Round 0 key ++ ++ //load two ROUND_KEYs at a time ++ for (int i = 1; i < rounds[k]; ) { ++ load_key(xmm_key_tmp1, key, (0x10 * i), xmm_key_shuf_mask); ++ load_key(xmm_key_tmp0, key, (0x10 * (i+1)), xmm_key_shuf_mask); ++ CTR_DoSix(aesenc, xmm_key_tmp1); ++ i++; ++ if (i != rounds[k]) { ++ CTR_DoSix(aesenc, xmm_key_tmp0); ++ } else { ++ CTR_DoSix(aesenclast, xmm_key_tmp0); ++ } ++ i++; ++ } ++ ++ // get next PARALLEL_FACTOR blocks into xmm_result registers ++ __ movdqu(xmm_from0, Address(from, pos, Address::times_1, 0 * AESBlockSize)); ++ __ movdqu(xmm_from1, Address(from, pos, Address::times_1, 1 * AESBlockSize)); ++ __ movdqu(xmm_from2, Address(from, pos, Address::times_1, 2 * AESBlockSize)); ++ __ movdqu(xmm_from3, Address(from, pos, Address::times_1, 3 * AESBlockSize)); ++ __ movdqu(xmm_from4, Address(from, pos, Address::times_1, 4 * AESBlockSize)); ++ __ movdqu(xmm_from5, Address(from, pos, Address::times_1, 5 * AESBlockSize)); ++ ++ __ pxor(xmm_result0, xmm_from0); ++ __ pxor(xmm_result1, xmm_from1); ++ __ pxor(xmm_result2, xmm_from2); ++ __ pxor(xmm_result3, xmm_from3); ++ __ pxor(xmm_result4, xmm_from4); ++ __ pxor(xmm_result5, xmm_from5); ++ ++ // store 6 results into the next 64 bytes of output ++ __ movdqu(Address(to, pos, Address::times_1, 0 * AESBlockSize), xmm_result0); ++ __ movdqu(Address(to, pos, Address::times_1, 1 * AESBlockSize), xmm_result1); ++ __ movdqu(Address(to, pos, Address::times_1, 2 * AESBlockSize), xmm_result2); ++ __ movdqu(Address(to, pos, Address::times_1, 3 * AESBlockSize), xmm_result3); ++ __ movdqu(Address(to, pos, Address::times_1, 4 * AESBlockSize), xmm_result4); ++ __ movdqu(Address(to, pos, Address::times_1, 5 * AESBlockSize), xmm_result5); ++ ++ __ addptr(pos, PARALLEL_FACTOR * AESBlockSize); // increase the length of crypt text ++ __ subptr(len_reg, PARALLEL_FACTOR * AESBlockSize); // decrease the remaining length ++ __ jmp(L_multiBlock_loopTop[k]); ++ ++ // singleBlock starts here ++ __ align(OptoLoopAlignment); ++ __ BIND(L_singleBlockLoopTop[k]); ++ __ cmpptr(len_reg, 0); ++ __ jcc(Assembler::lessEqual, L_exit); ++ load_key(xmm_key_tmp0, key, 0x00, xmm_key_shuf_mask); ++ __ movdqa(xmm_result0, xmm_curr_counter); ++ inc_counter(rbx, xmm_curr_counter, 0x01, L__incCounter_single[k]); ++ __ pshufb(xmm_result0, xmm_counter_shuf_mask); ++ __ pxor(xmm_result0, xmm_key_tmp0); ++ for (int i = 1; i < rounds[k]; i++) { ++ load_key(xmm_key_tmp0, key, (0x10 * i), xmm_key_shuf_mask); ++ __ aesenc(xmm_result0, xmm_key_tmp0); ++ } ++ load_key(xmm_key_tmp0, key, (rounds[k] * 0x10), xmm_key_shuf_mask); ++ __ aesenclast(xmm_result0, xmm_key_tmp0); ++ __ cmpptr(len_reg, AESBlockSize); ++ __ jcc(Assembler::less, L_processTail_insr[k]); ++ __ movdqu(xmm_from0, Address(from, pos, Address::times_1, 0 * AESBlockSize)); ++ __ pxor(xmm_result0, xmm_from0); ++ __ movdqu(Address(to, pos, Address::times_1, 0 * AESBlockSize), xmm_result0); ++ __ addptr(pos, AESBlockSize); ++ __ subptr(len_reg, AESBlockSize); ++ __ jmp(L_singleBlockLoopTop[k]); ++ __ BIND(L_processTail_insr[k]); ++ __ addptr(pos, len_reg); ++ __ testptr(len_reg, 8); ++ __ jcc(Assembler::zero, L_processTail_4_insr[k]); ++ __ subptr(pos,8); ++ __ pinsrq(xmm_from0, Address(from, pos), 0); ++ __ BIND(L_processTail_4_insr[k]); ++ __ testptr(len_reg, 4); ++ __ jcc(Assembler::zero, L_processTail_2_insr[k]); ++ __ subptr(pos,4); ++ __ pslldq(xmm_from0, 4); ++ __ pinsrd(xmm_from0, Address(from, pos), 0); ++ __ BIND(L_processTail_2_insr[k]); ++ __ testptr(len_reg, 2); ++ __ jcc(Assembler::zero, L_processTail_1_insr[k]); ++ __ subptr(pos, 2); ++ __ pslldq(xmm_from0, 2); ++ __ pinsrw(xmm_from0, Address(from, pos), 0); ++ __ BIND(L_processTail_1_insr[k]); ++ __ testptr(len_reg, 1); ++ __ jcc(Assembler::zero, L_processTail_exit_insr[k]); ++ __ subptr(pos, 1); ++ __ pslldq(xmm_from0, 1); ++ __ pinsrb(xmm_from0, Address(from, pos), 0); ++ __ BIND(L_processTail_exit_insr[k]); ++ ++ __ movdqu(Address(saved_encCounter_start, 0), xmm_result0); ++ __ pxor(xmm_result0, xmm_from0); ++ ++ __ testptr(len_reg, 8); ++ __ jcc(Assembler::zero, L_processTail_4_extr[k]); ++ __ pextrq(Address(to, pos), xmm_result0, 0); ++ __ psrldq(xmm_result0, 8); ++ __ addptr(pos, 8); ++ __ BIND(L_processTail_4_extr[k]); ++ __ testptr(len_reg, 4); ++ __ jcc(Assembler::zero, L_processTail_2_extr[k]); ++ __ pextrd(Address(to, pos), xmm_result0, 0); ++ __ psrldq(xmm_result0, 4); ++ __ addptr(pos, 4); ++ __ BIND(L_processTail_2_extr[k]); ++ __ testptr(len_reg, 2); ++ __ jcc(Assembler::zero, L_processTail_1_extr[k]); ++ __ pextrw(Address(to, pos), xmm_result0, 0); ++ __ psrldq(xmm_result0, 2); ++ __ addptr(pos, 2); ++ __ BIND(L_processTail_1_extr[k]); ++ __ testptr(len_reg, 1); ++ __ jcc(Assembler::zero, L_processTail_exit_extr[k]); ++ __ pextrb(Address(to, pos), xmm_result0, 0); ++ ++ __ BIND(L_processTail_exit_extr[k]); ++ __ movl(Address(used_addr, 0), len_reg); ++ __ jmp(L_exit); ++ ++ } ++ ++ __ BIND(L_exit); ++ __ pshufb(xmm_curr_counter, xmm_counter_shuf_mask); //counter is shuffled back. ++ __ movdqu(Address(counter, 0), xmm_curr_counter); //save counter back ++ __ pop(rbx); // pop the saved RBX. ++#ifdef _WIN64 ++ // restore regs belonging to calling function ++ for (int i = 6; i <= XMM_REG_NUM_KEY_LAST; i++) { ++ __ movdqu(as_XMMRegister(i), xmm_save(i)); ++ } ++ __ movl(rax, len_mem); ++ __ movptr(r13, r13_save); ++ __ movptr(r14, r14_save); ++#else ++ __ pop(rax); // return 'len' ++#endif ++ __ leave(); // required for proper stackwalking of RuntimeStub frame ++ __ ret(0); ++ return start; ++ } + + // byte swap x86 long + address generate_ghash_long_swap_mask() { +@@ -4239,12 +4574,15 @@ class StubGenerator: public StubCodeGenerator { + // don't bother generating these AES intrinsic stubs unless global flag is set + if (UseAESIntrinsics) { + StubRoutines::x86::_key_shuffle_mask_addr = generate_key_shuffle_mask(); // needed by the others +- + StubRoutines::_aescrypt_encryptBlock = generate_aescrypt_encryptBlock(); + StubRoutines::_aescrypt_decryptBlock = generate_aescrypt_decryptBlock(); + StubRoutines::_cipherBlockChaining_encryptAESCrypt = generate_cipherBlockChaining_encryptAESCrypt(); + StubRoutines::_cipherBlockChaining_decryptAESCrypt = generate_cipherBlockChaining_decryptAESCrypt_Parallel(); + } ++ if (UseAESCTRIntrinsics){ ++ StubRoutines::x86::_counter_shuffle_mask_addr = generate_counter_shuffle_mask(); ++ StubRoutines::_counterMode_AESCrypt = generate_counterMode_AESCrypt_Parallel(); ++ } + + // Generate GHASH intrinsics code + if (UseGHASHIntrinsics) { +diff --git a/hotspot/src/cpu/x86/vm/stubRoutines_x86.cpp b/hotspot/src/cpu/x86/vm/stubRoutines_x86.cpp +index 9b0d8fc75..617879377 100644 +--- a/hotspot/src/cpu/x86/vm/stubRoutines_x86.cpp ++++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86.cpp +@@ -33,6 +33,7 @@ + + address StubRoutines::x86::_verify_mxcsr_entry = NULL; + address StubRoutines::x86::_key_shuffle_mask_addr = NULL; ++address StubRoutines::x86::_counter_shuffle_mask_addr = NULL; + address StubRoutines::x86::_ghash_long_swap_mask_addr = NULL; + address StubRoutines::x86::_ghash_byte_swap_mask_addr = NULL; + +diff --git a/hotspot/src/cpu/x86/vm/stubRoutines_x86.hpp b/hotspot/src/cpu/x86/vm/stubRoutines_x86.hpp +index bb160486c..70b5a34ac 100644 +--- a/hotspot/src/cpu/x86/vm/stubRoutines_x86.hpp ++++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86.hpp +@@ -33,6 +33,10 @@ + static address _verify_mxcsr_entry; + // shuffle mask for fixing up 128-bit words consisting of big-endian 32-bit integers + static address _key_shuffle_mask_addr; ++ ++ //shuffle mask for big-endian 128-bit integers ++ static address _counter_shuffle_mask_addr; ++ + // masks and table for CRC32 + static uint64_t _crc_by128_masks[]; + static juint _crc_table[]; +@@ -43,6 +47,7 @@ + public: + static address verify_mxcsr_entry() { return _verify_mxcsr_entry; } + static address key_shuffle_mask_addr() { return _key_shuffle_mask_addr; } ++ static address counter_shuffle_mask_addr() { return _counter_shuffle_mask_addr; } + static address crc_by128_masks_addr() { return (address)_crc_by128_masks; } + static address ghash_long_swap_mask_addr() { return _ghash_long_swap_mask_addr; } + static address ghash_byte_swap_mask_addr() { return _ghash_byte_swap_mask_addr; } +diff --git a/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp b/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp +index bca5d493c..538f83e69 100644 +--- a/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp ++++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp +@@ -31,7 +31,7 @@ + + enum platform_dependent_constants { + code_size1 = 9000, // simply increase if too small (assembler will crash if too small) +- code_size2 = 22000 // simply increase if too small (assembler will crash if too small) ++ code_size2 = 25800 // simply increase if too small (assembler will crash if too small) + }; + + class x86 { +diff --git a/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp b/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp +index b048fd74e..f963cd2f8 100644 +--- a/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp ++++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp +@@ -33,7 +33,7 @@ static bool returns_to_call_stub(address return_pc) { return return_pc == _ + + enum platform_dependent_constants { + code_size1 = 19000, // simply increase if too small (assembler will crash if too small) +- code_size2 = 24000 // simply increase if too small (assembler will crash if too small) ++ code_size2 = 27000 // simply increase if too small (assembler will crash if too small) + }; + + class x86 { +diff --git a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp +index 46b3e32ea..ce3037d76 100644 +--- a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp ++++ b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp +@@ -573,6 +573,28 @@ void VM_Version::get_processor_features() { + } + FLAG_SET_DEFAULT(UseAESIntrinsics, false); + } ++ ++ // --AES-CTR begins-- ++ if (!UseAESIntrinsics) { ++ if (UseAESCTRIntrinsics && !FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { ++ warning("AES-CTR intrinsics require UseAESIntrinsics flag to be enabled. Intrinsics will be disabled."); ++ FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); ++ } ++ } else { ++ if(supports_sse4_1() && UseSSE >= 4) { ++ if (FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { ++ FLAG_SET_DEFAULT(UseAESCTRIntrinsics, true); ++ } ++ } else { ++ // The AES-CTR intrinsic stubs require AES instruction support (of course) ++ // but also require sse4.1 mode or higher for instructions it use. ++ if (UseAESCTRIntrinsics && !FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { ++ warning("X86 AES-CTR intrinsics require SSE4.1 instructions or higher. Intrinsics will be disabled."); ++ } ++ FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); ++ } ++ } ++ // --AES-CTR ends-- + } + } else if (UseAES || UseAESIntrinsics) { + if (UseAES && !FLAG_IS_DEFAULT(UseAES)) { +@@ -583,6 +605,10 @@ void VM_Version::get_processor_features() { + warning("AES intrinsics are not available on this CPU"); + FLAG_SET_DEFAULT(UseAESIntrinsics, false); + } ++ if (UseAESCTRIntrinsics && !FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { ++ warning("AES-CTR intrinsics are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); ++ } + } + + // Use CLMUL instructions if available. +@@ -606,6 +632,16 @@ void VM_Version::get_processor_features() { + FLAG_SET_DEFAULT(UseCRC32Intrinsics, false); + } + ++ if (UseAESIntrinsics) { ++ if (FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { ++ UseAESCTRIntrinsics = true; ++ } ++ } else if (UseAESCTRIntrinsics) { ++ if (!FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) ++ warning("AES/CTR intrinsics are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); ++ } ++ + // GHASH/GCM intrinsics + if (UseCLMUL && (UseSSE > 2)) { + if (FLAG_IS_DEFAULT(UseGHASHIntrinsics)) { +diff --git a/hotspot/src/share/vm/classfile/vmSymbols.hpp b/hotspot/src/share/vm/classfile/vmSymbols.hpp +index 942d172a1..4ca2a3ad4 100644 +--- a/hotspot/src/share/vm/classfile/vmSymbols.hpp ++++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp +@@ -846,6 +846,10 @@ + do_name( decrypt_name, "implDecrypt") \ + do_signature(byteArray_int_int_byteArray_int_signature, "([BII[BI)I") \ + \ ++ do_class(com_sun_crypto_provider_counterMode, "com/sun/crypto/provider/CounterMode") \ ++ do_intrinsic(_counterMode_AESCrypt, com_sun_crypto_provider_counterMode, crypt_name, byteArray_int_int_byteArray_int_signature, F_R) \ ++ do_name( crypt_name, "implCrypt") \ ++ \ + /* support for sun.security.provider.SHA */ \ + do_class(sun_security_provider_sha, "sun/security/provider/SHA") \ + do_intrinsic(_sha_implCompress, sun_security_provider_sha, implCompress_name, implCompress_signature, F_R) \ +diff --git a/hotspot/src/share/vm/opto/escape.cpp b/hotspot/src/share/vm/opto/escape.cpp +index 6f8ffe608..a0e497f08 100644 +--- a/hotspot/src/share/vm/opto/escape.cpp ++++ b/hotspot/src/share/vm/opto/escape.cpp +@@ -952,6 +952,7 @@ void ConnectionGraph::process_call_arguments(CallNode *call) { + strcmp(call->as_CallLeaf()->_name, "aescrypt_decryptBlock") == 0 || + strcmp(call->as_CallLeaf()->_name, "cipherBlockChaining_encryptAESCrypt") == 0 || + strcmp(call->as_CallLeaf()->_name, "cipherBlockChaining_decryptAESCrypt") == 0 || ++ strcmp(call->as_CallLeaf()->_name, "counterMode_AESCrypt") == 0 || + strcmp(call->as_CallLeaf()->_name, "ghash_processBlocks") == 0 || + strcmp(call->as_CallLeaf()->_name, "sha1_implCompress") == 0 || + strcmp(call->as_CallLeaf()->_name, "sha1_implCompressMB") == 0 || +diff --git a/hotspot/src/share/vm/opto/library_call.cpp b/hotspot/src/share/vm/opto/library_call.cpp +index bb721f6f1..2add82dd1 100644 +--- a/hotspot/src/share/vm/opto/library_call.cpp ++++ b/hotspot/src/share/vm/opto/library_call.cpp +@@ -196,6 +196,7 @@ class LibraryCallKit : public GraphKit { + return generate_method_call(method_id, true, false); + } + Node * load_field_from_object(Node * fromObj, const char * fieldName, const char * fieldTypeString, bool is_exact, bool is_static); ++ Node * field_address_from_object(Node * fromObj, const char * fieldName, const char * fieldTypeString, bool is_exact, bool is_static, ciInstanceKlass * fromKls); + + Node* make_string_method_node(int opcode, Node* str1_start, Node* cnt1, Node* str2_start, Node* cnt2); + Node* make_string_method_node(int opcode, Node* str1, Node* str2); +@@ -309,7 +310,9 @@ class LibraryCallKit : public GraphKit { + bool inline_reference_get(); + bool inline_aescrypt_Block(vmIntrinsics::ID id); + bool inline_cipherBlockChaining_AESCrypt(vmIntrinsics::ID id); ++ bool inline_counterMode_AESCrypt(vmIntrinsics::ID id); + Node* inline_cipherBlockChaining_AESCrypt_predicate(bool decrypting); ++ Node* inline_counterMode_AESCrypt_predicate(); + Node* get_key_start_from_aescrypt_object(Node* aescrypt_object); + Node* get_original_key_start_from_aescrypt_object(Node* aescrypt_object); + bool inline_ghash_processBlocks(); +@@ -558,6 +561,13 @@ CallGenerator* Compile::make_vm_intrinsic(ciMethod* m, bool is_virtual) { + predicates = 1; + break; + ++ case vmIntrinsics::_counterMode_AESCrypt: ++ if (!UseAESCTRIntrinsics) { ++ return NULL; ++ } ++ predicates = 1; ++ break; ++ + case vmIntrinsics::_sha_implCompress: + if (!UseSHA1Intrinsics) return NULL; + break; +@@ -950,6 +960,9 @@ bool LibraryCallKit::try_to_inline(int predicate) { + case vmIntrinsics::_cipherBlockChaining_decryptAESCrypt: + return inline_cipherBlockChaining_AESCrypt(intrinsic_id()); + ++ case vmIntrinsics::_counterMode_AESCrypt: ++ return inline_counterMode_AESCrypt(intrinsic_id()); ++ + case vmIntrinsics::_sha_implCompress: + case vmIntrinsics::_sha2_implCompress: + case vmIntrinsics::_sha5_implCompress: +@@ -1021,6 +1034,8 @@ Node* LibraryCallKit::try_to_predicate(int predicate) { + return inline_cipherBlockChaining_AESCrypt_predicate(false); + case vmIntrinsics::_cipherBlockChaining_decryptAESCrypt: + return inline_cipherBlockChaining_AESCrypt_predicate(true); ++ case vmIntrinsics::_counterMode_AESCrypt: ++ return inline_counterMode_AESCrypt_predicate(); + case vmIntrinsics::_digestBase_implCompressMB: + return inline_digestBase_implCompressMB_predicate(predicate); + +@@ -6581,6 +6596,39 @@ Node * LibraryCallKit::load_field_from_object(Node * fromObj, const char * field + return loadedField; + } + ++Node * LibraryCallKit::field_address_from_object(Node * fromObj, const char * fieldName, const char * fieldTypeString, ++ bool is_exact = true, bool is_static = false, ++ ciInstanceKlass * fromKls = NULL) { ++ if (fromKls == NULL) { ++ const TypeInstPtr* tinst = _gvn.type(fromObj)->isa_instptr(); ++ assert(tinst != NULL, "obj is null"); ++ assert(tinst->klass()->is_loaded(), "obj is not loaded"); ++ assert(!is_exact || tinst->klass_is_exact(), "klass not exact"); ++ fromKls = tinst->klass()->as_instance_klass(); ++ } ++ else { ++ assert(is_static, "only for static field access"); ++ } ++ ciField* field = fromKls->get_field_by_name(ciSymbol::make(fieldName), ++ ciSymbol::make(fieldTypeString), ++ is_static); ++ ++ assert(field != NULL, "undefined field"); ++ assert(!field->is_volatile(), "not defined for volatile fields"); ++ ++ if (is_static) { ++ const TypeInstPtr* tip = TypeInstPtr::make(fromKls->java_mirror()); ++ fromObj = makecon(tip); ++ } ++ ++ // Next code copied from Parse::do_get_xxx(): ++ ++ // Compute address and memory type. ++ int offset = field->offset_in_bytes(); ++ Node *adr = basic_plus_adr(fromObj, fromObj, offset); ++ ++ return adr; ++} + + //------------------------------inline_aescrypt_Block----------------------- + bool LibraryCallKit::inline_aescrypt_Block(vmIntrinsics::ID id) { +@@ -6747,6 +6795,90 @@ bool LibraryCallKit::inline_cipherBlockChaining_AESCrypt(vmIntrinsics::ID id) { + return true; + } + ++//------------------------------inline_counterMode_AESCrypt----------------------- ++bool LibraryCallKit::inline_counterMode_AESCrypt(vmIntrinsics::ID id) { ++ assert(UseAES, "need AES instruction support"); ++ if (!UseAESCTRIntrinsics) return false; ++ ++ address stubAddr = NULL; ++ const char *stubName = NULL; ++ if (id == vmIntrinsics::_counterMode_AESCrypt) { ++ stubAddr = StubRoutines::counterMode_AESCrypt(); ++ stubName = "counterMode_AESCrypt"; ++ } ++ if (stubAddr == NULL) return false; ++ ++ Node* counterMode_object = argument(0); ++ Node* src = argument(1); ++ Node* src_offset = argument(2); ++ Node* len = argument(3); ++ Node* dest = argument(4); ++ Node* dest_offset = argument(5); ++ ++ // (1) src and dest are arrays. ++ const Type* src_type = src->Value(&_gvn); ++ const Type* dest_type = dest->Value(&_gvn); ++ const TypeAryPtr* top_src = src_type->isa_aryptr(); ++ const TypeAryPtr* top_dest = dest_type->isa_aryptr(); ++ assert(top_src != NULL && top_src->klass() != NULL && ++ top_dest != NULL && top_dest->klass() != NULL, "args are strange"); ++ ++ // checks are the responsibility of the caller ++ Node* src_start = src; ++ Node* dest_start = dest; ++ if (src_offset != NULL || dest_offset != NULL) { ++ assert(src_offset != NULL && dest_offset != NULL, ""); ++ src_start = array_element_address(src, src_offset, T_BYTE); ++ dest_start = array_element_address(dest, dest_offset, T_BYTE); ++ } ++ ++ // if we are in this set of code, we "know" the embeddedCipher is an AESCrypt object ++ // (because of the predicated logic executed earlier). ++ // so we cast it here safely. ++ // this requires a newer class file that has this array as littleEndian ints, otherwise we revert to java ++ Node* embeddedCipherObj = load_field_from_object(counterMode_object, "embeddedCipher", "Lcom/sun/crypto/provider/SymmetricCipher;", /*is_exact*/ false); ++ if (embeddedCipherObj == NULL) return false; ++ // cast it to what we know it will be at runtime ++ const TypeInstPtr* tinst = _gvn.type(counterMode_object)->isa_instptr(); ++ assert(tinst != NULL, "CTR obj is null"); ++ assert(tinst->klass()->is_loaded(), "CTR obj is not loaded"); ++ ciKlass* klass_AESCrypt = tinst->klass()->as_instance_klass()->find_klass(ciSymbol::make("com/sun/crypto/provider/AESCrypt")); ++ assert(klass_AESCrypt->is_loaded(), "predicate checks that this class is loaded"); ++ ciInstanceKlass* instklass_AESCrypt = klass_AESCrypt->as_instance_klass(); ++ const TypeKlassPtr* aklass = TypeKlassPtr::make(instklass_AESCrypt); ++ const TypeOopPtr* xtype = aklass->as_instance_type(); ++ Node* aescrypt_object = new (C) CheckCastPPNode(control(), embeddedCipherObj, xtype); ++ aescrypt_object = _gvn.transform(aescrypt_object); ++ // we need to get the start of the aescrypt_object's expanded key array ++ Node* k_start = get_key_start_from_aescrypt_object(aescrypt_object); ++ if (k_start == NULL) return false; ++ // similarly, get the start address of the r vector ++ Node* obj_counter = load_field_from_object(counterMode_object, "counter", "[B", /*is_exact*/ false); ++ if (obj_counter == NULL) return false; ++ Node* cnt_start = array_element_address(obj_counter, intcon(0), T_BYTE); ++ ++ Node* saved_encCounter = load_field_from_object(counterMode_object, "encryptedCounter", "[B", /*is_exact*/ false); ++ if (saved_encCounter == NULL) return false; ++ Node* saved_encCounter_start = array_element_address(saved_encCounter, intcon(0), T_BYTE); ++ Node* used = field_address_from_object(counterMode_object, "used", "I", /*is_exact*/ false); ++ ++ Node* ctrCrypt; ++ if (Matcher::pass_original_key_for_aes()) { ++ // no SPARC version for AES/CTR intrinsics now. ++ return false; ++ } ++ // Call the stub, passing src_start, dest_start, k_start, r_start and src_len ++ ctrCrypt = make_runtime_call(RC_LEAF|RC_NO_FP, ++ OptoRuntime::counterMode_aescrypt_Type(), ++ stubAddr, stubName, TypePtr::BOTTOM, ++ src_start, dest_start, k_start, cnt_start, len, saved_encCounter_start, used); ++ ++ // return cipher length (int) ++ Node* retvalue = _gvn.transform(new (C) ProjNode(ctrCrypt, TypeFunc::Parms)); ++ set_result(retvalue); ++ return true; ++} ++ + //------------------------------get_key_start_from_aescrypt_object----------------------- + Node * LibraryCallKit::get_key_start_from_aescrypt_object(Node *aescrypt_object) { + #ifdef PPC64 +@@ -6841,6 +6973,48 @@ Node* LibraryCallKit::inline_cipherBlockChaining_AESCrypt_predicate(bool decrypt + return _gvn.transform(region); + } + ++//----------------------------inline_counterMode_AESCrypt_predicate---------------------------- ++// Return node representing slow path of predicate check. ++// the pseudo code we want to emulate with this predicate is: ++// for encryption: ++// if (embeddedCipherObj instanceof AESCrypt) do_intrinsic, else do_javapath ++// for decryption: ++// if ((embeddedCipherObj instanceof AESCrypt) && (cipher!=plain)) do_intrinsic, else do_javapath ++// note cipher==plain is more conservative than the original java code but that's OK ++// ++ ++Node* LibraryCallKit::inline_counterMode_AESCrypt_predicate() { ++ // The receiver was checked for NULL already. ++ Node* objCTR = argument(0); ++ ++ // Load embeddedCipher field of CipherBlockChaining object. ++ Node* embeddedCipherObj = load_field_from_object(objCTR, "embeddedCipher", "Lcom/sun/crypto/provider/SymmetricCipher;", /*is_exact*/ false); ++ ++ // get AESCrypt klass for instanceOf check ++ // AESCrypt might not be loaded yet if some other SymmetricCipher got us to this compile point ++ // will have same classloader as CipherBlockChaining object ++ const TypeInstPtr* tinst = _gvn.type(objCTR)->isa_instptr(); ++ assert(tinst != NULL, "CTRobj is null"); ++ assert(tinst->klass()->is_loaded(), "CTRobj is not loaded"); ++ ++ // we want to do an instanceof comparison against the AESCrypt class ++ ciKlass* klass_AESCrypt = tinst->klass()->as_instance_klass()->find_klass(ciSymbol::make("com/sun/crypto/provider/AESCrypt")); ++ if (!klass_AESCrypt->is_loaded()) { ++ // if AESCrypt is not even loaded, we never take the intrinsic fast path ++ Node* ctrl = control(); ++ set_control(top()); // no regular fast path ++ return ctrl; ++ } ++ ++ ciInstanceKlass* instklass_AESCrypt = klass_AESCrypt->as_instance_klass(); ++ Node* instof = gen_instanceof(embeddedCipherObj, makecon(TypeKlassPtr::make(instklass_AESCrypt))); ++ Node* cmp_instof = _gvn.transform(new (C) CmpINode(instof, intcon(1))); ++ Node* bool_instof = _gvn.transform(new (C) BoolNode(cmp_instof, BoolTest::ne)); ++ Node* instof_false = generate_guard(bool_instof, NULL, PROB_MIN); ++ ++ return instof_false; // even if it is NULL ++} ++ + //------------------------------inline_ghash_processBlocks + bool LibraryCallKit::inline_ghash_processBlocks() { + address stubAddr; +diff --git a/hotspot/src/share/vm/opto/runtime.cpp b/hotspot/src/share/vm/opto/runtime.cpp +index 0a86211ba..1c51be19b 100644 +--- a/hotspot/src/share/vm/opto/runtime.cpp ++++ b/hotspot/src/share/vm/opto/runtime.cpp +@@ -1021,6 +1021,35 @@ const TypeFunc* OptoRuntime::cipherBlockChaining_aescrypt_Type() { + return TypeFunc::make(domain, range); + } + ++//for counterMode calls of aescrypt encrypt/decrypt, four pointers and a length, returning int ++const TypeFunc* OptoRuntime::counterMode_aescrypt_Type() { ++ // create input type (domain) ++ int num_args = 7; ++ if (Matcher::pass_original_key_for_aes()) { ++ num_args = 8; ++ } ++ int argcnt = num_args; ++ const Type** fields = TypeTuple::fields(argcnt); ++ int argp = TypeFunc::Parms; ++ fields[argp++] = TypePtr::NOTNULL; // src ++ fields[argp++] = TypePtr::NOTNULL; // dest ++ fields[argp++] = TypePtr::NOTNULL; // k array ++ fields[argp++] = TypePtr::NOTNULL; // counter array ++ fields[argp++] = TypeInt::INT; // src len ++ fields[argp++] = TypePtr::NOTNULL; // saved_encCounter ++ fields[argp++] = TypePtr::NOTNULL; // saved used addr ++ if (Matcher::pass_original_key_for_aes()) { ++ fields[argp++] = TypePtr::NOTNULL; // original k array ++ } ++ assert(argp == TypeFunc::Parms + argcnt, "correct decoding"); ++ const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms + argcnt, fields); ++ // returning cipher len (int) ++ fields = TypeTuple::fields(1); ++ fields[TypeFunc::Parms + 0] = TypeInt::INT; ++ const TypeTuple* range = TypeTuple::make(TypeFunc::Parms + 1, fields); ++ return TypeFunc::make(domain, range); ++} ++ + /* + * void implCompress(byte[] buf, int ofs) + */ +diff --git a/hotspot/src/share/vm/opto/runtime.hpp b/hotspot/src/share/vm/opto/runtime.hpp +index 47133d58c..f27e7d507 100644 +--- a/hotspot/src/share/vm/opto/runtime.hpp ++++ b/hotspot/src/share/vm/opto/runtime.hpp +@@ -299,6 +299,7 @@ private: + + static const TypeFunc* aescrypt_block_Type(); + static const TypeFunc* cipherBlockChaining_aescrypt_Type(); ++ static const TypeFunc* counterMode_aescrypt_Type(); + + static const TypeFunc* sha_implCompress_Type(); + static const TypeFunc* digestBase_implCompressMB_Type(); +diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp +index 65dfcf69b..91e52f033 100644 +--- a/hotspot/src/share/vm/runtime/globals.hpp ++++ b/hotspot/src/share/vm/runtime/globals.hpp +@@ -734,6 +734,9 @@ class CommandLineFlags { + product(bool, UseAESIntrinsics, false, \ + "Use intrinsics for AES versions of crypto") \ + \ ++ product(bool, UseAESCTRIntrinsics, false, \ ++ "Use intrinsics for the paralleled version of AES/CTR crypto") \ ++ \ + product(bool, UseSHA1Intrinsics, false, \ + "Use intrinsics for SHA-1 crypto hash function") \ + \ +diff --git a/hotspot/src/share/vm/runtime/stubRoutines.cpp b/hotspot/src/share/vm/runtime/stubRoutines.cpp +index f2106d13a..d66237137 100644 +--- a/hotspot/src/share/vm/runtime/stubRoutines.cpp ++++ b/hotspot/src/share/vm/runtime/stubRoutines.cpp +@@ -124,6 +124,7 @@ address StubRoutines::_aescrypt_encryptBlock = NULL; + address StubRoutines::_aescrypt_decryptBlock = NULL; + address StubRoutines::_cipherBlockChaining_encryptAESCrypt = NULL; + address StubRoutines::_cipherBlockChaining_decryptAESCrypt = NULL; ++address StubRoutines::_counterMode_AESCrypt = NULL; + address StubRoutines::_ghash_processBlocks = NULL; + + address StubRoutines::_sha1_implCompress = NULL; +diff --git a/hotspot/src/share/vm/runtime/stubRoutines.hpp b/hotspot/src/share/vm/runtime/stubRoutines.hpp +index 16075d9f4..9fb589540 100644 +--- a/hotspot/src/share/vm/runtime/stubRoutines.hpp ++++ b/hotspot/src/share/vm/runtime/stubRoutines.hpp +@@ -202,6 +202,7 @@ class StubRoutines: AllStatic { + static address _aescrypt_decryptBlock; + static address _cipherBlockChaining_encryptAESCrypt; + static address _cipherBlockChaining_decryptAESCrypt; ++ static address _counterMode_AESCrypt; + static address _ghash_processBlocks; + + static address _sha1_implCompress; +@@ -370,6 +371,7 @@ class StubRoutines: AllStatic { + static address aescrypt_decryptBlock() { return _aescrypt_decryptBlock; } + static address cipherBlockChaining_encryptAESCrypt() { return _cipherBlockChaining_encryptAESCrypt; } + static address cipherBlockChaining_decryptAESCrypt() { return _cipherBlockChaining_decryptAESCrypt; } ++ static address counterMode_AESCrypt() { return _counterMode_AESCrypt; } + static address ghash_processBlocks() { return _ghash_processBlocks; } + + static address sha1_implCompress() { return _sha1_implCompress; } +diff --git a/hotspot/src/share/vm/runtime/vmStructs.cpp b/hotspot/src/share/vm/runtime/vmStructs.cpp +index 3f2bfeb74..842b5840d 100644 +--- a/hotspot/src/share/vm/runtime/vmStructs.cpp ++++ b/hotspot/src/share/vm/runtime/vmStructs.cpp +@@ -815,6 +815,7 @@ typedef TwoOopHashtable SymbolTwoOopHashtable; + static_field(StubRoutines, _aescrypt_decryptBlock, address) \ + static_field(StubRoutines, _cipherBlockChaining_encryptAESCrypt, address) \ + static_field(StubRoutines, _cipherBlockChaining_decryptAESCrypt, address) \ ++ static_field(StubRoutines, _counterMode_AESCrypt, address) \ + static_field(StubRoutines, _ghash_processBlocks, address) \ + static_field(StubRoutines, _updateBytesCRC32, address) \ + static_field(StubRoutines, _crc_table_adr, address) \ +diff --git a/hotspot/test/compiler/7184394/TestAESBase.java b/hotspot/test/compiler/7184394/TestAESBase.java +index 5c3e6881e..afda2a1f7 100644 +--- a/hotspot/test/compiler/7184394/TestAESBase.java ++++ b/hotspot/test/compiler/7184394/TestAESBase.java +@@ -106,8 +106,8 @@ abstract public class TestAESBase { + cipher = Cipher.getInstance(algorithm + "/" + mode + "/" + paddingStr, "SunJCE"); + dCipher = Cipher.getInstance(algorithm + "/" + mode + "/" + paddingStr, "SunJCE"); + +- // CBC init +- if (mode.equals("CBC")) { ++ // CBC or CTR init ++ if (mode.equals("CBC") || mode.equals("CTR")) { + IvParameterSpec initVector = new IvParameterSpec(iv); + cipher.init(Cipher.ENCRYPT_MODE, key, initVector); + algParams = cipher.getParameters(); +diff --git a/hotspot/test/compiler/7184394/TestAESMain.java b/hotspot/test/compiler/7184394/TestAESMain.java +index ddd8eeaef..65949420a 100644 +--- a/hotspot/test/compiler/7184394/TestAESMain.java ++++ b/hotspot/test/compiler/7184394/TestAESMain.java +@@ -48,6 +48,13 @@ + * @run main/othervm/timeout=600 -Xbatch -DcheckOutput=true -Dmode=GCM -DencInputOffset=1 -DencOutputOffset=1 TestAESMain + * @run main/othervm/timeout=600 -Xbatch -DcheckOutput=true -Dmode=GCM -DencInputOffset=1 -DencOutputOffset=1 -DdecOutputOffset=1 TestAESMain + * @run main/othervm/timeout=600 -Xbatch -DcheckOutput=true -Dmode=GCM -DencInputOffset=1 -DencOutputOffset=1 -DdecOutputOffset=1 -DpaddingStr=NoPadding -DmsgSize=640 TestAESMain ++ * @run main/othervm/timeout=600 -Xbatch -DcheckOutput=true -Dmode=CTR TestAESMain ++ * @run main/othervm/timeout=600 -Xbatch -DcheckOutput=true -Dmode=CTR -DencInputOffset=1 TestAESMain ++ * @run main/othervm/timeout=600 -Xbatch -DcheckOutput=true -Dmode=CTR -DencOutputOffset=1 TestAESMain ++ * @run main/othervm/timeout=600 -Xbatch -DcheckOutput=true -Dmode=CTR -DdecOutputOffset=1 TestAESMain ++ * @run main/othervm/timeout=600 -Xbatch -DcheckOutput=true -Dmode=CTR -DencInputOffset=1 -DencOutputOffset=1 TestAESMain ++ * @run main/othervm/timeout=600 -Xbatch -DcheckOutput=true -Dmode=CTR -DencInputOffset=1 -DencOutputOffset=1 -DdecOutputOffset=1 TestAESMain ++ * @run main/othervm/timeout=600 -Xbatch -DcheckOutput=true -Dmode=CTR -DencInputOffset=1 -DencOutputOffset=1 -DdecOutputOffset=1 -DpaddingStr=NoPadding -DmsgSize=640 TestAESMain + * + * @author Tom Deneau + */ +diff --git a/jdk/src/share/classes/com/sun/crypto/provider/CounterMode.java b/jdk/src/share/classes/com/sun/crypto/provider/CounterMode.java +index aea9336c9..c2bd38a71 100644 +--- a/jdk/src/share/classes/com/sun/crypto/provider/CounterMode.java ++++ b/jdk/src/share/classes/com/sun/crypto/provider/CounterMode.java +@@ -39,10 +39,10 @@ import java.security.InvalidKeyException; + * @author Andreas Sterbenz + * @since 1.4.2 + */ +-final class CounterMode extends FeedbackCipher { ++class CounterMode extends FeedbackCipher { + + // current counter value +- private final byte[] counter; ++ final byte[] counter; + + // encrypted bytes of the previous counter value + private final byte[] encryptedCounter; +@@ -137,7 +137,7 @@ final class CounterMode extends FeedbackCipher { + * cipherOffset. + * + * @param in the buffer with the input data to be encrypted +- * @param inOffset the offset in plain ++ * @param inOff the offset in plain + * @param len the length of the input data + * @param out the buffer for the result + * @param outOff the offset in cipher +@@ -176,6 +176,11 @@ final class CounterMode extends FeedbackCipher { + RangeUtil.nullAndBoundsCheck(in, inOff, len); + RangeUtil.nullAndBoundsCheck(out, outOff, len); + ++ return implCrypt(in, inOff, len, out, outOff); ++ } ++ ++ // Implementation of crpyt() method. Possibly replaced with a compiler intrinsic. ++ private int implCrypt(byte[] in, int inOff, int len, byte[] out, int outOff) { + int result = len; + while (len-- > 0) { + if (used >= blockSize) { +diff --git a/jdk/src/share/classes/com/sun/crypto/provider/GCTR.java b/jdk/src/share/classes/com/sun/crypto/provider/GCTR.java +index f8a3eaa0a..6a394e448 100644 +--- a/jdk/src/share/classes/com/sun/crypto/provider/GCTR.java ++++ b/jdk/src/share/classes/com/sun/crypto/provider/GCTR.java +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2013, 2017 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 +@@ -29,52 +29,43 @@ + + package com.sun.crypto.provider; + +-import java.security.*; +-import javax.crypto.*; ++import javax.crypto.IllegalBlockSizeException; + import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE; + + /** + * This class represents the GCTR function defined in NIST 800-38D +- * under section 6.5. It needs to be constructed w/ an initialized +- * cipher object, and initial counter block(ICB). Given an input X +- * of arbitrary length, it processes and returns an output which has +- * the same length as X. The invariants of this class are: +- * +- * (1) The length of intialCounterBlk (and also of its clones, e.g., +- * fields counter and counterSave) is equal to AES_BLOCK_SIZE. +- * +- * (2) After construction, the field counter never becomes null, it +- * always contains a byte array of length AES_BLOCK_SIZE. ++ * under section 6.5. With a given cipher object and initial counter ++ * block, a counter mode operation is performed. Blocksize is limited ++ * to 16 bytes. + * + * If any invariant is broken, failures can occur because the + * AESCrypt.encryptBlock method can be intrinsified on the HotSpot VM + * (see JDK-8067648 for details). + * ++ * The counter mode operations can be intrinsified and parallelized ++ * by using CounterMode.implCrypt() if HotSpot VM supports it on the ++ * architecture. ++ * + *

This function is used in the implementation of GCM mode. + * + * @since 1.8 + */ +-final class GCTR { +- +- // these fields should not change after the object has been constructed +- private final SymmetricCipher aes; +- private final byte[] icb; +- +- // the current counter value +- private byte[] counter; ++final class GCTR extends CounterMode { + +- // needed for save/restore calls +- private byte[] counterSave = null; +- +- // NOTE: cipher should already be initialized + GCTR(SymmetricCipher cipher, byte[] initialCounterBlk) { +- this.aes = cipher; ++ super(cipher); + if (initialCounterBlk.length != AES_BLOCK_SIZE) { + throw new RuntimeException("length of initial counter block (" + initialCounterBlk.length + + ") not equal to AES_BLOCK_SIZE (" + AES_BLOCK_SIZE + ")"); + } +- this.icb = initialCounterBlk; +- this.counter = icb.clone(); ++ ++ iv = initialCounterBlk; ++ reset(); ++ } ++ ++ @Override ++ String getFeedback() { ++ return "GCTR"; + } + + // input must be multiples of 128-bit blocks when calling update +@@ -89,23 +80,11 @@ final class GCTR { + throw new RuntimeException("output buffer too small"); + } + +- byte[] encryptedCntr = new byte[AES_BLOCK_SIZE]; +- +- int numOfCompleteBlocks = inLen / AES_BLOCK_SIZE; +- for (int i = 0; i < numOfCompleteBlocks; i++) { +- aes.encryptBlock(counter, 0, encryptedCntr, 0); +- for (int n = 0; n < AES_BLOCK_SIZE; n++) { +- int index = (i * AES_BLOCK_SIZE + n); +- out[outOfs + index] = +- (byte) ((in[inOfs + index] ^ encryptedCntr[n])); +- } +- GaloisCounterMode.increment32(counter); +- } +- return inLen; ++ return encrypt(in, inOfs, inLen, out, outOfs); + } + + // input can be arbitrary size when calling doFinal +- protected int doFinal(byte[] in, int inOfs, int inLen, byte[] out, ++ int doFinal(byte[] in, int inOfs, int inLen, byte[] out, + int outOfs) throws IllegalBlockSizeException { + try { + if (inLen < 0) { +@@ -118,7 +97,7 @@ final class GCTR { + if (lastBlockSize != 0) { + // do the last partial block + byte[] encryptedCntr = new byte[AES_BLOCK_SIZE]; +- aes.encryptBlock(counter, 0, encryptedCntr, 0); ++ embeddedCipher.encryptBlock(counter, 0, encryptedCntr, 0); + for (int n = 0; n < lastBlockSize; n++) { + out[outOfs + completeBlkLen + n] = + (byte) ((in[inOfs + completeBlkLen + n] ^ +@@ -131,28 +110,4 @@ final class GCTR { + } + return inLen; + } +- +- /** +- * Resets the content of this object to when it's first constructed. +- */ +- void reset() { +- System.arraycopy(icb, 0, counter, 0, icb.length); +- counterSave = null; +- } +- +- /** +- * Save the current content of this object. +- */ +- void save() { +- this.counterSave = this.counter.clone(); +- } +- +- /** +- * Restores the content of this object to the previous saved one. +- */ +- void restore() { +- if (this.counterSave != null) { +- this.counter = this.counterSave; +- } +- } + } +diff --git a/jdk/src/share/classes/com/sun/crypto/provider/GHASH.java b/jdk/src/share/classes/com/sun/crypto/provider/GHASH.java +index dc42e6bbf..78f0723d7 100644 +--- a/jdk/src/share/classes/com/sun/crypto/provider/GHASH.java ++++ b/jdk/src/share/classes/com/sun/crypto/provider/GHASH.java +@@ -122,10 +122,10 @@ final class GHASH { + + } + +- /* subkeyH and state are stored in long[] for GHASH intrinsic use */ ++ /* subkeyHtbl and state are stored in long[] for GHASH intrinsic use */ + +- // hash subkey H; should not change after the object has been constructed +- private final long[] subkeyH; ++ // hashtable subkeyHtbl; holds 2*9 powers of subkeyH computed using carry-less multiplication ++ private long[] subkeyHtbl; + + // buffer for storing hash + private final long[] state; +@@ -147,9 +147,9 @@ final class GHASH { + throw new ProviderException("Internal error"); + } + state = new long[2]; +- this.subkeyH = new long[2]; +- this.subkeyH[0] = getLong(subkeyH, 0); +- this.subkeyH[1] = getLong(subkeyH, 8); ++ subkeyHtbl = new long[2*9]; ++ subkeyHtbl[0] = getLong(subkeyH, 0); ++ subkeyHtbl[1] = getLong(subkeyH, 8); + } + + /** +@@ -192,8 +192,8 @@ final class GHASH { + if (inLen == 0) { + return; + } +- ghashRangeCheck(in, inOfs, inLen, state, subkeyH); +- processBlocks(in, inOfs, inLen/AES_BLOCK_SIZE, state, subkeyH); ++ ghashRangeCheck(in, inOfs, inLen, state, subkeyHtbl); ++ processBlocks(in, inOfs, inLen/AES_BLOCK_SIZE, state, subkeyHtbl); + } + + private static void ghashRangeCheck(byte[] in, int inOfs, int inLen, long[] st, long[] subH) { +@@ -217,8 +217,8 @@ final class GHASH { + throw new RuntimeException("internal state has invalid length: " + + st.length); + } +- if (subH.length != 2) { +- throw new RuntimeException("internal subkeyH has invalid length: " + ++ if (subH.length != 18) { ++ throw new RuntimeException("internal subkeyHtbl has invalid length: " + + subH.length); + } + } +diff --git a/jdk/src/share/classes/sun/security/ssl/SSLSocketImpl.java b/jdk/src/share/classes/sun/security/ssl/SSLSocketImpl.java +index ab93e3097..dd2618455 100644 +--- a/jdk/src/share/classes/sun/security/ssl/SSLSocketImpl.java ++++ b/jdk/src/share/classes/sun/security/ssl/SSLSocketImpl.java +@@ -439,6 +439,8 @@ public final class SSLSocketImpl + if (!conContext.isNegotiated) { + readHandshakeRecord(); + } ++ } catch (InterruptedIOException iioe) { ++ handleException(iioe); + } catch (IOException ioe) { + throw conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Couldn't kickstart handshaking", ioe); +@@ -1309,12 +1311,11 @@ public final class SSLSocketImpl + } + } catch (SSLException ssle) { + throw ssle; ++ } catch (InterruptedIOException iioe) { ++ // don't change exception in case of timeouts or interrupts ++ throw iioe; + } catch (IOException ioe) { +- if (!(ioe instanceof SSLException)) { +- throw new SSLException("readHandshakeRecord", ioe); +- } else { +- throw ioe; +- } ++ throw new SSLException("readHandshakeRecord", ioe); + } + } + +@@ -1375,6 +1376,9 @@ public final class SSLSocketImpl + } + } catch (SSLException ssle) { + throw ssle; ++ } catch (InterruptedIOException iioe) { ++ // don't change exception in case of timeouts or interrupts ++ throw iioe; + } catch (IOException ioe) { + if (!(ioe instanceof SSLException)) { + throw new SSLException("readApplicationRecord", ioe); +diff --git a/jdk/src/share/classes/sun/security/ssl/SSLSocketInputRecord.java b/jdk/src/share/classes/sun/security/ssl/SSLSocketInputRecord.java +index 401822759..ab5712acc 100644 +--- a/jdk/src/share/classes/sun/security/ssl/SSLSocketInputRecord.java ++++ b/jdk/src/share/classes/sun/security/ssl/SSLSocketInputRecord.java +@@ -26,6 +26,7 @@ + package sun.security.ssl; + + import java.io.EOFException; ++import java.io.InterruptedIOException; + import java.io.IOException; + import java.io.InputStream; + import java.io.OutputStream; +@@ -47,37 +48,31 @@ import sun.security.ssl.SSLCipher.SSLReadCipher; + final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + private InputStream is = null; + private OutputStream os = null; +- private final byte[] temporary = new byte[1024]; ++ private final byte[] header = new byte[headerSize]; ++ private int headerOff = 0; ++ // Cache for incomplete record body. ++ private ByteBuffer recordBody = ByteBuffer.allocate(1024); + + private boolean formatVerified = false; // SSLv2 ruled out? + + // Cache for incomplete handshake messages. + private ByteBuffer handshakeBuffer = null; + +- private boolean hasHeader = false; // Had read the record header +- + SSLSocketInputRecord(HandshakeHash handshakeHash) { + super(handshakeHash, SSLReadCipher.nullTlsReadCipher()); + } + + @Override + int bytesInCompletePacket() throws IOException { +- if (!hasHeader) { +- // read exactly one record +- try { +- int really = read(is, temporary, 0, headerSize); +- if (really < 0) { +- // EOF: peer shut down incorrectly +- return -1; +- } +- } catch (EOFException eofe) { +- // The caller will handle EOF. +- return -1; +- } +- hasHeader = true; ++ // read header ++ try { ++ readHeader(); ++ } catch (EOFException eofe) { ++ // The caller will handle EOF. ++ return -1; + } + +- byte byteZero = temporary[0]; ++ byte byteZero = header[0]; + int len = 0; + + /* +@@ -93,9 +88,9 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + * Last sanity check that it's not a wild record + */ + if (!ProtocolVersion.isNegotiable( +- temporary[1], temporary[2], false)) { ++ header[1], header[2], false)) { + throw new SSLException("Unrecognized record version " + +- ProtocolVersion.nameOf(temporary[1], temporary[2]) + ++ ProtocolVersion.nameOf(header[1], header[2]) + + " , plaintext connection?"); + } + +@@ -109,8 +104,8 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + /* + * One of the SSLv3/TLS message types. + */ +- len = ((temporary[3] & 0xFF) << 8) + +- (temporary[4] & 0xFF) + headerSize; ++ len = ((header[3] & 0xFF) << 8) + ++ (header[4] & 0xFF) + headerSize; + } else { + /* + * Must be SSLv2 or something unknown. +@@ -121,11 +116,11 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + */ + boolean isShort = ((byteZero & 0x80) != 0); + +- if (isShort && ((temporary[2] == 1) || (temporary[2] == 4))) { ++ if (isShort && ((header[2] == 1) || (header[2] == 4))) { + if (!ProtocolVersion.isNegotiable( +- temporary[3], temporary[4], false)) { ++ header[3], header[4], false)) { + throw new SSLException("Unrecognized record version " + +- ProtocolVersion.nameOf(temporary[3], temporary[4]) + ++ ProtocolVersion.nameOf(header[3], header[4]) + + " , plaintext connection?"); + } + +@@ -138,9 +133,9 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + // + // int mask = (isShort ? 0x7F : 0x3F); + // len = ((byteZero & mask) << 8) + +- // (temporary[1] & 0xFF) + (isShort ? 2 : 3); ++ // (header[1] & 0xFF) + (isShort ? 2 : 3); + // +- len = ((byteZero & 0x7F) << 8) + (temporary[1] & 0xFF) + 2; ++ len = ((byteZero & 0x7F) << 8) + (header[1] & 0xFF) + 2; + } else { + // Gobblygook! + throw new SSLException( +@@ -160,34 +155,41 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + return null; + } + +- if (!hasHeader) { +- // read exactly one record +- int really = read(is, temporary, 0, headerSize); +- if (really < 0) { +- throw new EOFException("SSL peer shut down incorrectly"); +- } +- hasHeader = true; +- } ++ // read header ++ readHeader(); + +- Plaintext plaintext = null; +- if (!formatVerified) { +- formatVerified = true; ++ Plaintext[] plaintext = null; ++ boolean cleanInBuffer = true; ++ try { ++ if (!formatVerified) { ++ formatVerified = true; + +- /* +- * The first record must either be a handshake record or an +- * alert message. If it's not, it is either invalid or an +- * SSLv2 message. +- */ +- if ((temporary[0] != ContentType.HANDSHAKE.id) && +- (temporary[0] != ContentType.ALERT.id)) { +- hasHeader = false; +- return handleUnknownRecord(temporary); ++ /* ++ * The first record must either be a handshake record or an ++ * alert message. If it's not, it is either invalid or an ++ * SSLv2 message. ++ */ ++ if ((header[0] != ContentType.HANDSHAKE.id) && ++ (header[0] != ContentType.ALERT.id)) { ++ plaintext = handleUnknownRecord(); ++ } + } +- } + +- // The record header should has consumed. +- hasHeader = false; +- return decodeInputRecord(temporary); ++ // The record header should has consumed. ++ if (plaintext == null) { ++ plaintext = decodeInputRecord(); ++ } ++ } catch(InterruptedIOException e) { ++ // do not clean header and recordBody in case of Socket Timeout ++ cleanInBuffer = false; ++ throw e; ++ } finally { ++ if (cleanInBuffer) { ++ headerOff = 0; ++ recordBody.clear(); ++ } ++ } ++ return plaintext; + } + + @Override +@@ -200,9 +202,7 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + this.os = outputStream; + } + +- // Note that destination may be null +- private Plaintext[] decodeInputRecord( +- byte[] header) throws IOException, BadPaddingException { ++ private Plaintext[] decodeInputRecord() throws IOException, BadPaddingException { + byte contentType = header[0]; // pos: 0 + byte majorVersion = header[1]; // pos: 1 + byte minorVersion = header[2]; // pos: 2 +@@ -227,30 +227,27 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + } + + // +- // Read a complete record. ++ // Read a complete record and store in the recordBody ++ // recordBody is used to cache incoming record and restore in case of ++ // read operation timedout + // +- ByteBuffer destination = ByteBuffer.allocate(headerSize + contentLen); +- int dstPos = destination.position(); +- destination.put(temporary, 0, headerSize); +- while (contentLen > 0) { +- int howmuch = Math.min(temporary.length, contentLen); +- int really = read(is, temporary, 0, howmuch); +- if (really < 0) { +- throw new EOFException("SSL peer shut down incorrectly"); ++ if (recordBody.position() == 0) { ++ if (recordBody.capacity() < contentLen) { ++ recordBody = ByteBuffer.allocate(contentLen); + } +- +- destination.put(temporary, 0, howmuch); +- contentLen -= howmuch; ++ recordBody.limit(contentLen); ++ } else { ++ contentLen = recordBody.remaining(); + } +- destination.flip(); +- destination.position(dstPos + headerSize); ++ readFully(contentLen); ++ recordBody.flip(); + + if (SSLLogger.isOn && SSLLogger.isOn("record")) { + SSLLogger.fine( + "READ: " + + ProtocolVersion.nameOf(majorVersion, minorVersion) + + " " + ContentType.nameOf(contentType) + ", length = " + +- destination.remaining()); ++ recordBody.remaining()); + } + + // +@@ -259,7 +256,7 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + ByteBuffer fragment; + try { + Plaintext plaintext = +- readCipher.decrypt(contentType, destination, null); ++ readCipher.decrypt(contentType, recordBody, null); + fragment = plaintext.fragment; + contentType = plaintext.contentType; + } catch (BadPaddingException bpe) { +@@ -368,8 +365,7 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + }; + } + +- private Plaintext[] handleUnknownRecord( +- byte[] header) throws IOException, BadPaddingException { ++ private Plaintext[] handleUnknownRecord() throws IOException, BadPaddingException { + byte firstByte = header[0]; + byte thirdByte = header[2]; + +@@ -411,32 +407,29 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + } + + int msgLen = ((header[0] & 0x7F) << 8) | (header[1] & 0xFF); +- +- ByteBuffer destination = ByteBuffer.allocate(headerSize + msgLen); +- destination.put(temporary, 0, headerSize); +- msgLen -= 3; // had read 3 bytes of content as header +- while (msgLen > 0) { +- int howmuch = Math.min(temporary.length, msgLen); +- int really = read(is, temporary, 0, howmuch); +- if (really < 0) { +- throw new EOFException("SSL peer shut down incorrectly"); ++ if (recordBody.position() == 0) { ++ if (recordBody.capacity() < (headerSize + msgLen)) { ++ recordBody = ByteBuffer.allocate(headerSize + msgLen); + } +- +- destination.put(temporary, 0, howmuch); +- msgLen -= howmuch; ++ recordBody.limit(headerSize + msgLen); ++ recordBody.put(header, 0, headerSize); ++ } else { ++ msgLen = recordBody.remaining(); + } +- destination.flip(); ++ msgLen -= 3; // had read 3 bytes of content as header ++ readFully(msgLen); ++ recordBody.flip(); + + /* + * If we can map this into a V3 ClientHello, read and + * hash the rest of the V2 handshake, turn it into a + * V3 ClientHello message, and pass it up. + */ +- destination.position(2); // exclude the header +- handshakeHash.receive(destination); +- destination.position(0); ++ recordBody.position(2); // exclude the header ++ handshakeHash.receive(recordBody); ++ recordBody.position(0); + +- ByteBuffer converted = convertToClientHello(destination); ++ ByteBuffer converted = convertToClientHello(recordBody); + + if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + SSLLogger.fine( +@@ -456,28 +449,42 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + } + } + +- // Read the exact bytes of data, otherwise, return -1. +- private static int read(InputStream is, +- byte[] buffer, int offset, int len) throws IOException { +- int n = 0; +- while (n < len) { +- int readLen = is.read(buffer, offset + n, len - n); +- if (readLen < 0) { +- if (SSLLogger.isOn && SSLLogger.isOn("packet")) { +- SSLLogger.fine("Raw read: EOF"); +- } +- return -1; ++ // Read the exact bytes of data, otherwise, throw IOException. ++ private int readFully(int len) throws IOException { ++ int end = len + recordBody.position(); ++ int off = recordBody.position(); ++ try { ++ while (off < end) { ++ off += read(is, recordBody.array(), off, end - off); + } ++ } finally { ++ recordBody.position(off); ++ } ++ return len; ++ } ++ ++ // Read SSE record header, otherwise, throw IOException. ++ private int readHeader() throws IOException { ++ while (headerOff < headerSize) { ++ headerOff += read(is, header, headerOff, headerSize - headerOff); ++ } ++ return headerSize; ++ } + ++ private static int read(InputStream is, byte[] buf, int off, int len) throws IOException { ++ int readLen = is.read(buf, off, len); ++ if (readLen < 0) { + if (SSLLogger.isOn && SSLLogger.isOn("packet")) { +- ByteBuffer bb = ByteBuffer.wrap(buffer, offset + n, readLen); +- SSLLogger.fine("Raw read", bb); ++ SSLLogger.fine("Raw read: EOF"); + } +- +- n += readLen; ++ throw new EOFException("SSL peer shut down incorrectly"); + } + +- return n; ++ if (SSLLogger.isOn && SSLLogger.isOn("packet")) { ++ ByteBuffer bb = ByteBuffer.wrap(buf, off, readLen); ++ SSLLogger.fine("Raw read", bb); ++ } ++ return readLen; + } + + // Try to use up the input stream without impact the performance too much. +diff --git a/jdk/src/share/classes/sun/security/ssl/SSLTransport.java b/jdk/src/share/classes/sun/security/ssl/SSLTransport.java +index b3d03b370..78e13ea2c 100644 +--- a/jdk/src/share/classes/sun/security/ssl/SSLTransport.java ++++ b/jdk/src/share/classes/sun/security/ssl/SSLTransport.java +@@ -27,6 +27,7 @@ package sun.security.ssl; + + import java.io.EOFException; + import java.io.IOException; ++import java.io.InterruptedIOException; + import java.nio.ByteBuffer; + import javax.crypto.AEADBadTagException; + import javax.crypto.BadPaddingException; +@@ -134,6 +135,9 @@ interface SSLTransport { + } catch (EOFException eofe) { + // rethrow EOFException, the call will handle it if neede. + throw eofe; ++ } catch (InterruptedIOException iioe) { ++ // don't close the Socket in case of timeouts or interrupts. ++ throw iioe; + } catch (IOException ioe) { + throw context.fatal(Alert.UNEXPECTED_MESSAGE, ioe); + } +diff --git a/jdk/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMBench.java b/jdk/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMBench.java +new file mode 100644 +index 000000000..258672f59 +--- /dev/null ++++ b/jdk/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMBench.java +@@ -0,0 +1,128 @@ ++/* ++ * Copyright (c) 2015, 2019, 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++package org.openjdk.bench.javax.crypto.full; ++ ++import org.openjdk.jmh.annotations.Benchmark; ++import org.openjdk.jmh.annotations.Param; ++import org.openjdk.jmh.annotations.Setup; ++ ++import javax.crypto.Cipher; ++import javax.crypto.spec.GCMParameterSpec; ++import javax.crypto.spec.SecretKeySpec; ++ ++/** ++ * This performance tests runs AES/GCM encryption and decryption using byte[] ++ * as input and output buffers for single and multi-part testing. ++ * ++ * This test rotates the IV and creates a new GCMParameterSpec for each encrypt ++ * benchmark operation ++ */ ++ ++public class AESGCMBench extends CryptoBase { ++ ++ @Param({"128"}) ++ private int keyLength; ++ ++ @Param({"1024", "1500", "4096", "16384"}) ++ private int dataSize; ++ ++ byte[] encryptedData; ++ byte[] in, out; ++ private Cipher encryptCipher; ++ private Cipher decryptCipher; ++ SecretKeySpec ks; ++ GCMParameterSpec gcm_spec; ++ byte[] iv; ++ ++ private static final int IV_BUFFER_SIZE = 32; ++ private static final int IV_MODULO = IV_BUFFER_SIZE - 16; ++ int iv_index = 0; ++ int updateLen = 0; ++ ++ private int next_iv_index() { ++ int r = iv_index; ++ iv_index = (iv_index + 1) % IV_MODULO; ++ return r; ++ } ++ ++ @Setup ++ public void setup() throws Exception { ++ setupProvider(); ++ ++ // Setup key material ++ byte[] keystring = fillSecureRandom(new byte[keyLength / 8]); ++ ks = new SecretKeySpec(keystring, "AES"); ++ iv = fillSecureRandom(new byte[IV_BUFFER_SIZE]); ++ gcm_spec = new GCMParameterSpec(96, iv, next_iv_index(), 16); ++ ++ // Setup Cipher classes ++ encryptCipher = makeCipher(prov, "AES/GCM/NoPadding"); ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); ++ decryptCipher = makeCipher(prov, "AES/GCM/NoPadding"); ++ decryptCipher.init(Cipher.DECRYPT_MODE, ks, ++ encryptCipher.getParameters(). ++ getParameterSpec(GCMParameterSpec.class)); ++ ++ // Setup input/output buffers ++ in = fillRandom(new byte[dataSize]); ++ encryptedData = new byte[encryptCipher.getOutputSize(in.length)]; ++ out = new byte[encryptedData.length]; ++ encryptCipher.doFinal(in, 0, in.length, encryptedData, 0); ++ updateLen = in.length / 2; ++ ++ } ++ ++ @Benchmark ++ public void encrypt() throws Exception { ++ gcm_spec = new GCMParameterSpec(96, iv, next_iv_index(), 16); ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); ++ encryptCipher.doFinal(in, 0, in.length, out, 0); ++ } ++ ++ @Benchmark ++ public void encryptMultiPart() throws Exception { ++ gcm_spec = new GCMParameterSpec(96, iv, next_iv_index(), 16); ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); ++ int outOfs = encryptCipher.update(in, 0, updateLen, out, 0); ++ encryptCipher.doFinal(in, updateLen, in.length - updateLen, ++ out, outOfs); ++ } ++ ++ @Benchmark ++ public void decrypt() throws Exception { ++ decryptCipher.init(Cipher.DECRYPT_MODE, ks, ++ encryptCipher.getParameters(). ++ getParameterSpec(GCMParameterSpec.class)); ++ decryptCipher.doFinal(encryptedData, 0, encryptedData.length, out, 0); ++ } ++ ++ @Benchmark ++ public void decryptMultiPart() throws Exception { ++ decryptCipher.init(Cipher.DECRYPT_MODE, ks, ++ encryptCipher.getParameters(). ++ getParameterSpec(GCMParameterSpec.class)); ++ decryptCipher.update(encryptedData, 0, updateLen, out, 0); ++ decryptCipher.doFinal(encryptedData, updateLen, ++ encryptedData.length - updateLen, out, 0); ++ } ++} +\ No newline at end of file +diff --git a/jdk/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMByteBuffer.java b/jdk/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMByteBuffer.java +new file mode 100644 +index 000000000..cb6d20c51 +--- /dev/null ++++ b/jdk/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMByteBuffer.java +@@ -0,0 +1,163 @@ ++/* ++ * Copyright (c) 2021, 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. Oracle designates this ++ * particular file as subject to the "Classpath" exception as provided ++ * by Oracle in the LICENSE file that accompanied this code. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++package org.openjdk.bench.javax.crypto.full; ++ ++import org.openjdk.jmh.annotations.Benchmark; ++import org.openjdk.jmh.annotations.Param; ++import org.openjdk.jmh.annotations.Setup; ++ ++import javax.crypto.Cipher; ++import javax.crypto.spec.GCMParameterSpec; ++import javax.crypto.spec.SecretKeySpec; ++import java.nio.ByteBuffer; ++ ++/** ++ * This performance tests runs AES/GCM encryption and decryption using heap and ++ * direct ByteBuffers as input and output buffers for single and multi-part ++ * operations. ++ * ++ * This test rotates the IV and creates a new GCMParameterSpec for each encrypt ++ * benchmark operation ++ */ ++ ++public class AESGCMByteBuffer extends CryptoBase { ++ ++ @Param({"128"}) ++ private int keyLength; ++ ++ @Param({"1024", "1500", "4096", "16384"}) ++ private int dataSize; ++ ++ @Param({"direct", "heap"}) ++ private String dataMethod; ++ ++ byte[] data; ++ ByteBuffer encryptedData; ++ ByteBuffer in, out; ++ private Cipher encryptCipher; ++ private Cipher decryptCipher; ++ SecretKeySpec ks; ++ GCMParameterSpec gcm_spec; ++ byte[] iv; ++ ++ private static final int IV_BUFFER_SIZE = 32; ++ private static final int IV_MODULO = IV_BUFFER_SIZE - 16; ++ int iv_index = 0; ++ int updateLen = 0; ++ ++ private int next_iv_index() { ++ int r = iv_index; ++ iv_index = (iv_index + 1) % IV_MODULO; ++ return r; ++ } ++ ++ @Setup ++ public void setup() throws Exception { ++ setupProvider(); ++ ++ // Setup key material ++ byte[] keystring = fillSecureRandom(new byte[keyLength / 8]); ++ ks = new SecretKeySpec(keystring, "AES"); ++ iv = fillSecureRandom(new byte[IV_BUFFER_SIZE]); ++ gcm_spec = new GCMParameterSpec(96, iv, next_iv_index(), 16); ++ ++ // Setup Cipher classes ++ encryptCipher = makeCipher(prov, "AES/GCM/NoPadding"); ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); ++ decryptCipher = makeCipher(prov, "AES/GCM/NoPadding"); ++ decryptCipher.init(Cipher.DECRYPT_MODE, ks, ++ encryptCipher.getParameters(). ++ getParameterSpec(GCMParameterSpec.class)); ++ ++ // Setup input/output buffers ++ data = fillRandom(new byte[dataSize]); ++ if (dataMethod.equalsIgnoreCase("direct")) { ++ in = ByteBuffer.allocateDirect(data.length); ++ in.put(data); ++ in.flip(); ++ encryptedData = ByteBuffer.allocateDirect( ++ encryptCipher.getOutputSize(data.length)); ++ out = ByteBuffer.allocateDirect(encryptedData.capacity()); ++ } else if (dataMethod.equalsIgnoreCase("heap")) { ++ in = ByteBuffer.wrap(data); ++ encryptedData = ByteBuffer.allocate( ++ encryptCipher.getOutputSize(data.length)); ++ out = ByteBuffer.allocate(encryptedData.capacity()); ++ } ++ ++ encryptCipher.doFinal(in, encryptedData); ++ encryptedData.flip(); ++ in.flip(); ++ updateLen = in.remaining() / 2; ++ } ++ ++ @Benchmark ++ public void encrypt() throws Exception { ++ gcm_spec = new GCMParameterSpec(96, iv, next_iv_index(), 16); ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); ++ encryptCipher.doFinal(in, out); ++ out.flip(); ++ in.flip(); ++ } ++ ++ @Benchmark ++ public void encryptMultiPart() throws Exception { ++ gcm_spec = new GCMParameterSpec(96, iv, next_iv_index(), 16); ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); ++ in.limit(updateLen); ++ encryptCipher.update(in, out); ++ in.limit(in.capacity()); ++ encryptCipher.doFinal(in, out); ++ out.flip(); ++ in.flip(); ++ } ++ ++ @Benchmark ++ public void decrypt() throws Exception { ++ decryptCipher.init(Cipher.DECRYPT_MODE, ks, ++ encryptCipher.getParameters(). ++ getParameterSpec(GCMParameterSpec.class)); ++ decryptCipher.doFinal(encryptedData, out); ++ encryptedData.flip(); ++ out.flip(); ++ } ++ ++ @Benchmark ++ public void decryptMultiPart() throws Exception { ++ decryptCipher.init(Cipher.DECRYPT_MODE, ks, ++ encryptCipher.getParameters(). ++ getParameterSpec(GCMParameterSpec.class)); ++ ++ int len = encryptedData.remaining(); ++ encryptedData.limit(updateLen); ++ decryptCipher.update(encryptedData, out); ++ encryptedData.limit(len); ++ ++ decryptCipher.doFinal(encryptedData, out); ++ encryptedData.flip(); ++ out.flip(); ++ } ++ ++} +\ No newline at end of file +diff --git a/jdk/test/micro/org/openjdk/bench/javax/crypto/full/CryptoBase.java b/jdk/test/micro/org/openjdk/bench/javax/crypto/full/CryptoBase.java +new file mode 100644 +index 000000000..4af12703b +--- /dev/null ++++ b/jdk/test/micro/org/openjdk/bench/javax/crypto/full/CryptoBase.java +@@ -0,0 +1,102 @@ ++/* ++ * Copyright (c) 2015, 2018, 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++package org.openjdk.bench.javax.crypto.full; ++ ++import org.openjdk.jmh.annotations.BenchmarkMode; ++import org.openjdk.jmh.annotations.Fork; ++import org.openjdk.jmh.annotations.Measurement; ++import org.openjdk.jmh.annotations.Mode; ++import org.openjdk.jmh.annotations.OutputTimeUnit; ++import org.openjdk.jmh.annotations.Param; ++import org.openjdk.jmh.annotations.Scope; ++import org.openjdk.jmh.annotations.Setup; ++import org.openjdk.jmh.annotations.State; ++import org.openjdk.jmh.annotations.Warmup; ++ ++import javax.crypto.BadPaddingException; ++import javax.crypto.Cipher; ++import javax.crypto.IllegalBlockSizeException; ++import javax.crypto.NoSuchPaddingException; ++import java.security.NoSuchAlgorithmException; ++import java.security.Provider; ++import java.security.SecureRandom; ++import java.security.Security; ++import java.util.Random; ++import java.util.concurrent.TimeUnit; ++ ++ ++@Fork(jvmArgsAppend = {"-XX:+AlwaysPreTouch"}, value = 5) ++@Warmup(iterations = 3, time = 3) ++@Measurement(iterations = 8, time = 2) ++@OutputTimeUnit(TimeUnit.SECONDS) ++@State(Scope.Thread) ++@BenchmarkMode(Mode.Throughput) ++public class CryptoBase { ++ ++ @Param({""}) ++ private String provider; ++ ++ public Provider prov = null; ++ ++ @Setup ++ public void setupProvider() { ++ if (provider != null && !provider.isEmpty()) { ++ prov = Security.getProvider(provider); ++ if (prov == null) { ++ throw new RuntimeException("Can't find prodiver \"" + provider + "\""); ++ } ++ } ++ } ++ ++ public static Cipher makeCipher(Provider prov, String algorithm) throws NoSuchPaddingException, NoSuchAlgorithmException { ++ return (prov == null) ? Cipher.getInstance(algorithm) : Cipher.getInstance(algorithm, prov); ++ } ++ ++ public static byte[][] fillRandom(byte[][] data) { ++ Random rnd = new Random(); ++ for (byte[] d : data) { ++ rnd.nextBytes(d); ++ } ++ return data; ++ } ++ ++ public static byte[] fillRandom(byte[] data) { ++ Random rnd = new Random(); ++ rnd.nextBytes(data); ++ return data; ++ } ++ ++ public static byte[] fillSecureRandom(byte[] data) { ++ SecureRandom rnd = new SecureRandom(); ++ rnd.nextBytes(data); ++ return data; ++ } ++ ++ public static byte[][] fillEncrypted(byte[][] data, Cipher encryptCipher) throws BadPaddingException, IllegalBlockSizeException { ++ byte[][] encryptedData = new byte[data.length][]; ++ for (int i = 0; i < encryptedData.length; i++) { ++ encryptedData[i] = encryptCipher.doFinal(data[i]); ++ } ++ return encryptedData; ++ } ++} +\ No newline at end of file +diff --git a/jdk/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMBench.java b/jdk/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMBench.java +new file mode 100644 +index 000000000..a21b0c87f +--- /dev/null ++++ b/jdk/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMBench.java +@@ -0,0 +1,36 @@ ++/* ++ * Copyright (c) 2015, 2021, 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++package org.openjdk.bench.javax.crypto.small; ++ ++import org.openjdk.jmh.annotations.Param; ++ ++public class AESGCMBench extends ++ org.openjdk.bench.javax.crypto.full.AESGCMBench { ++ ++ @Param({"128"}) ++ private int keyLength; ++ ++ @Param({"1024"}) ++ private int dataSize; ++ ++} +\ No newline at end of file +diff --git a/jdk/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMByteBuffer.java b/jdk/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMByteBuffer.java +new file mode 100644 +index 000000000..2e389d300 +--- /dev/null ++++ b/jdk/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMByteBuffer.java +@@ -0,0 +1,36 @@ ++/* ++ * Copyright (c) 2021, 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++package org.openjdk.bench.javax.crypto.small; ++ ++import org.openjdk.jmh.annotations.Param; ++ ++public class AESGCMByteBuffer extends ++ org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer { ++ ++ @Param({"128"}) ++ private int keyLength; ++ ++ @Param({"1024"}) ++ private int dataSize; ++ ++} +\ No newline at end of file +diff --git a/jdk/test/sun/security/ssl/SSLSocketImpl/ClientTimeout.java b/jdk/test/sun/security/ssl/SSLSocketImpl/ClientTimeout.java +index 3eb1d7b89..7678cc71f 100644 +--- a/jdk/test/sun/security/ssl/SSLSocketImpl/ClientTimeout.java ++++ b/jdk/test/sun/security/ssl/SSLSocketImpl/ClientTimeout.java +@@ -26,8 +26,7 @@ + + /* + * @test +- * @bug 4836493 +- * @ignore need further evaluation ++ * @bug 4836493 8239798 + * @summary Socket timeouts for SSLSockets causes data corruption. + * @run main/othervm ClientTimeout + */ +diff --git a/jdk/test/sun/security/ssl/SSLSocketImpl/SSLExceptionForIOIssue.java b/jdk/test/sun/security/ssl/SSLSocketImpl/SSLExceptionForIOIssue.java +index 3e626a257..5578ea725 100644 +--- a/jdk/test/sun/security/ssl/SSLSocketImpl/SSLExceptionForIOIssue.java ++++ b/jdk/test/sun/security/ssl/SSLSocketImpl/SSLExceptionForIOIssue.java +@@ -36,7 +36,7 @@ + + import javax.net.ssl.*; + import java.io.*; +-import java.net.InetAddress; ++import java.net.*; + + public class SSLExceptionForIOIssue implements SSLContextTemplate { + +@@ -139,7 +139,7 @@ public class SSLExceptionForIOIssue implements SSLContextTemplate { + } catch (SSLProtocolException | SSLHandshakeException sslhe) { + clientException = sslhe; + System.err.println("unexpected client exception: " + sslhe); +- } catch (SSLException ssle) { ++ } catch (SSLException | SocketTimeoutException ssle) { + // the expected exception, ignore it + System.err.println("expected client exception: " + ssle); + } catch (Exception e) { +-- +2.17.1 + diff --git a/8159720-Failure-of-C2-compilation-with-tiered-preven.patch b/8159720-Failure-of-C2-compilation-with-tiered-preven.patch new file mode 100644 index 0000000000000000000000000000000000000000..a52d0c90a81ef183ecc162b86487ce0d0e75d5b2 --- /dev/null +++ b/8159720-Failure-of-C2-compilation-with-tiered-preven.patch @@ -0,0 +1,114 @@ +From 717ae5f43045b1e2d6f95c52fbd81c54ebf50977 Mon Sep 17 00:00:00 2001 +Date: Fri, 16 Sep 2022 01:12:20 +0000 +Subject: 8159720: Failure of C2 compilation with tiered prevents some + C1 compilations. + +--- + hotspot/src/share/vm/opto/compile.cpp | 2 +- + hotspot/src/share/vm/opto/compile.hpp | 10 +++------- + hotspot/src/share/vm/opto/matcher.cpp | 8 ++++---- + hotspot/src/share/vm/opto/parse1.cpp | 4 ++-- + 4 files changed, 10 insertions(+), 14 deletions(-) + +diff --git a/hotspot/src/share/vm/opto/compile.cpp b/hotspot/src/share/vm/opto/compile.cpp +index 5a42422e1..4a32e8a9f 100644 +--- a/hotspot/src/share/vm/opto/compile.cpp ++++ b/hotspot/src/share/vm/opto/compile.cpp +@@ -791,7 +791,7 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr + } + if (failing()) return; + if (cg == NULL) { +- record_method_not_compilable_all_tiers("cannot parse method"); ++ record_method_not_compilable("cannot parse method"); + return; + } + JVMState* jvms = build_start_state(start(), tf()); +diff --git a/hotspot/src/share/vm/opto/compile.hpp b/hotspot/src/share/vm/opto/compile.hpp +index 1150fd549..fb12b6874 100644 +--- a/hotspot/src/share/vm/opto/compile.hpp ++++ b/hotspot/src/share/vm/opto/compile.hpp +@@ -742,16 +742,12 @@ class Compile : public Phase { + bool failure_reason_is(const char* r) { return (r==_failure_reason) || (r!=NULL && _failure_reason!=NULL && strcmp(r, _failure_reason)==0); } + + void record_failure(const char* reason); +- void record_method_not_compilable(const char* reason, bool all_tiers = false) { +- // All bailouts cover "all_tiers" when TieredCompilation is off. +- if (!TieredCompilation) all_tiers = true; +- env()->record_method_not_compilable(reason, all_tiers); ++ void record_method_not_compilable(const char* reason) { ++ // Bailouts cover "all_tiers" when TieredCompilation is off. ++ env()->record_method_not_compilable(reason, !TieredCompilation); + // Record failure reason. + record_failure(reason); + } +- void record_method_not_compilable_all_tiers(const char* reason) { +- record_method_not_compilable(reason, true); +- } + bool check_node_count(uint margin, const char* reason) { + if (live_nodes() + margin > max_node_limit()) { + record_method_not_compilable(reason); +diff --git a/hotspot/src/share/vm/opto/matcher.cpp b/hotspot/src/share/vm/opto/matcher.cpp +index 07b8ee4c6..b26015ce6 100644 +--- a/hotspot/src/share/vm/opto/matcher.cpp ++++ b/hotspot/src/share/vm/opto/matcher.cpp +@@ -137,7 +137,7 @@ OptoReg::Name Matcher::warp_incoming_stk_arg( VMReg reg ) { + _in_arg_limit = OptoReg::add(warped, 1); // Bump max stack slot seen + if (!RegMask::can_represent_arg(warped)) { + // the compiler cannot represent this method's calling sequence +- C->record_method_not_compilable_all_tiers("unsupported incoming calling sequence"); ++ C->record_method_not_compilable("unsupported incoming calling sequence"); + return OptoReg::Bad; + } + return warped; +@@ -1148,7 +1148,7 @@ OptoReg::Name Matcher::warp_outgoing_stk_arg( VMReg reg, OptoReg::Name begin_out + if( warped >= out_arg_limit_per_call ) + out_arg_limit_per_call = OptoReg::add(warped,1); + if (!RegMask::can_represent_arg(warped)) { +- C->record_method_not_compilable_all_tiers("unsupported calling sequence"); ++ C->record_method_not_compilable("unsupported calling sequence"); + return OptoReg::Bad; + } + return warped; +@@ -1327,7 +1327,7 @@ MachNode *Matcher::match_sfpt( SafePointNode *sfpt ) { + uint r_cnt = mcall->tf()->range()->cnt(); + MachProjNode *proj = new (C) MachProjNode( mcall, r_cnt+10000, RegMask::Empty, MachProjNode::fat_proj ); + if (!RegMask::can_represent_arg(OptoReg::Name(out_arg_limit_per_call-1))) { +- C->record_method_not_compilable_all_tiers("unsupported outgoing calling sequence"); ++ C->record_method_not_compilable("unsupported outgoing calling sequence"); + } else { + for (int i = begin_out_arg_area; i < out_arg_limit_per_call; i++) + proj->_rout.Insert(OptoReg::Name(i)); +@@ -1515,7 +1515,7 @@ Node *Matcher::Label_Root( const Node *n, State *svec, Node *control, const Node + // out of stack space. See bugs 6272980 & 6227033 for more info. + LabelRootDepth++; + if (LabelRootDepth > MaxLabelRootDepth) { +- C->record_method_not_compilable_all_tiers("Out of stack space, increase MaxLabelRootDepth"); ++ C->record_method_not_compilable("Out of stack space, increase MaxLabelRootDepth"); + return NULL; + } + uint care = 0; // Edges matcher cares about +diff --git a/hotspot/src/share/vm/opto/parse1.cpp b/hotspot/src/share/vm/opto/parse1.cpp +index a9ef4f910..4fcd58cb4 100644 +--- a/hotspot/src/share/vm/opto/parse1.cpp ++++ b/hotspot/src/share/vm/opto/parse1.cpp +@@ -415,7 +415,7 @@ Parse::Parse(JVMState* caller, ciMethod* parse_method, float expected_uses) + _iter.reset_to_method(method()); + _flow = method()->get_flow_analysis(); + if (_flow->failing()) { +- C->record_method_not_compilable_all_tiers(_flow->failure_reason()); ++ C->record_method_not_compilable(_flow->failure_reason()); + } + + #ifndef PRODUCT +@@ -1088,7 +1088,7 @@ SafePointNode* Parse::create_entry_map() { + // Check for really stupid bail-out cases. + uint len = TypeFunc::Parms + method()->max_locals() + method()->max_stack(); + if (len >= 32760) { +- C->record_method_not_compilable_all_tiers("too many local variables"); ++ C->record_method_not_compilable("too many local variables"); + return NULL; + } + +-- +2.18.0.huawei.25 + diff --git a/8173339-AArch64-Fix-minimum-stack-size-computations.patch b/8173339-AArch64-Fix-minimum-stack-size-computations.patch deleted file mode 100644 index e9a08f8d791cdf07772146817a197850dd197bc8..0000000000000000000000000000000000000000 --- a/8173339-AArch64-Fix-minimum-stack-size-computations.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 85a351276984f56d817560db8b5b837254ec2994 Mon Sep 17 00:00:00 2001 -From: zhangyipeng -Date: Tue, 7 Jun 2022 20:10:03 +0800 -Subject: [PATCH 05/10] 8173339: AArch64: Fix minimum stack size computations - -Bug url: https://bugs.openjdk.java.net/browse/JDK-8173339 ---- - hotspot/src/cpu/aarch64/vm/globals_aarch64.hpp | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/hotspot/src/cpu/aarch64/vm/globals_aarch64.hpp b/hotspot/src/cpu/aarch64/vm/globals_aarch64.hpp -index 6610cc4fb..7c6b24879 100644 ---- a/hotspot/src/cpu/aarch64/vm/globals_aarch64.hpp -+++ b/hotspot/src/cpu/aarch64/vm/globals_aarch64.hpp -@@ -56,7 +56,10 @@ define_pd_global(intx, InlineFrequencyCount, 100); - define_pd_global(intx, StackYellowPages, 2); - define_pd_global(intx, StackRedPages, 1); - --define_pd_global(intx, StackShadowPages, 4 DEBUG_ONLY(+5)); -+// Java_java_net_SocketOutputStream_socketWrite0() uses a 64k buffer on the -+// stack if compiled for unix and LP64. To pass stack overflow tests we need -+// 20 shadow pages. -+define_pd_global(intx, StackShadowPages, 20 DEBUG_ONLY(+5)); - - define_pd_global(intx, PreInflateSpin, 10); - --- -2.22.0 - diff --git a/8173361-various-crashes-in-JvmtiExport-post_compiled.patch b/8173361-various-crashes-in-JvmtiExport-post_compiled.patch deleted file mode 100755 index 2b14cc7a333bce48c10049f12810d85d683adb50..0000000000000000000000000000000000000000 --- a/8173361-various-crashes-in-JvmtiExport-post_compiled.patch +++ /dev/null @@ -1,290 +0,0 @@ -diff --git a/hotspot/src/share/vm/code/nmethod.cpp b/hotspot/src/share/vm/code/nmethod.cpp -index 175c195c6..01e878022 100644 ---- a/hotspot/src/share/vm/code/nmethod.cpp -+++ b/hotspot/src/share/vm/code/nmethod.cpp -@@ -1656,24 +1656,28 @@ bool nmethod::can_unload(BoolObjectClosure* is_alive, oop* root, bool unloading_ - // Transfer information from compilation to jvmti - void nmethod::post_compiled_method_load_event() { - -- Method* moop = method(); -+ // This is a bad time for a safepoint. We don't want -+ // this nmethod to get unloaded while we're queueing the event. -+ No_Safepoint_Verifier nsv; -+ -+ Method* m = method(); - #ifndef USDT2 - HS_DTRACE_PROBE8(hotspot, compiled__method__load, -- moop->klass_name()->bytes(), -- moop->klass_name()->utf8_length(), -- moop->name()->bytes(), -- moop->name()->utf8_length(), -- moop->signature()->bytes(), -- moop->signature()->utf8_length(), -+ m->klass_name()->bytes(), -+ m->klass_name()->utf8_length(), -+ m->name()->bytes(), -+ m->name()->utf8_length(), -+ m->signature()->bytes(), -+ m->signature()->utf8_length(), - insts_begin(), insts_size()); - #else /* USDT2 */ - HOTSPOT_COMPILED_METHOD_LOAD( -- (char *) moop->klass_name()->bytes(), -- moop->klass_name()->utf8_length(), -- (char *) moop->name()->bytes(), -- moop->name()->utf8_length(), -- (char *) moop->signature()->bytes(), -- moop->signature()->utf8_length(), -+ (char *) m->klass_name()->bytes(), -+ m->klass_name()->utf8_length(), -+ (char *) m->name()->bytes(), -+ m->name()->utf8_length(), -+ (char *) m->signature()->bytes(), -+ m->signature()->utf8_length(), - insts_begin(), insts_size()); - #endif /* USDT2 */ - -diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp -index 895fbbf07..367c9a09d 100644 ---- a/hotspot/src/share/vm/oops/instanceKlass.cpp -+++ b/hotspot/src/share/vm/oops/instanceKlass.cpp -@@ -1786,7 +1786,7 @@ jmethodID InstanceKlass::get_jmethod_id(instanceKlassHandle ik_h, methodHandle m - // we're single threaded or at a safepoint - no locking needed - get_jmethod_id_length_value(jmeths, idnum, &length, &id); - } else { -- MutexLocker ml(JmethodIdCreation_lock); -+ MutexLockerEx ml(JmethodIdCreation_lock, Mutex::_no_safepoint_check_flag); - get_jmethod_id_length_value(jmeths, idnum, &length, &id); - } - } -@@ -1836,7 +1836,7 @@ jmethodID InstanceKlass::get_jmethod_id(instanceKlassHandle ik_h, methodHandle m - id = get_jmethod_id_fetch_or_update(ik_h, idnum, new_id, new_jmeths, - &to_dealloc_id, &to_dealloc_jmeths); - } else { -- MutexLocker ml(JmethodIdCreation_lock); -+ MutexLockerEx ml(JmethodIdCreation_lock, Mutex::_no_safepoint_check_flag); - id = get_jmethod_id_fetch_or_update(ik_h, idnum, new_id, new_jmeths, - &to_dealloc_id, &to_dealloc_jmeths); - } -diff --git a/hotspot/src/share/vm/prims/jvmtiExport.cpp b/hotspot/src/share/vm/prims/jvmtiExport.cpp -index 9b612598f..967ed200d 100644 ---- a/hotspot/src/share/vm/prims/jvmtiExport.cpp -+++ b/hotspot/src/share/vm/prims/jvmtiExport.cpp -@@ -1754,7 +1754,7 @@ jvmtiCompiledMethodLoadInlineRecord* create_inline_record(nmethod* nm) { - int stackframe = 0; - for(ScopeDesc* sd = nm->scope_desc_at(p->real_pc(nm));sd != NULL;sd = sd->sender()) { - // sd->method() can be NULL for stubs but not for nmethods. To be completely robust, include an assert that we should never see a null sd->method() -- assert(sd->method() != NULL, "sd->method() cannot be null."); -+ guarantee(sd->method() != NULL, "sd->method() cannot be null."); - record->pcinfo[scope].methods[stackframe] = sd->method()->jmethod_id(); - record->pcinfo[scope].bcis[stackframe] = sd->bci(); - stackframe++; -diff --git a/hotspot/src/share/vm/prims/jvmtiImpl.cpp b/hotspot/src/share/vm/prims/jvmtiImpl.cpp -index 3c66b1671..3bcd15ed6 100644 ---- a/hotspot/src/share/vm/prims/jvmtiImpl.cpp -+++ b/hotspot/src/share/vm/prims/jvmtiImpl.cpp -@@ -897,9 +897,6 @@ JvmtiDeferredEvent JvmtiDeferredEvent::compiled_method_load_event( - nmethod* nm) { - JvmtiDeferredEvent event = JvmtiDeferredEvent(TYPE_COMPILED_METHOD_LOAD); - event._event_data.compiled_method_load = nm; -- // Keep the nmethod alive until the ServiceThread can process -- // this deferred event. -- nmethodLocker::lock_nmethod(nm); - return event; - } - -@@ -932,14 +929,12 @@ JvmtiDeferredEvent JvmtiDeferredEvent::dynamic_code_generated_event( - } - - void JvmtiDeferredEvent::post() { -- assert(ServiceThread::is_service_thread(Thread::current()), -+ assert(Thread::current()->is_service_thread(), - "Service thread must post enqueued events"); - switch(_type) { - case TYPE_COMPILED_METHOD_LOAD: { - nmethod* nm = _event_data.compiled_method_load; - JvmtiExport::post_compiled_method_load(nm); -- // done with the deferred event so unlock the nmethod -- nmethodLocker::unlock_nmethod(nm); - break; - } - case TYPE_COMPILED_METHOD_UNLOAD: { -@@ -969,6 +964,21 @@ void JvmtiDeferredEvent::post() { - } - } - -+// Keep the nmethod for compiled_method_load from being unloaded. -+void JvmtiDeferredEvent::oops_do(OopClosure* f, CodeBlobClosure* cf) { -+ if (cf != NULL && _type == TYPE_COMPILED_METHOD_LOAD) { -+ cf->do_code_blob(_event_data.compiled_method_load); -+ } -+} -+ -+// The sweeper calls this and marks the nmethods here on the stack so that -+// they cannot be turned into zombies while in the queue. -+void JvmtiDeferredEvent::nmethods_do(CodeBlobClosure* cf) { -+ if (cf != NULL && _type == TYPE_COMPILED_METHOD_LOAD) { -+ cf->do_code_blob(_event_data.compiled_method_load); -+ } // May add UNLOAD event but it doesn't work yet. -+} -+ - JvmtiDeferredEventQueue::QueueNode* JvmtiDeferredEventQueue::_queue_tail = NULL; - JvmtiDeferredEventQueue::QueueNode* JvmtiDeferredEventQueue::_queue_head = NULL; - -@@ -1084,3 +1094,15 @@ void JvmtiDeferredEventQueue::process_pending_events() { - } - } - } -+ -+void JvmtiDeferredEventQueue::oops_do(OopClosure* f, CodeBlobClosure* cf) { -+ for(QueueNode* node = _queue_head; node != NULL; node = node->next()) { -+ node->event().oops_do(f, cf); -+ } -+} -+ -+void JvmtiDeferredEventQueue::nmethods_do(CodeBlobClosure* cf) { -+ for(QueueNode* node = _queue_head; node != NULL; node = node->next()) { -+ node->event().nmethods_do(cf); -+ } -+} -diff --git a/hotspot/src/share/vm/prims/jvmtiImpl.hpp b/hotspot/src/share/vm/prims/jvmtiImpl.hpp -index 9f36f28fb..d74789451 100644 ---- a/hotspot/src/share/vm/prims/jvmtiImpl.hpp -+++ b/hotspot/src/share/vm/prims/jvmtiImpl.hpp -@@ -492,6 +492,10 @@ class JvmtiDeferredEvent VALUE_OBJ_CLASS_SPEC { - - // Actually posts the event. - void post() NOT_JVMTI_RETURN; -+ // Sweeper support to keep nmethods from being zombied while in the queue. -+ void nmethods_do(CodeBlobClosure* cf); -+ // GC support to keep nmethod from being unloaded while in the queue. -+ void oops_do(OopClosure* f, CodeBlobClosure* cf); - }; - - /** -@@ -511,7 +515,7 @@ class JvmtiDeferredEventQueue : AllStatic { - QueueNode(const JvmtiDeferredEvent& event) - : _event(event), _next(NULL) {} - -- const JvmtiDeferredEvent& event() const { return _event; } -+ JvmtiDeferredEvent& event() { return _event; } - QueueNode* next() const { return _next; } - - void set_next(QueueNode* next) { _next = next; } -@@ -529,6 +533,10 @@ class JvmtiDeferredEventQueue : AllStatic { - static bool has_events() NOT_JVMTI_RETURN_(false); - static void enqueue(const JvmtiDeferredEvent& event) NOT_JVMTI_RETURN; - static JvmtiDeferredEvent dequeue() NOT_JVMTI_RETURN_(JvmtiDeferredEvent()); -+ // Sweeper support to keep nmethods from being zombied while in the queue. -+ static void nmethods_do(CodeBlobClosure* cf); -+ // GC support to keep nmethod from being unloaded while in the queue. -+ static void oops_do(OopClosure* f, CodeBlobClosure* cf); - - // Used to enqueue events without using a lock, for times (such as during - // safepoint) when we can't or don't want to lock the Service_lock. -diff --git a/hotspot/src/share/vm/runtime/serviceThread.cpp b/hotspot/src/share/vm/runtime/serviceThread.cpp -index c3a2b88a5..a2a32ad2b 100644 ---- a/hotspot/src/share/vm/runtime/serviceThread.cpp -+++ b/hotspot/src/share/vm/runtime/serviceThread.cpp -@@ -34,6 +34,7 @@ - #include "services/diagnosticFramework.hpp" - - ServiceThread* ServiceThread::_instance = NULL; -+JvmtiDeferredEvent* ServiceThread::_jvmti_event = NULL; - - void ServiceThread::initialize() { - EXCEPTION_MARK; -@@ -112,12 +113,15 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) { - } - - if (has_jvmti_events) { -+ // Get the event under the Service_lock - jvmti_event = JvmtiDeferredEventQueue::dequeue(); -+ _jvmti_event = &jvmti_event; - } - } - - if (has_jvmti_events) { -- jvmti_event.post(); -+ _jvmti_event->post(); -+ _jvmti_event = NULL; // reset - } - - if (sensors_changed) { -@@ -138,6 +142,26 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) { - } - } - --bool ServiceThread::is_service_thread(Thread* thread) { -- return thread == _instance; -+void ServiceThread::oops_do(OopClosure* f, CLDClosure* cld_f, CodeBlobClosure* cf) { -+ JavaThread::oops_do(f, cld_f, cf); -+ // The ServiceThread "owns" the JVMTI Deferred events, scan them here -+ // to keep them alive until they are processed. -+ if (cf != NULL) { -+ if (_jvmti_event != NULL) { -+ _jvmti_event->oops_do(f, cf); -+ } -+ MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag); -+ JvmtiDeferredEventQueue::oops_do(f, cf); -+ } -+} -+ -+void ServiceThread::nmethods_do(CodeBlobClosure* cf) { -+ JavaThread::nmethods_do(cf); -+ if (cf != NULL) { -+ if (_jvmti_event != NULL) { -+ _jvmti_event->nmethods_do(cf); -+ } -+ MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag); -+ JvmtiDeferredEventQueue::nmethods_do(cf); -+ } - } -diff --git a/hotspot/src/share/vm/runtime/serviceThread.hpp b/hotspot/src/share/vm/runtime/serviceThread.hpp -index 42373e6f7..a9c219580 100644 ---- a/hotspot/src/share/vm/runtime/serviceThread.hpp -+++ b/hotspot/src/share/vm/runtime/serviceThread.hpp -@@ -29,11 +29,13 @@ - - // A JavaThread for low memory detection support and JVMTI - // compiled-method-load events. -+class JvmtiDeferredEvent; -+ - class ServiceThread : public JavaThread { - friend class VMStructs; - private: -- - static ServiceThread* _instance; -+ static JvmtiDeferredEvent* _jvmti_event; - - static void service_thread_entry(JavaThread* thread, TRAPS); - ServiceThread(ThreadFunction entry_point) : JavaThread(entry_point) {}; -@@ -43,9 +45,11 @@ class ServiceThread : public JavaThread { - - // Hide this thread from external view. - bool is_hidden_from_external_view() const { return true; } -+ bool is_service_thread() const { return true; } - -- // Returns true if the passed thread is the service thread. -- static bool is_service_thread(Thread* thread); -+ // GC support -+ void oops_do(OopClosure* f, CLDClosure* cld_f, CodeBlobClosure* cf); -+ void nmethods_do(CodeBlobClosure* cf); - }; - - #endif // SHARE_VM_RUNTIME_SERVICETHREAD_HPP -diff --git a/hotspot/src/share/vm/runtime/thread.hpp b/hotspot/src/share/vm/runtime/thread.hpp -index cc976182d..950c1b4fa 100644 ---- a/hotspot/src/share/vm/runtime/thread.hpp -+++ b/hotspot/src/share/vm/runtime/thread.hpp -@@ -313,6 +313,7 @@ class Thread: public ThreadShadow { - virtual bool is_VM_thread() const { return false; } - virtual bool is_Java_thread() const { return false; } - virtual bool is_Compiler_thread() const { return false; } -+ virtual bool is_service_thread() const { return false; } - virtual bool is_hidden_from_external_view() const { return false; } - virtual bool is_jvmti_agent_thread() const { return false; } - // True iff the thread can perform GC operations at a safepoint. --- -2.22.0 - diff --git a/8200332-Improve-GCM-counting.patch b/8200332-Improve-GCM-counting.patch new file mode 100644 index 0000000000000000000000000000000000000000..5990053ab1c82aab9e0c5f2a4136cb5450bf1a52 --- /dev/null +++ b/8200332-Improve-GCM-counting.patch @@ -0,0 +1,68 @@ +From 30883daeac796c877a765cedee52f27f51444203 Mon Sep 17 00:00:00 2001 +Date: Thu, 8 Sep 2022 10:22:32 +0800 +Subject: 8200332: Improve GCM counting + +Bug url: https://bugs.openjdk.org/browse/JDK-8200332 +--- + .../classes/com/sun/crypto/provider/GCTR.java | 31 ++++++++++++++++++- + 1 file changed, 30 insertions(+), 1 deletion(-) + +diff --git a/jdk/src/share/classes/com/sun/crypto/provider/GCTR.java b/jdk/src/share/classes/com/sun/crypto/provider/GCTR.java +index 6a394e448..1ab0f63db 100644 +--- a/jdk/src/share/classes/com/sun/crypto/provider/GCTR.java ++++ b/jdk/src/share/classes/com/sun/crypto/provider/GCTR.java +@@ -29,6 +29,8 @@ + + package com.sun.crypto.provider; + ++import java.nio.ByteBuffer; ++import java.nio.ByteOrder; + import javax.crypto.IllegalBlockSizeException; + import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE; + +@@ -68,6 +70,15 @@ final class GCTR extends CounterMode { + return "GCTR"; + } + ++ // return the number of blocks until the lower 32 bits roll over ++ private long blocksUntilRollover() { ++ ByteBuffer buf = ByteBuffer.wrap(counter, counter.length - 4, 4); ++ buf.order(ByteOrder.BIG_ENDIAN); ++ long ctr32 = 0xFFFFFFFFL & buf.getInt(); ++ long blocksLeft = (1L << 32) - ctr32; ++ return blocksLeft; ++ } ++ + // input must be multiples of 128-bit blocks when calling update + int update(byte[] in, int inOfs, int inLen, byte[] out, int outOfs) { + if (inLen - inOfs > in.length) { +@@ -80,7 +91,25 @@ final class GCTR extends CounterMode { + throw new RuntimeException("output buffer too small"); + } + +- return encrypt(in, inOfs, inLen, out, outOfs); ++ long blocksLeft = blocksUntilRollover(); ++ int numOfCompleteBlocks = inLen / AES_BLOCK_SIZE; ++ if (numOfCompleteBlocks >= blocksLeft) { ++ // Counter Mode encryption cannot be used because counter will ++ // roll over incorrectly. Use GCM-specific code instead. ++ byte[] encryptedCntr = new byte[AES_BLOCK_SIZE]; ++ for (int i = 0; i < numOfCompleteBlocks; i++) { ++ embeddedCipher.encryptBlock(counter, 0, encryptedCntr, 0); ++ for (int n = 0; n < AES_BLOCK_SIZE; n++) { ++ int index = (i * AES_BLOCK_SIZE + n); ++ out[outOfs + index] = ++ (byte) ((in[inOfs + index] ^ encryptedCntr[n])); ++ } ++ GaloisCounterMode.increment32(counter); ++ } ++ return inLen; ++ } else { ++ return encrypt(in, inOfs, inLen, out, outOfs); ++ } + } + + // input can be arbitrary size when calling doFinal +-- +2.22.0 + diff --git a/8202951-Support-default-jsa.patch b/8202951-Support-default-jsa.patch new file mode 100644 index 0000000000000000000000000000000000000000..0724ff1c97e131598afaf47223b5bafdff75b8fe --- /dev/null +++ b/8202951-Support-default-jsa.patch @@ -0,0 +1,1394 @@ +From 49f7ef8df4cade226de5754172e208975343967c Mon Sep 17 00:00:00 2001 +Date: Sat, 3 Sep 2022 14:25:50 +0000 +Subject: 8202951-Support-default-jsa + +--- + common/autoconf/configure.ac | 3 + + common/autoconf/generated-configure.sh | 40 +++++ + common/autoconf/jdk-options.m4 | 32 ++++ + common/autoconf/spec.gmk.in | 4 + + common/bin/compare.sh | 1 + + hotspot/src/share/vm/cds/archiveBuilder.cpp | 34 +++- + hotspot/src/share/vm/cds/archiveBuilder.hpp | 4 + + hotspot/src/share/vm/cds/dynamicArchive.cpp | 1 + + .../src/share/vm/classfile/classLoader.cpp | 152 +++++++++++++++++- + .../src/share/vm/classfile/classLoader.hpp | 15 +- + .../share/vm/classfile/classLoaderData.hpp | 1 + + hotspot/src/share/vm/classfile/dictionary.cpp | 2 +- + .../vm/classfile/sharedPathsMiscInfo.hpp | 13 +- + .../vm/classfile/systemDictionaryShared.cpp | 2 +- + hotspot/src/share/vm/memory/filemap.cpp | 34 +++- + hotspot/src/share/vm/memory/filemap.hpp | 7 + + hotspot/src/share/vm/memory/metachunk.hpp | 2 + + hotspot/src/share/vm/memory/metaspace.cpp | 14 ++ + hotspot/src/share/vm/memory/metaspace.hpp | 2 + + .../src/share/vm/memory/metaspaceShared.cpp | 22 +++ + .../src/share/vm/memory/metaspaceShared.hpp | 5 +- + hotspot/src/share/vm/oops/instanceKlass.cpp | 5 +- + hotspot/src/share/vm/runtime/arguments.cpp | 34 ++-- + hotspot/src/share/vm/runtime/arguments.hpp | 6 + + hotspot/src/share/vm/utilities/hashtable.cpp | 1 + + hotspot/test/runtime/appcds/TestCommon.java | 74 ++++++++- + .../appcds/dynamicArchive/DynamicFlag.java | 39 +++++ + .../dynamicArchive/DynamicHelloTest.java | 42 +++++ + .../VerifyWithDynamicArchive.java | 42 +++++ + jdk/make/BuildJdk.gmk | 7 + + 30 files changed, 603 insertions(+), 37 deletions(-) + create mode 100644 hotspot/test/runtime/appcds/dynamicArchive/DynamicFlag.java + create mode 100644 hotspot/test/runtime/appcds/dynamicArchive/DynamicHelloTest.java + create mode 100644 hotspot/test/runtime/appcds/dynamicArchive/VerifyWithDynamicArchive.java + +diff --git a/common/autoconf/configure.ac b/common/autoconf/configure.ac +index 151e5a10..dbcdd59e 100644 +--- a/common/autoconf/configure.ac ++++ b/common/autoconf/configure.ac +@@ -98,6 +98,9 @@ JDKOPT_SETUP_JVM_INTERPRETER + JDKOPT_SETUP_JVM_VARIANTS + JDKOPT_SETUP_DEBUG_LEVEL + ++# Enable default CDS ARCHIVE ++JDKOPT_ENABLE_DISABLE_CDS_ARCHIVE ++ + # With basic setup done, call the custom early hook. + CUSTOM_EARLY_HOOK + +diff --git a/common/autoconf/generated-configure.sh b/common/autoconf/generated-configure.sh +index c41c4336..f0e49f50 100644 +--- a/common/autoconf/generated-configure.sh ++++ b/common/autoconf/generated-configure.sh +@@ -883,6 +883,7 @@ OUTPUT_ROOT + CONF_NAME + SPEC + DEVKIT_LIB_DIR ++BUILD_CDS_ARCHIVE + BUILD_VARIANT_RELEASE + DEBUG_CLASSFILES + FASTDEBUG +@@ -1047,6 +1048,7 @@ with_jvm_interpreter + with_jvm_variants + enable_debug + with_debug_level ++enable_cds_archive + with_devkit + with_sys_root + with_sysroot +@@ -1857,6 +1859,8 @@ Optional Features: + [disabled] + --enable-debug set the debug level to fastdebug (shorthand for + --with-debug-level=fastdebug) [disabled] ++ --disable-cds-archive Set to disable generation of a default CDS archive ++ in the product image [enabled] + --disable-headful disable building headful support (graphical UI + support) [enabled] + --enable-hotspot-test-in-build +@@ -14704,6 +14708,42 @@ fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DEBUG_LEVEL" >&5 + $as_echo "$DEBUG_LEVEL" >&6; } + ++ ++ ++# Enable default CDS ARCHIVE ++ ++ # Check whether --enable-cds-archive was given. ++if test "${enable_cds_archive+set}" = set; then : ++ enableval=$enable_cds_archive; ++fi ++ ++ ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if a default CDS archive should be generated" >&5 ++$as_echo_n "checking if a default CDS archive should be generated... " >&6; } ++ if test "x$COMPILE_TYPE" = "xcross"; then ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, not possible with cross compilation" >&5 ++$as_echo "no, not possible with cross compilation" >&6; } ++ BUILD_CDS_ARCHIVE="false" ++ elif test "x$enable_cds_archive" = "xyes"; then ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes, forced" >&5 ++$as_echo "yes, forced" >&6; } ++ BUILD_CDS_ARCHIVE="true" ++ elif test "x$enable_cds_archive" = "x"; then ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 ++$as_echo "yes" >&6; } ++ BUILD_CDS_ARCHIVE="true" ++ elif test "x$enable_cds_archive" = "xno"; then ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, forced" >&5 ++$as_echo "no, forced" >&6; } ++ BUILD_CDS_ARCHIVE="false" ++ else ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } ++ as_fn_error $? "--enable-cds_archive can only be yes/no or empty" "$LINENO" 5 ++ fi ++ ++ ++ + if test "x$DEBUG_LEVEL" != xrelease && \ + test "x$DEBUG_LEVEL" != xfastdebug && \ + test "x$DEBUG_LEVEL" != xslowdebug; then +diff --git a/common/autoconf/jdk-options.m4 b/common/autoconf/jdk-options.m4 +index bca78afe..c506086d 100644 +--- a/common/autoconf/jdk-options.m4 ++++ b/common/autoconf/jdk-options.m4 +@@ -789,6 +789,38 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_DEBUG_SYMBOLS], + AC_SUBST(ZIP_DEBUGINFO_FILES) + ]) + ++################################################################################ ++# ++# Disable the default CDS archive generation ++# cross compilation - disabled ++# ++AC_DEFUN_ONCE([JDKOPT_ENABLE_DISABLE_CDS_ARCHIVE], ++[ ++ AC_ARG_ENABLE([cds-archive], [AS_HELP_STRING([--disable-cds-archive], ++ [Set to disable generation of a default CDS archive in the product image @<:@enabled@:>@])]) ++ ++ AC_MSG_CHECKING([if a default CDS archive should be generated]) ++ if test "x$COMPILE_TYPE" = "xcross"; then ++ AC_MSG_RESULT([no, not possible with cross compilation]) ++ BUILD_CDS_ARCHIVE="false" ++ elif test "x$enable_cds_archive" = "xyes"; then ++ AC_MSG_RESULT([yes, forced]) ++ BUILD_CDS_ARCHIVE="true" ++ elif test "x$enable_cds_archive" = "x"; then ++ AC_MSG_RESULT([yes]) ++ BUILD_CDS_ARCHIVE="true" ++ elif test "x$enable_cds_archive" = "xno"; then ++ AC_MSG_RESULT([no, forced]) ++ BUILD_CDS_ARCHIVE="false" ++ else ++ AC_MSG_RESULT([no]) ++ AC_MSG_ERROR([--enable-cds_archive can only be yes/no or empty]) ++ fi ++ ++ AC_SUBST(BUILD_CDS_ARCHIVE) ++]) ++ ++ + # Support for customization of the build process. Some build files + # will include counterparts from this location, if they exist. This allows + # for a degree of customization of the build targets and the rules/recipes +diff --git a/common/autoconf/spec.gmk.in b/common/autoconf/spec.gmk.in +index 4c3a9f61..79248cbf 100644 +--- a/common/autoconf/spec.gmk.in ++++ b/common/autoconf/spec.gmk.in +@@ -611,6 +611,10 @@ LIBZIP_CAN_USE_MMAP:=@LIBZIP_CAN_USE_MMAP@ + MSVCR_DLL:=@MSVCR_DLL@ + MSVCP_DLL:=@MSVCP_DLL@ + UCRT_DLL_DIR:=@UCRT_DLL_DIR@ ++# CDS_ARCHIVE ++BUILD_CDS_ARCHIVE:=@BUILD_CDS_ARCHIVE@ ++ ++ + + + # ADD_SRCS takes a single argument with source roots +diff --git a/common/bin/compare.sh b/common/bin/compare.sh +index ff88bb1f..a36464a9 100644 +--- a/common/bin/compare.sh ++++ b/common/bin/compare.sh +@@ -290,6 +290,7 @@ compare_general_files() { + ! -name "ct.sym" ! -name "*.diz" ! -name "*.dll" \ + ! -name "*.pdb" ! -name "*.exp" ! -name "*.ilk" \ + ! -name "*.lib" ! -name "*.war" ! -name "JavaControlPanel" \ ++ ! -name "classes.jsa" \ + | $GREP -v "./bin/" | $SORT | $FILTER) + + echo General files... +diff --git a/hotspot/src/share/vm/cds/archiveBuilder.cpp b/hotspot/src/share/vm/cds/archiveBuilder.cpp +index 144dedfa..13a62002 100644 +--- a/hotspot/src/share/vm/cds/archiveBuilder.cpp ++++ b/hotspot/src/share/vm/cds/archiveBuilder.cpp +@@ -59,6 +59,18 @@ ArchiveBuilder::SourceObjList::~SourceObjList() { + delete _objs; + } + ++static void caculate_fingerprint(Klass * klass) { ++ if (klass->oop_is_instance()) { ++ InstanceKlass* ik = InstanceKlass::cast(klass); ++ for (int i = 0; i < ik->methods()->length(); i++) { ++ Method* m = ik->methods()->at(i); ++ Fingerprinter fp(m); ++ // The side effect of this call sets method's fingerprint field. ++ fp.fingerprint(); ++ } ++ } ++} ++ + void ArchiveBuilder::SourceObjList::append(MetaspaceClosure::Ref* enclosing_ref, SourceObjInfo* src_info) { + // Save this source object for copying + _objs->append(src_info); +@@ -166,6 +178,7 @@ ArchiveBuilder::ArchiveBuilder() : + _buffer_to_requested_delta(0), + _rw_region("rw", MAX_SHARED_DELTA), + _ro_region("ro", MAX_SHARED_DELTA), ++ _md_region("md", MAX_SHARED_DELTA), + _rw_src_objs(), + _ro_src_objs(), + _src_obj_table(INITIAL_TABLE_SIZE), +@@ -384,6 +397,7 @@ bool ArchiveBuilder::gather_klass_and_symbol(MetaspaceClosure::Ref* ref, bool re + Klass* klass = (Klass*)ref->obj(); + assert(klass->is_klass(), "must be"); + if (!is_excluded(klass)) { ++ caculate_fingerprint(klass); + _klasses->append(klass); + if (klass->oop_is_instance()) { + _num_instance_klasses ++; +@@ -434,7 +448,8 @@ size_t ArchiveBuilder::estimate_archive_size() { + + address ArchiveBuilder::reserve_buffer() { + size_t buffer_size = estimate_archive_size(); +- ReservedSpace rs(buffer_size, os::vm_allocation_granularity(), false); ++ size_t package_hash_table_est = align_up(ClassLoader::estimate_size_for_archive(), (size_t)os::vm_allocation_granularity()); ++ ReservedSpace rs(buffer_size + package_hash_table_est, os::vm_allocation_granularity(), false); + if (!rs.is_reserved()) { + tty->print_cr("Failed to reserve " SIZE_FORMAT " bytes of output buffer.", buffer_size); + vm_direct_exit(0); +@@ -443,7 +458,8 @@ address ArchiveBuilder::reserve_buffer() { + // buffer_bottom is the lowest address of the 2 core regions (rw, ro) when + // we are copying the class metadata into the buffer. + address buffer_bottom = (address)rs.base(); +- _shared_rs = rs; ++ _shared_rs = rs.first_part(buffer_size); ++ _md_rs = rs.last_part(buffer_size); + + _buffer_bottom = buffer_bottom; + _last_verified_top = buffer_bottom; +@@ -508,6 +524,19 @@ void ArchiveBuilder::dump_ro_metadata() { + make_shallow_copies(&_ro_region, &_ro_src_objs); + } + ++void ArchiveBuilder::dump_md_metadata() { ++ ResourceMark rm; ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Allocating MD objects ... "); ++ } ++ _current_dump_space = &_md_region; ++ _md_region.init(&_md_rs, &_md_vs); ++ char* md_top = _md_vs.low(); ++ char* md_end = _md_vs.high_boundary(); ++ _md_region.allocate(md_end - md_top); ++ ClassLoader::serialize_package_hash_table(&md_top, md_end); ++} ++ + void ArchiveBuilder::start_dump_space(DumpRegion* next) { + address bottom = _last_verified_top; + address top = (address)(_current_dump_space->top()); +@@ -749,6 +778,7 @@ void ArchiveBuilder::write_archive(FileMapInfo* mapinfo) { + + write_region(mapinfo, MetaspaceShared::d_rw, &_rw_region, /*read_only=*/false,/*allow_exec=*/false); + write_region(mapinfo, MetaspaceShared::d_ro, &_ro_region, /*read_only=*/true, /*allow_exec=*/false); ++ write_region(mapinfo, MetaspaceShared::d_md, &_md_region, /*read_only=*/true, /*allow_exec=*/false); + + char* bitmap = mapinfo->write_bitmap_region(ArchivePtrMarker::ptrmap()); + +diff --git a/hotspot/src/share/vm/cds/archiveBuilder.hpp b/hotspot/src/share/vm/cds/archiveBuilder.hpp +index 18cd3c62..f7a5c107 100644 +--- a/hotspot/src/share/vm/cds/archiveBuilder.hpp ++++ b/hotspot/src/share/vm/cds/archiveBuilder.hpp +@@ -163,10 +163,13 @@ private: + static const int MAX_TABLE_SIZE = 1000000; + + ReservedSpace _shared_rs; ++ ReservedSpace _md_rs; + VirtualSpace _shared_vs; ++ VirtualSpace _md_vs; + + DumpRegion _rw_region; + DumpRegion _ro_region; ++ DumpRegion _md_region; + BitMap _ptrmap; + + SourceObjList _rw_src_objs; // objs to put in rw region +@@ -327,6 +330,7 @@ public: + + void dump_rw_metadata(); + void dump_ro_metadata(); ++ void dump_md_metadata(); + void relocate_metaspaceobj_embedded_pointers(); + void relocate_roots(); + void make_klasses_shareable(); +diff --git a/hotspot/src/share/vm/cds/dynamicArchive.cpp b/hotspot/src/share/vm/cds/dynamicArchive.cpp +index efed275c..a623c5b0 100644 +--- a/hotspot/src/share/vm/cds/dynamicArchive.cpp ++++ b/hotspot/src/share/vm/cds/dynamicArchive.cpp +@@ -149,6 +149,7 @@ public: + + relocate_to_requested(); + ++ dump_md_metadata(); + write_archive(serialized_data); + release_header(); + +diff --git a/hotspot/src/share/vm/classfile/classLoader.cpp b/hotspot/src/share/vm/classfile/classLoader.cpp +index e3470ca8..04fa84d4 100644 +--- a/hotspot/src/share/vm/classfile/classLoader.cpp ++++ b/hotspot/src/share/vm/classfile/classLoader.cpp +@@ -219,6 +219,30 @@ const char* ClassLoader::package_from_name(const char* const class_name, bool* b + return (const char *)pkg_name; + } + ++const char* ClassLoader::get_file_name_from_path(const char* path) { ++ const char* pos = strrchr(path, '/'); ++ if (pos == NULL) { ++ return path; ++ } else { ++ return pos + 1; ++ } ++} ++ ++const char* ClassLoader::get_boot_class_path(const char* shared_path) { ++ const char* shared_name = get_file_name_from_path(shared_path); ++ ClassPathEntry* e = _first_entry; ++ while (e != NULL) { ++ if (e->sys_class()) { ++ const char* name = get_file_name_from_path(e->name()); ++ if (strcmp(name, shared_name) == 0) { ++ return e->name(); ++ } ++ } ++ e = e->next(); ++ } ++ return NULL; ++} ++ + MetaIndex::MetaIndex(char** meta_package_names, int num_meta_package_names) { + if (num_meta_package_names == 0) { + _meta_package_names = NULL; +@@ -512,6 +536,8 @@ void ClassLoader::setup_meta_index(const char* meta_index_path, const char* meta + int line_no = 0; + #if INCLUDE_CDS + if (DumpSharedSpaces) { ++ meta_index_path = Arguments::get_is_default_jsa() ? ++ get_file_name_from_path(meta_index_path) : meta_index_path; + if (file != NULL) { + _shared_paths_misc_info->add_required_file(meta_index_path); + } else { +@@ -644,7 +670,9 @@ void ClassLoader::setup_bootstrap_search_path() { + } + #if INCLUDE_CDS + if (DumpSharedSpaces) { +- _shared_paths_misc_info->add_boot_classpath(sys_class_path); ++ const char* new_sys_class_path = Arguments::get_is_default_jsa() ? ++ get_file_name_from_path(sys_class_path) : sys_class_path; ++ _shared_paths_misc_info->add_boot_classpath(new_sys_class_path); + } + #endif + setup_search_path(sys_class_path); +@@ -688,7 +716,7 @@ void ClassLoader::setup_search_path(const char *class_path, bool canonicalize) { + path = canonical_path; + } + } +- update_class_path_entry_list(path, /*check_for_duplicates=*/canonicalize); ++ update_class_path_entry_list(path, /*check_for_duplicates=*/canonicalize, true, true); + #if INCLUDE_CDS + if (DumpSharedSpaces) { + check_shared_classpath(path); +@@ -816,7 +844,9 @@ void ClassLoader::add_to_list(ClassPathEntry *new_entry) { + // Returns true IFF the file/dir exists and the entry was successfully created. + bool ClassLoader::update_class_path_entry_list(const char *path, + bool check_for_duplicates, +- bool throw_exception) { ++ bool throw_exception, ++ bool sys_class_type) { ++ // sys_class_type indicates whether *path is a system path. The default value is false. + struct stat st; + if (os::stat(path, &st) == 0) { + // File or directory found +@@ -826,6 +856,11 @@ bool ClassLoader::update_class_path_entry_list(const char *path, + if (new_entry == NULL) { + return false; + } ++ // If the path is a system path, set sys_class of the newly created ++ // linked list node to true. The default value is false. ++ if (sys_class_type) { ++ new_entry->set_sys_class(true); ++ } + // The kernel VM adds dynamically to the end of the classloader path and + // doesn't reorder the bootclasspath which would break java.lang.Package + // (see PackageInfo). +@@ -837,6 +872,8 @@ bool ClassLoader::update_class_path_entry_list(const char *path, + } else { + #if INCLUDE_CDS + if (DumpSharedSpaces) { ++ path = Arguments::get_is_default_jsa() ? ++ get_file_name_from_path(path) : path; + _shared_paths_misc_info->add_nonexist_path(path); + } + #endif +@@ -918,6 +955,7 @@ int ClassLoader::crc32(int crc, const char* buf, int len) { + class PackageInfo: public BasicHashtableEntry { + public: + const char* _pkgname; // Package name ++ const char* _filename; // File name + int _classpath_index; // Index of directory or JAR file loaded from + + PackageInfo* next() { +@@ -926,9 +964,10 @@ public: + + const char* pkgname() { return _pkgname; } + void set_pkgname(char* pkgname) { _pkgname = pkgname; } ++ void set_filename(char* filename) { _filename = filename; } + + const char* filename() { +- return ClassLoader::classpath_entry(_classpath_index)->name(); ++ return _filename == NULL ? ClassLoader::classpath_entry(_classpath_index)->name() : _filename; + } + + void set_index(int index) { +@@ -975,11 +1014,12 @@ public: + return get_entry(hash_to_index(hash), hash, pkgname, n); + } + +- PackageInfo* new_entry(char* pkgname, int n) { ++ PackageInfo* new_entry(char* pkgname, int n, char* filename = NULL) { + unsigned int hash = compute_hash(pkgname, n); + PackageInfo* pp; + pp = (PackageInfo*)BasicHashtable::new_entry(hash); + pp->set_pkgname(pkgname); ++ pp->set_filename(filename); + return pp; + } + +@@ -999,6 +1039,9 @@ public: + } + + CDS_ONLY(void copy_table(char** top, char* end, PackageHashtable* table);) ++ CDS_ONLY(void serialize(char** top, char* end);) ++ CDS_ONLY(void deserialize(char* start);) ++ CDS_ONLY(size_t estimate_size();) + }; + + #if INCLUDE_CDS +@@ -1035,6 +1078,93 @@ void PackageHashtable::copy_table(char** top, char* end, + *tableSize = len; + } + ++size_t PackageHashtable::estimate_size() { ++ int size = sizeof(int); ++ ClassPathEntry* e = ClassLoader::_first_entry; ++ while (e != NULL) { ++ int length = (int)(strlen(e->name()) + 1); ++ size += length; ++ e = e->next(); ++ } ++ size = align_size_up(size, sizeof(int)); ++ ++ size += sizeof(int); ++ for (int i = 0; i < table_size(); ++i) { ++ for (PackageInfo* pp = bucket(i); ++ pp != NULL; ++ pp = pp->next()) { ++ size += sizeof(int); ++ int n1 = (int)(strlen(pp->pkgname()) + 1); ++ n1 = align_size_up(n1, sizeof(int)); ++ size += n1; ++ } ++ } ++ return align_size_up(size, sizeof(int)); ++} ++ ++void PackageHashtable::serialize(char** top, char* end) { ++ *(int*)(*top) = ClassLoader::_num_entries; ++ *top += sizeof(int); ++ ++ ClassPathEntry* e = ClassLoader::_first_entry; ++ while (e != NULL) { ++ int length = (int)(strlen(e->name()) + 1); ++ memcpy(*top, e->name(), length); ++ *top += length; ++ e = e->next(); ++ } ++ *top = (char*)align_size_up((intptr_t)*top, sizeof(int)); ++ *(int*)(*top) = number_of_entries(); ++ *top += sizeof(int); ++ ++ for (int i = 0; i < table_size(); ++i) { ++ for (PackageInfo* pp = bucket(i); ++ pp != NULL; ++ pp = pp->next()) { ++ *(int*)(*top) = pp->_classpath_index; ++ *top += sizeof(int); ++ int n1 = (int)(strlen(pp->pkgname()) + 1); ++ memcpy(*top, pp->pkgname(), n1); ++ n1 = align_size_up(n1, sizeof(int)); ++ *top += n1; ++ } ++ } ++} ++ ++void PackageHashtable::deserialize(char* start) { ++ int num_entries = *(int*)start; ++ char** class_loader_entries = NEW_C_HEAP_ARRAY(char*, num_entries, mtClass); ++ start += sizeof(int); ++ int entries_len = 0; ++ for (int i = 0, index = 0; i < num_entries; i++) { ++ class_loader_entries[index++] = start + entries_len; ++ entries_len += (int)(strlen(start + entries_len) + 1); ++ } ++ start += align_size_up(entries_len, sizeof(int)); ++ int number_of_entries = *(int*)start; ++ start += sizeof(int); ++ for (int i = 0; i < number_of_entries; i++) { ++ int classpath_index = *(int*)start; ++ start += sizeof(int); ++ char* pkgname = start; ++ const char *cp = strrchr(pkgname, '/'); ++ if (cp != NULL) { ++ int n = cp - pkgname + 1; ++ if (get_entry(pkgname, n) == NULL) { ++ PackageInfo* info = new_entry(pkgname, n, class_loader_entries[classpath_index]); ++ add_entry(info); ++ } ++ } ++ int n1 = (int)(strlen(start) + 1); ++ start += align_size_up(n1, sizeof(int)); ++ } ++ FREE_C_HEAP_ARRAY(char*, class_loader_entries, mtClass); ++} ++ ++void ClassLoader::deserialize_package_hash_table(char* start) { ++ assert(_package_hash_table != NULL, "should have one yet"); ++ _package_hash_table->deserialize(start); ++} + + void ClassLoader::copy_package_info_buckets(char** top, char* end) { + _package_hash_table->copy_buckets(top, end); +@@ -1043,6 +1173,14 @@ void ClassLoader::copy_package_info_buckets(char** top, char* end) { + void ClassLoader::copy_package_info_table(char** top, char* end) { + _package_hash_table->copy_table(top, end, _package_hash_table); + } ++ ++size_t ClassLoader::estimate_size_for_archive() { ++ return _package_hash_table->estimate_size(); ++} ++ ++void ClassLoader::serialize_package_hash_table(char** top, char* end) { ++ return _package_hash_table->serialize(top, end); ++} + #endif + + PackageInfo* ClassLoader::lookup_package(const char *pkgname) { +@@ -1226,8 +1364,8 @@ void ClassLoader::create_package_info_table(HashtableBucket *t, int len + + + void ClassLoader::create_package_info_table() { +- assert(_package_hash_table == NULL, "shouldn't have one yet"); +- _package_hash_table = new PackageHashtable(package_hash_table_size); ++ assert(_package_hash_table == NULL, "shouldn't have one yet"); ++ _package_hash_table = new PackageHashtable(package_hash_table_size); + } + + +diff --git a/hotspot/src/share/vm/classfile/classLoader.hpp b/hotspot/src/share/vm/classfile/classLoader.hpp +index 9514d3bb..cf39ce99 100644 +--- a/hotspot/src/share/vm/classfile/classLoader.hpp ++++ b/hotspot/src/share/vm/classfile/classLoader.hpp +@@ -49,13 +49,18 @@ class MetaIndex: public CHeapObj { + class ClassPathEntry: public CHeapObj { + private: + ClassPathEntry* _next; ++ bool _sys_class; + public: + // Next entry in class path + ClassPathEntry* next() { return _next; } ++ bool sys_class() const { return _sys_class; } + void set_next(ClassPathEntry* next) { + // may have unlocked readers, so write atomically. + OrderAccess::release_store_ptr(&_next, next); + } ++ void set_sys_class(bool isSysClass) { ++ _sys_class = isSysClass; ++ } + virtual bool is_jar_file() = 0; + virtual const char* name() = 0; + virtual bool is_lazy(); +@@ -158,6 +163,7 @@ class ClassLoader: AllStatic { + }; + protected: + friend class LazyClassPathEntry; ++ friend class PackageHashtable; + + // Performance counters + static PerfCounter* _perf_accumulated_time; +@@ -234,7 +240,8 @@ class ClassLoader: AllStatic { + static int crc32(int crc, const char* buf, int len); + static bool update_class_path_entry_list(const char *path, + bool check_for_duplicates, +- bool throw_exception=true); ++ bool throw_exception=true, ++ bool sys_class=false); + static void print_bootclasspath(); + + // Timing +@@ -318,6 +325,9 @@ class ClassLoader: AllStatic { + // Initialization + static void initialize(); + CDS_ONLY(static void initialize_shared_path();) ++ static const char* get_file_name_from_path(const char* path); ++ static const char* get_boot_class_path(const char* shared_path); ++ + static void create_package_info_table(); + static void create_package_info_table(HashtableBucket *t, int length, + int number_of_entries); +@@ -340,6 +350,9 @@ class ClassLoader: AllStatic { + // Sharing dump and restore + static void copy_package_info_buckets(char** top, char* end); + static void copy_package_info_table(char** top, char* end); ++ static size_t estimate_size_for_archive(); ++ static void serialize_package_hash_table(char** top, char* end); ++ static void deserialize_package_hash_table(char* start); + + static void check_shared_classpath(const char *path); + static void finalize_shared_paths_misc_info(); +diff --git a/hotspot/src/share/vm/classfile/classLoaderData.hpp b/hotspot/src/share/vm/classfile/classLoaderData.hpp +index 9b901303..7155257e 100644 +--- a/hotspot/src/share/vm/classfile/classLoaderData.hpp ++++ b/hotspot/src/share/vm/classfile/classLoaderData.hpp +@@ -168,6 +168,7 @@ class ClassLoaderData : public CHeapObj { + friend class ClassLoaderDataGraphMetaspaceIterator; + friend class MetaDataFactory; + friend class Method; ++ friend class VM_PopulateDumpSharedSpace; + + static ClassLoaderData * _the_null_class_loader_data; + +diff --git a/hotspot/src/share/vm/classfile/dictionary.cpp b/hotspot/src/share/vm/classfile/dictionary.cpp +index b9d473b0..d41372ec 100644 +--- a/hotspot/src/share/vm/classfile/dictionary.cpp ++++ b/hotspot/src/share/vm/classfile/dictionary.cpp +@@ -197,7 +197,7 @@ void Dictionary::roots_oops_do(OopClosure* strong, OopClosure* weak) { + } + + void Dictionary::remove_classes_in_error_state() { +- assert(DumpSharedSpaces, "supported only when dumping"); ++ assert(DynamicDumpSharedSpaces || DumpSharedSpaces, "supported only when dumping"); + DictionaryEntry* probe = NULL; + for (int index = 0; index < table_size(); index++) { + for (DictionaryEntry** p = bucket_addr(index); *p != NULL; ) { +diff --git a/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.hpp b/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.hpp +index 882fed01..b1609e46 100644 +--- a/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.hpp ++++ b/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.hpp +@@ -26,6 +26,7 @@ + #define SHARE_VM_CLASSFILE_SHAREDPATHSMISCINFO_HPP + + #include "runtime/os.hpp" ++#include "runtime/arguments.hpp" + + // During dumping time, when processing class paths, we build up the dump-time + // classpath. The JAR files that exist are stored in the list ClassLoader::_first_entry. +@@ -111,12 +112,18 @@ public: + add_path(path, REQUIRED); + + struct stat st; +- if (os::stat(path, &st) != 0) { ++ if (!Arguments::get_is_default_jsa() && os::stat(path, &st) != 0) { + assert(0, "sanity"); + ClassLoader::exit_with_path_failure("failed to os::stat(%s)", path); // should not happen + } +- write_time(st.st_mtime); +- write_long(st.st_size); ++ ++ if (Arguments::get_is_default_jsa()) { ++ write_time(0); ++ write_long(0); ++ } else { ++ write_time(st.st_mtime); ++ write_long(st.st_size); ++ } + } + + // The path must exist, and must contain exactly files/dirs +diff --git a/hotspot/src/share/vm/classfile/systemDictionaryShared.cpp b/hotspot/src/share/vm/classfile/systemDictionaryShared.cpp +index 99354cd4..3a601ee3 100644 +--- a/hotspot/src/share/vm/classfile/systemDictionaryShared.cpp ++++ b/hotspot/src/share/vm/classfile/systemDictionaryShared.cpp +@@ -659,7 +659,7 @@ bool SystemDictionaryShared::warn_excluded(InstanceKlass* k, const char* reason) + + bool SystemDictionaryShared::is_jfr_event_class(InstanceKlass *k) { + while (k) { +- if (k->name()->equals("jdk/jfr/Event")) { ++ if (k->name()->equals("jdk/jfr/Event") || k->name()->starts_with("jdk/jfr/event")) { + return true; + } + k = k->java_super(); +diff --git a/hotspot/src/share/vm/memory/filemap.cpp b/hotspot/src/share/vm/memory/filemap.cpp +index 3f410647..5fd62a74 100644 +--- a/hotspot/src/share/vm/memory/filemap.cpp ++++ b/hotspot/src/share/vm/memory/filemap.cpp +@@ -263,7 +263,12 @@ void FileMapInfo::allocate_classpath_entry_table() { + + for (int cur_entry = 0 ; cpe != NULL; cpe = cpe->next(), cur_entry++) { + const char *name = cpe->name(); +- int name_bytes = (int)(strlen(name) + 1); ++ int name_bytes; ++ if (cpe->sys_class()) { ++ name_bytes = (int)(strlen(ClassLoader::get_file_name_from_path(name)) + 1); ++ } else { ++ name_bytes = (int)(strlen(name) + 1); ++ } + + if (pass == 0) { + count ++; +@@ -286,7 +291,13 @@ void FileMapInfo::allocate_classpath_entry_table() { + } + + EXCEPTION_MARK; // The following call should never throw, but would exit VM on error. +- SharedClassUtil::update_shared_classpath(cpe, ent, st.st_mtime, st.st_size, THREAD); ++ if (cpe->sys_class()) { ++ // Jdk boot jar not need validate timestamp for we may copy whole jdk. ++ SharedClassUtil::update_shared_classpath(cpe, ent, 0, st.st_size, THREAD); ++ ent->set_sys_class(true); ++ } else { ++ SharedClassUtil::update_shared_classpath(cpe, ent, st.st_mtime, st.st_size, THREAD); ++ } + } else { + ent->_filesize = -1; + if (!os::dir_is_empty(name)) { +@@ -295,7 +306,11 @@ void FileMapInfo::allocate_classpath_entry_table() { + } + ent->_name = strptr; + if (strptr + name_bytes <= strptr_max) { +- strncpy(strptr, name, (size_t)name_bytes); // name_bytes includes trailing 0. ++ if (cpe->sys_class()) { ++ strncpy(strptr, ClassLoader::get_file_name_from_path(name), (size_t)name_bytes); ++ } else { ++ strncpy(strptr, name, (size_t)name_bytes); // name_bytes includes trailing 0. ++ } + strptr += name_bytes; + } else { + assert(0, "miscalculated buffer size"); +@@ -334,6 +349,14 @@ bool FileMapInfo::validate_classpath_entry_table() { + if (TraceClassPaths || (TraceClassLoading && Verbose)) { + tty->print_cr("[Checking shared classpath entry: %s]", name); + } ++ if (ent->_sys_class) { ++ name = ClassLoader::get_boot_class_path(name); ++ if (name == NULL) { ++ fail_continue("Required classpath entry of system class does not exist"); ++ continue; ++ } ++ } ++ + if (os::stat(name, &st) != 0) { + fail_continue("Required classpath entry does not exist: %s", name); + ok = false; +@@ -343,7 +366,7 @@ bool FileMapInfo::validate_classpath_entry_table() { + ok = false; + } + } else { +- if (ent->_timestamp != st.st_mtime || ++ if ((ent->_timestamp != 0 && ent->_timestamp != st.st_mtime) || + ent->_filesize != st.st_size) { + ok = false; + if (PrintSharedArchiveAndExit) { +@@ -640,6 +663,7 @@ void FileMapInfo::write_space(int i, Metaspace* space, bool read_only) { + size_t used = space->used_bytes_slow(Metaspace::NonClassType); + size_t capacity = space->capacity_bytes_slow(Metaspace::NonClassType); + struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[i]; ++ space->reset_metachunks(); + write_region(i, (char*)space->bottom(), used, capacity, read_only, false); + } + +@@ -967,7 +991,7 @@ bool FileMapInfo::validate_header() { + return DynamicArchive::validate(this); + } + +- if (status) { ++ if (status && !_header->_is_default_jsa) { + if (!ClassLoader::check_shared_paths_misc_info(_paths_misc_info, _header->_paths_misc_info_size)) { + if (!PrintSharedArchiveAndExit) { + fail_continue("shared class paths mismatch (hint: enable -XX:+TraceClassPaths to diagnose the failure)"); +diff --git a/hotspot/src/share/vm/memory/filemap.hpp b/hotspot/src/share/vm/memory/filemap.hpp +index eab9ebcf..36b27f13 100644 +--- a/hotspot/src/share/vm/memory/filemap.hpp ++++ b/hotspot/src/share/vm/memory/filemap.hpp +@@ -52,9 +52,13 @@ public: + const char *_name; + time_t _timestamp; // jar timestamp, 0 if is directory + long _filesize; // jar file size, -1 if is directory ++ bool _sys_class; + bool is_dir() { + return _filesize == -1; + } ++ void set_sys_class(bool isSysClass) { ++ _sys_class = isSysClass; ++ } + }; + + class FileMapInfo : public CHeapObj { +@@ -100,6 +104,7 @@ public: + int _version; // (from enum, above.) + size_t _alignment; // how shared archive should be aligned + int _obj_alignment; // value of ObjectAlignmentInBytes ++ bool _is_default_jsa; // indicates whether is the default jsa file + + struct space_info { + int _crc; // crc checksum of the current space +@@ -264,6 +269,8 @@ public: + bool is_open() { return _file_open; } + bool is_static() const { return _is_static; } + bool is_mapped() const { return _is_mapped; } ++ bool is_default_jsa() const { return _header->_is_default_jsa; } ++ void set_is_default_jsa(bool v) { _header->_is_default_jsa = v; } + void set_is_mapped(bool v) { _is_mapped = v; } + ReservedSpace reserve_shared_memory(); + void set_requested_base(char* b) { dynamic_header()->set_requested_base(b); } +diff --git a/hotspot/src/share/vm/memory/metachunk.hpp b/hotspot/src/share/vm/memory/metachunk.hpp +index e873dc6a..7889b622 100644 +--- a/hotspot/src/share/vm/memory/metachunk.hpp ++++ b/hotspot/src/share/vm/memory/metachunk.hpp +@@ -126,6 +126,8 @@ class Metachunk : public Metabase { + + VirtualSpaceNode* container() const { return _container; } + ++ void reset_container() { _container = NULL; } ++ + MetaWord* bottom() const { return (MetaWord*) this; } + + // Reset top to bottom so chunk can be reused. +diff --git a/hotspot/src/share/vm/memory/metaspace.cpp b/hotspot/src/share/vm/memory/metaspace.cpp +index 7e95b5c0..6c4654b2 100644 +--- a/hotspot/src/share/vm/memory/metaspace.cpp ++++ b/hotspot/src/share/vm/memory/metaspace.cpp +@@ -775,6 +775,7 @@ class SpaceManager : public CHeapObj { + // Notify memory usage to MemoryService. + void track_metaspace_memory_usage(); + ++ void reset_metachunks(); + // debugging support. + + void dump(outputStream* const out) const; +@@ -1923,6 +1924,15 @@ void ChunkManager::print_on(outputStream* out) const { + + // SpaceManager methods + ++void SpaceManager::reset_metachunks() { ++ for (ChunkIndex i = ZeroIndex; i <= HumongousIndex; i = next_chunk_index(i)) { ++ Metachunk* chunks = chunks_in_use(i); ++ if (chunks != NULL) { ++ chunks->reset_container(); ++ } ++ } ++} ++ + size_t SpaceManager::adjust_initial_chunk_size(size_t requested, bool is_class_space) { + size_t chunk_sizes[] = { + specialized_chunk_size(is_class_space), +@@ -3002,6 +3012,10 @@ Metaspace::~Metaspace() { + } + } + ++void Metaspace::reset_metachunks() { ++ vsm()->reset_metachunks(); ++} ++ + VirtualSpaceList* Metaspace::_space_list = NULL; + VirtualSpaceList* Metaspace::_class_space_list = NULL; + +diff --git a/hotspot/src/share/vm/memory/metaspace.hpp b/hotspot/src/share/vm/memory/metaspace.hpp +index 2b06cb62..122dd4bf 100644 +--- a/hotspot/src/share/vm/memory/metaspace.hpp ++++ b/hotspot/src/share/vm/memory/metaspace.hpp +@@ -243,6 +243,8 @@ class Metaspace : public CHeapObj { + MetaWord* expand_and_allocate(size_t size, + MetadataType mdtype); + ++ void reset_metachunks(); ++ + static bool contains(const void* ptr); + + void dump(outputStream* const out) const; +diff --git a/hotspot/src/share/vm/memory/metaspaceShared.cpp b/hotspot/src/share/vm/memory/metaspaceShared.cpp +index 00fb9fe9..b31d0a3f 100644 +--- a/hotspot/src/share/vm/memory/metaspaceShared.cpp ++++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp +@@ -205,6 +205,21 @@ static void patch_klass_vtables(void** vtbl_list, void* new_vtable_start) { + } + } + ++static void patch_deallocate_meta_vtables(void** vtbl_list, void* new_vtable_start, GrowableArray* deallocate_list) { ++ if (deallocate_list == NULL) { ++ return; ++ } ++ for (int i = deallocate_list->length() - 1; i >= 0; i--) { ++ Metadata* m = deallocate_list->at(i); ++ if (!m->on_stack()) { ++ if (m->is_constantPool()) { ++ ((ConstantPool*)m)->remove_unshareable_info(); ++ *(void**)m = find_matching_vtbl_ptr(vtbl_list, new_vtable_start, m); ++ } ++ } ++ } ++} ++ + // Closure for serializing initialization data out to a data area to be + // written to the shared file. + +@@ -591,6 +606,7 @@ void VM_PopulateDumpSharedSpace::doit() { + // Update the vtable pointers in all of the Klass objects in the + // heap. They should point to newly generated vtable. + patch_klass_vtables(vtbl_list, vtable); ++ patch_deallocate_meta_vtables(vtbl_list, vtable, _loader_data->_deallocate_list); + + // dunno what this is for. + char* saved_vtbl = (char*)os::malloc(vtbl_list_size * sizeof(void*), mtClass); +@@ -602,6 +618,9 @@ void VM_PopulateDumpSharedSpace::doit() { + FileMapInfo* mapinfo = new FileMapInfo(); + mapinfo->populate_header(MetaspaceShared::max_alignment()); + ++ if (Arguments::get_is_default_jsa()) { ++ mapinfo->set_is_default_jsa(true); ++ } + // Pass 1 - update file offsets in header. + mapinfo->write_header(); + mapinfo->write_space(MetaspaceShared::ro, _loader_data->ro_metaspace(), true); +@@ -997,6 +1016,8 @@ bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) { + mapinfo->verify_region_checksum(d_rw) && + (_ro_base = mapinfo->map_region(d_ro)) != NULL && + mapinfo->verify_region_checksum(d_ro) && ++ (_ro_base = mapinfo->map_region(d_md)) != NULL && ++ mapinfo->verify_region_checksum(d_md) && + (image_alignment == (size_t)max_alignment())) { + mapinfo->set_is_mapped(true); + return true; +@@ -1153,6 +1174,7 @@ void MetaspaceShared::initialize_shared_spaces() { + ReadClosure rc(&buffer); + SymbolTable::serialize_shared_table_header(&rc); + SystemDictionaryShared::serialize_dictionary_headers(&rc); ++ ClassLoader::deserialize_package_hash_table(dynamic_mapinfo->region_base(d_md)); + dynamic_mapinfo->close(); + } + +diff --git a/hotspot/src/share/vm/memory/metaspaceShared.hpp b/hotspot/src/share/vm/memory/metaspaceShared.hpp +index a9dadfbb..3eb8b12c 100644 +--- a/hotspot/src/share/vm/memory/metaspaceShared.hpp ++++ b/hotspot/src/share/vm/memory/metaspaceShared.hpp +@@ -90,8 +90,9 @@ class MetaspaceShared : AllStatic { + // core dynamic archive spaces + d_rw = 0, // read-write shared space in the heap + d_ro = 1, // read-only shared space in the heap +- d_bm = 2, // relocation bitmaps (freed after file mapping is finished) +- d_n_regions = 2 // d_rw and d_ro ++ d_md = 2, // miscellaneous data ++ d_bm = 3, // relocation bitmaps (freed after file mapping is finished) ++ d_n_regions = 3 // d_rw, d_ro, d_md + }; + + // Accessor functions to save shared space created for metadata, which has +diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp +index 0d1b1a8d..9276b895 100644 +--- a/hotspot/src/share/vm/oops/instanceKlass.cpp ++++ b/hotspot/src/share/vm/oops/instanceKlass.cpp +@@ -2633,7 +2633,7 @@ void InstanceKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handl + + // returns true IFF is_in_error_state() has been changed as a result of this call. + bool InstanceKlass::check_sharing_error_state() { +- assert(DumpSharedSpaces, "should only be called during dumping"); ++ assert(DynamicDumpSharedSpaces || DumpSharedSpaces, "should only be called during dumping"); + bool old_state = is_in_error_state(); + + if (!is_in_error_state()) { +@@ -3573,6 +3573,9 @@ void InstanceKlass::verify_on(outputStream* st) { + // Avoid redundant verifies, this really should be in product. + if (_verify_count == Universe::verify_count()) return; + _verify_count = Universe::verify_count(); ++ if (is_in_error_state()) { ++ return; ++ } + #endif + + // Verify Klass +diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp +index 1f603021..5a79ab7e 100644 +--- a/hotspot/src/share/vm/runtime/arguments.cpp ++++ b/hotspot/src/share/vm/runtime/arguments.cpp +@@ -100,6 +100,7 @@ do { \ + } \ + } while(0) + ++bool Arguments::_is_default_jsa = false; + char** Arguments::_jvm_flags_array = NULL; + int Arguments::_num_jvm_flags = 0; + char** Arguments::_jvm_args_array = NULL; +@@ -4041,23 +4042,32 @@ static void force_serial_gc() { + } + #endif // INCLUDE_ALL_GCS + ++char* Arguments::get_default_shared_archive_path() { ++ char *default_archive_path; ++ char jvm_path[JVM_MAXPATHLEN]; ++ os::jvm_path(jvm_path, sizeof(jvm_path)); ++ char *end = strrchr(jvm_path, *os::file_separator()); ++ if (end != NULL) { ++ *end = '\0'; ++ } ++ size_t jvm_path_len = strlen(jvm_path); ++ size_t file_sep_len = strlen(os::file_separator()); ++ const size_t len = jvm_path_len + file_sep_len + 20; ++ default_archive_path = NEW_C_HEAP_ARRAY(char, len, mtInternal); ++ if (default_archive_path != NULL) { ++ jio_snprintf(default_archive_path, len, "%s%sclasses.jsa", ++ jvm_path, os::file_separator()); ++ } ++ Arguments::set_is_default_jsa(true); ++ return default_archive_path; ++} ++ + // Sharing support + // Construct the path to the archive + static char* get_shared_archive_path() { + char *shared_archive_path; + if (SharedArchiveFile == NULL) { +- char jvm_path[JVM_MAXPATHLEN]; +- os::jvm_path(jvm_path, sizeof(jvm_path)); +- char *end = strrchr(jvm_path, *os::file_separator()); +- if (end != NULL) *end = '\0'; +- size_t jvm_path_len = strlen(jvm_path); +- size_t file_sep_len = strlen(os::file_separator()); +- const size_t len = jvm_path_len + file_sep_len + 20; +- shared_archive_path = NEW_C_HEAP_ARRAY(char, len, mtInternal); +- if (shared_archive_path != NULL) { +- jio_snprintf(shared_archive_path, len, "%s%sclasses.jsa", +- jvm_path, os::file_separator()); +- } ++ shared_archive_path = Arguments::get_default_shared_archive_path(); + } else { + shared_archive_path = os::strdup(SharedArchiveFile, mtInternal); + } +diff --git a/hotspot/src/share/vm/runtime/arguments.hpp b/hotspot/src/share/vm/runtime/arguments.hpp +index 19f5cb60..65907eb4 100644 +--- a/hotspot/src/share/vm/runtime/arguments.hpp ++++ b/hotspot/src/share/vm/runtime/arguments.hpp +@@ -240,6 +240,8 @@ class Arguments : AllStatic { + + private: + ++ // Indicates whether the JSA file is the default jsa file. ++ static bool _is_default_jsa; + // an array containing all flags specified in the .hotspotrc file + static char** _jvm_flags_array; + static int _num_jvm_flags; +@@ -487,6 +489,9 @@ class Arguments : AllStatic { + // Return the maximum size a heap with compressed oops can take + static size_t max_heap_for_compressed_oops(); + ++ static void set_is_default_jsa(bool is_default) { _is_default_jsa = is_default; } ++ static bool get_is_default_jsa() { return _is_default_jsa; } ++ + // return a char* array containing all options + static char** jvm_flags_array() { return _jvm_flags_array; } + static char** jvm_args_array() { return _jvm_args_array; } +@@ -622,6 +627,7 @@ class Arguments : AllStatic { + static char* get_ext_dirs() { return _java_ext_dirs->value(); } + static char* get_appclasspath() { return _java_class_path->value(); } + static void fix_appclasspath(); ++ static char* get_default_shared_archive_path(); + + // Operation modi + static Mode mode() { return _mode; } +diff --git a/hotspot/src/share/vm/utilities/hashtable.cpp b/hotspot/src/share/vm/utilities/hashtable.cpp +index 66df8f1f..df290d99 100644 +--- a/hotspot/src/share/vm/utilities/hashtable.cpp ++++ b/hotspot/src/share/vm/utilities/hashtable.cpp +@@ -58,6 +58,7 @@ template BasicHashtableEntry* BasicHashtable::new_entry(unsig + len = 1 << log2_int(len); // round down to power of 2 + assert(len >= _entry_size, ""); + _first_free_entry = NEW_C_HEAP_ARRAY2(char, len, F, CURRENT_PC); ++ memset(_first_free_entry, 0, len); + _end_block = _first_free_entry + len; + } + entry = (BasicHashtableEntry*)_first_free_entry; +diff --git a/hotspot/test/runtime/appcds/TestCommon.java b/hotspot/test/runtime/appcds/TestCommon.java +index 22eef4ed..6a61dc31 100644 +--- a/hotspot/test/runtime/appcds/TestCommon.java ++++ b/hotspot/test/runtime/appcds/TestCommon.java +@@ -54,6 +54,7 @@ public class TestCommon extends CDSTestUtils { + System.getProperty("test.timeout.factor", "1.0"); + + private static String currentArchiveName; ++ private static String topArchiveName; + + // Call this method to start new archive with new unique name + public static void startNewArchiveName() { +@@ -62,6 +63,13 @@ public class TestCommon extends CDSTestUtils { + timeStampFormat.format(new Date()) + ".jsa"; + } + ++ public static String getTopArchiveName() { ++ topArchiveName = System.getProperty("user.dir") + ++ File.separator + "d-appcds-" + timeStampFormat.format(new Date()) + ".jsa"; ++ currentArchiveName = topArchiveName; ++ return topArchiveName; ++ } ++ + // Call this method to get current archive name + public static String getCurrentArchiveName() { + return currentArchiveName; +@@ -90,6 +98,16 @@ public class TestCommon extends CDSTestUtils { + } + } + ++ public static void deletePriorTopArchives() { ++ File dir = new File(System.getProperty("user.dir")); ++ String files[] = dir.list(); ++ for (String name : files) { ++ if (name.startsWith("d-appcds-") && name.endsWith(".jsa")) { ++ if (!(new File(dir, name)).delete()) ++ System.out.println("deletePriorArchives(): delete failed for file " + name); ++ } ++ } ++ } + + // Create AppCDS archive using most common args - convenience method + // Legacy name preserved for compatibility +@@ -132,7 +150,6 @@ public class TestCommon extends CDSTestUtils { + + cmd.add("-Xshare:dump"); + cmd.add("-XX:+UseAppCDS"); +-// cmd.add("-Xlog:cds,cds+hashtables"); comment out because it will be run by jdk1.8 + cmd.add("-XX:ExtraSharedClassListFile=" + classList.getPath()); + + if (opts.archiveName == null) +@@ -147,6 +164,36 @@ public class TestCommon extends CDSTestUtils { + return executeAndLog(pb, "dump"); + } + ++ public static OutputAnalyzer createBaseArchive(String appJar, String appClasses[], String... suffix) ++ throws Exception { ++ return createArchive(appJar, appClasses, suffix); ++ } ++ ++ public static OutputAnalyzer createTopArchive(String appJar, String...suffix) ++ throws Exception { ++ AppCDSOptions opts = new AppCDSOptions(); ++ opts.setAppJar(appJar); ++ opts.addSuffix(suffix); ++ ++ ArrayList cmd = new ArrayList(); ++ cmd.add("-cp"); ++ cmd.add(opts.appJar); ++ ++ String baseArchiveName = getCurrentArchiveName(); ++ deletePriorTopArchives(); ++ String topArchiveNmae = getTopArchiveName(); ++ cmd.add("-XX:+UnlockExperimentalVMOptions"); ++ cmd.add("-Xshare:on"); ++ cmd.add("-XX:SharedArchiveFile=" + baseArchiveName); ++ cmd.add("-XX:ArchiveClassesAtExit=" + topArchiveNmae); ++ cmd.add("-XX:+InfoDynamicCDS"); ++ ++ for (String s : opts.suffix) cmd.add(s); ++ ++ String[] cmdLine = cmd.toArray(new String[cmd.size()]); ++ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, makeCommandLineForAppCDS(cmdLine)); ++ return executeAndLog(pb, "dump"); ++ } + + // Execute JVM using AppCDS archive with specified AppCDSOptions + public static OutputAnalyzer runWithArchive(AppCDSOptions opts) +@@ -156,6 +203,9 @@ public class TestCommon extends CDSTestUtils { + + for (String p : opts.prefix) cmd.add(p); + ++ if (topArchiveName != null) { ++ cmd.add("-XX:+InfoDynamicCDS"); ++ } + cmd.add("-Xshare:" + opts.xShareMode); + cmd.add("-XX:+UseAppCDS"); + cmd.add("-showversion"); +@@ -174,7 +224,6 @@ public class TestCommon extends CDSTestUtils { + return executeAndLog(pb, "exec"); + } + +- + public static OutputAnalyzer execCommon(String... suffix) throws Exception { + AppCDSOptions opts = (new AppCDSOptions()); + opts.addSuffix(suffix); +@@ -261,6 +310,27 @@ public class TestCommon extends CDSTestUtils { + } + + ++ public static OutputAnalyzer testDynamicCDS(String appJar, String appClasses[], String... args) ++ throws Exception { ++ // Create base archive ++ OutputAnalyzer output = createBaseArchive(appJar, appClasses, args); ++ output.shouldContain("Loading classes to share"); ++ output.shouldHaveExitValue(0); ++ ++ // Create top archive ++ output = createTopArchive(appJar, args); ++ output.shouldContain("Written dynamic archive"); ++ output.shouldHaveExitValue(0); ++ ++ // Exec with top archive ++ output = exec(appJar, args); ++ ++ // Check exec result ++ checkMatches(output, "SharedArchivePath", "SharedDynamicArchivePath"); ++ output.shouldHaveExitValue(0); ++ return output; ++ } ++ + public static OutputAnalyzer checkExecReturn(OutputAnalyzer output, int ret, + boolean checkContain, String... matches) throws Exception { + try { +diff --git a/hotspot/test/runtime/appcds/dynamicArchive/DynamicFlag.java b/hotspot/test/runtime/appcds/dynamicArchive/DynamicFlag.java +new file mode 100644 +index 00000000..79f30759 +--- /dev/null ++++ b/hotspot/test/runtime/appcds/dynamicArchive/DynamicFlag.java +@@ -0,0 +1,39 @@ ++/* ++ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++/* ++ * @test ++ * @summary The DynamicDumpShareSpaces flag is internal, setting it at the command line should have no effect. ++ * @library /testlibrary /runtime/appcds /runtime/appcds/test-classes ++ * @compile ../test-classes/Hello.java ++ * @run driver DynamicFlag ++ */ ++ ++public class DynamicFlag { ++ public static void main(String[] args) throws Exception { ++ TestCommon.testDynamicCDS(JarBuilder.getOrCreateHelloJar(), ++ TestCommon.list("Hello"), "-XX:+DynamicDumpSharedSpaces", "Hello"); ++ } ++} +diff --git a/hotspot/test/runtime/appcds/dynamicArchive/DynamicHelloTest.java b/hotspot/test/runtime/appcds/dynamicArchive/DynamicHelloTest.java +new file mode 100644 +index 00000000..48e97cb2 +--- /dev/null ++++ b/hotspot/test/runtime/appcds/dynamicArchive/DynamicHelloTest.java +@@ -0,0 +1,42 @@ ++/* ++ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++/* ++ * @test ++ * @summary Hello World test for dynamic cds ++ * @library /testlibrary /runtime/appcds /runtime/appcds/test-classes ++ * @compile ../test-classes/Hello.java ++ * @run main DynamicHelloTest ++ */ ++ ++public class DynamicHelloTest { ++ public static void main(String[] args) throws Exception { ++ TestCommon.testDynamicCDS(JarBuilder.getOrCreateHelloJar(), ++ null, "Hello"); ++ ++ TestCommon.testDynamicCDS(JarBuilder.getOrCreateHelloJar(), ++ TestCommon.list("Hello"), "Hello"); ++ } ++} +diff --git a/hotspot/test/runtime/appcds/dynamicArchive/VerifyWithDynamicArchive.java b/hotspot/test/runtime/appcds/dynamicArchive/VerifyWithDynamicArchive.java +new file mode 100644 +index 00000000..eacc1aff +--- /dev/null ++++ b/hotspot/test/runtime/appcds/dynamicArchive/VerifyWithDynamicArchive.java +@@ -0,0 +1,42 @@ ++/* ++ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++/* ++ * @test ++ * @summary Hello World test for dynamic cds ++ * @library /testlibrary /runtime/appcds /runtime/appcds/test-classes ++ * @compile ../test-classes/Hello.java ++ * @run main VerifyWithDynamicArchive ++ */ ++ ++public class VerifyWithDynamicArchive { ++ public static void main(String[] args) throws Exception { ++ TestCommon.testDynamicCDS(JarBuilder.getOrCreateHelloJar(), ++ null, "-XX:+VerifySharedSpaces", "Hello"); ++ ++ TestCommon.testDynamicCDS(JarBuilder.getOrCreateHelloJar(), ++ TestCommon.list("Hello"), "-XX:+VerifySharedSpaces", "Hello"); ++ } ++} +diff --git a/jdk/make/BuildJdk.gmk b/jdk/make/BuildJdk.gmk +index 467792fa..bb8ea8a9 100644 +--- a/jdk/make/BuildJdk.gmk ++++ b/jdk/make/BuildJdk.gmk +@@ -103,6 +103,13 @@ images: + ifeq ($(OPENJDK_TARGET_OS), macosx) + +$(MAKE) -f Bundles.gmk + endif ++ ifeq ($(BUILD_CDS_ARCHIVE), true) ++ echo Creating CDS archive for jdk image ++ $(JDK_IMAGE_DIR)/bin/java -Xshare:dump -Xmx128M -Xms128M -XX:ParallelGCThreads=1 -Xint $(LOG_INFO) ++ echo Creating CDS archive for jre image ++ $(JRE_IMAGE_DIR)/bin/java -Xshare:dump -Xmx128M -Xms128M -XX:ParallelGCThreads=1 -Xint $(LOG_INFO) ++ endif ++ + + overlay-images: + +$(MAKE) -f CompileLaunchers.gmk OVERLAY_IMAGES=true diff --git a/8287109-Distrust-failed-with-CertificateExpired.patch b/8287109-Distrust-failed-with-CertificateExpired.patch new file mode 100644 index 0000000000000000000000000000000000000000..11a8755508584eac643789f7ab155d4fba5a59b2 --- /dev/null +++ b/8287109-Distrust-failed-with-CertificateExpired.patch @@ -0,0 +1,237 @@ +From d2d3408154beb52370ee8784767375a7cc8d325d Mon Sep 17 00:00:00 2001 +Date: Wed, 21 Sep 2022 10:31:17 +0800 +Subject: 8287109: Distrust.java failed with CertificateExpiredException + +--- + .../Symantec/Distrust.java | 26 +++++- + .../Symantec/appleistca2g1-chain.pem | 80 ------------------- + .../Symantec/geotrustglobalca-chain.pem | 66 --------------- + 3 files changed, 23 insertions(+), 149 deletions(-) + delete mode 100644 jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/appleistca2g1-chain.pem + delete mode 100644 jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/geotrustglobalca-chain.pem + +diff --git a/jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/Distrust.java b/jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/Distrust.java +index d394f417..22266255 100644 +--- a/jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/Distrust.java ++++ b/jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/Distrust.java +@@ -51,15 +51,14 @@ public class Distrust { + // Each of the roots have a test certificate chain stored in a file + // named "-chain.pem". + private static String[] rootsToTest = new String[] { +- "geotrustglobalca", "geotrustprimarycag2", "geotrustprimarycag3", ++ "geotrustprimarycag2", "geotrustprimarycag3", + "geotrustuniversalca", "thawteprimaryrootca", "thawteprimaryrootcag2", + "thawteprimaryrootcag3", "verisignclass3g3ca", "verisignclass3g4ca", + "verisignclass3g5ca", "verisignuniversalrootca" }; + + // Each of the subCAs with a delayed distrust date have a test certificate + // chain stored in a file named "-chain.pem". +- private static String[] subCAsToTest = new String[] { +- "appleistca2g1", "appleistca8g1" }; ++ private static String[] subCAsToTest = new String[] {"appleistca8g1"}; + + // A date that is after the restrictions take affect + private static final Date APRIL_17_2019 = +@@ -177,6 +176,11 @@ public class Distrust { + throw new Exception("chain should be invalid"); + } + } catch (CertificateException ce) { ++ // expired TLS certificates should not be treated as failure ++ if (expired(ce)) { ++ System.err.println("Test is N/A, chain is expired"); ++ return; ++ } + if (valid) { + throw new Exception("Unexpected exception, chain " + + "should be valid", ce); +@@ -184,6 +188,7 @@ public class Distrust { + if (ce instanceof ValidatorException) { + ValidatorException ve = (ValidatorException)ce; + if (ve.getErrorType() != ValidatorException.T_UNTRUSTED_CERT) { ++ ce.printStackTrace(System.err); + throw new Exception("Unexpected exception: " + ce); + } + } else { +@@ -192,6 +197,21 @@ public class Distrust { + } + } + ++ // check if a cause of exception is an expired cert ++ private static boolean expired(CertificateException ce) { ++ if (ce instanceof CertificateExpiredException) { ++ return true; ++ } ++ Throwable t = ce.getCause(); ++ while (t != null) { ++ if (t instanceof CertificateExpiredException) { ++ return true; ++ } ++ t = t.getCause(); ++ } ++ return false; ++ } ++ + private static X509Certificate[] loadCertificateChain(String name) + throws Exception { + try (InputStream in = new FileInputStream(TEST_SRC + File.separator + +diff --git a/jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/appleistca2g1-chain.pem b/jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/appleistca2g1-chain.pem +deleted file mode 100644 +index 0235631d..00000000 +--- a/jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/appleistca2g1-chain.pem ++++ /dev/null +@@ -1,80 +0,0 @@ +------BEGIN CERTIFICATE----- +-MIIGGzCCBQOgAwIBAgIITJltLCqcD0gwDQYJKoZIhvcNAQELBQAwYjEcMBoGA1UE +-AxMTQXBwbGUgSVNUIENBIDIgLSBHMTEgMB4GA1UECxMXQ2VydGlmaWNhdGlvbiBB +-dXRob3JpdHkxEzARBgNVBAoTCkFwcGxlIEluYy4xCzAJBgNVBAYTAlVTMB4XDTE5 +-MDEwODIxMTcxNFoXDTIwMDgwODIxMjcwMFowgaoxSjBIBgNVBAMMQWFjdGl2ZS5n +-ZW90cnVzdC1nbG9iYWwtY2EudGVzdC1wYWdlcy5jZXJ0aWZpY2F0ZW1hbmFnZXIu +-YXBwbGUuY29tMSUwIwYDVQQLDBxtYW5hZ2VtZW50OmlkbXMuZ3JvdXAuODY0ODU5 +-MRMwEQYDVQQKDApBcHBsZSBJbmMuMRMwEQYDVQQIDApDYWxpZm9ybmlhMQswCQYD +-VQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMCjFUrVHTEX +-0aVU6x9LiGa6oVr9blaCsMFrLicPQguc43Vs/pN+g4jzRXsTSMe9XefezBQb6tzZ +-SMRXVB4kWMr4K1BVgQDkXeyoh4KrXRkdEF9ZIJPNxwTmmYUOc5M6NOYwkLelYz+t +-7n1iNIGylbjwU4qwauElk2alFVqYTEPDLzwvqVDb9jMAJ8MPSDjfUlXW0XD9oXZM +-hC+8LU9JBgJ3YBdzRHa4WnrudUbWjspqaNfAYpVIX0cfCJKnMsKqaSKjS4pIRtWm +-L6NlCTCoIMyOh+wmbWPPX24H2D3+ump5FA35fRYbVznmosl5n1AK34S9tD4XZ7lO +-WZKfaFi1liMCAwEAAaOCAoowggKGMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAU +-2HqURHyQcJAWnt0XnAFEA4bWKikwfgYIKwYBBQUHAQEEcjBwMDQGCCsGAQUFBzAC +-hihodHRwOi8vY2VydHMuYXBwbGUuY29tL2FwcGxlaXN0Y2EyZzEuZGVyMDgGCCsG +-AQUFBzABhixodHRwOi8vb2NzcC5hcHBsZS5jb20vb2NzcDAzLWFwcGxlaXN0Y2Ey +-ZzEwMTBMBgNVHREERTBDgkFhY3RpdmUuZ2VvdHJ1c3QtZ2xvYmFsLWNhLnRlc3Qt +-cGFnZXMuY2VydGlmaWNhdGVtYW5hZ2VyLmFwcGxlLmNvbTCB/wYDVR0gBIH3MIH0 +-MIHxBgoqhkiG92NkBQsEMIHiMIGkBggrBgEFBQcCAjCBlwyBlFJlbGlhbmNlIG9u +-IHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0YW5j +-ZSBvZiBhbnkgYXBwbGljYWJsZSB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2Ug +-YW5kL29yIGNlcnRpZmljYXRpb24gcHJhY3RpY2Ugc3RhdGVtZW50cy4wOQYIKwYB +-BQUHAgEWLWh0dHA6Ly93d3cuYXBwbGUuY29tL2NlcnRpZmljYXRlYXV0aG9yaXR5 +-L3JwYTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwNwYDVR0fBDAwLjAs +-oCqgKIYmaHR0cDovL2NybC5hcHBsZS5jb20vYXBwbGVpc3RjYTJnMS5jcmwwHQYD +-VR0OBBYEFP0qkmFJhArI0MsfW0V+/wY9x4GSMA4GA1UdDwEB/wQEAwIFoDANBgkq +-hkiG9w0BAQsFAAOCAQEATjT8M0bIq+mFc8k5cd4KDjCMBjYl/l3/8zKlWYGP+nl1 +-KRogXcGRa3LcfpdJcqgMrx8e9Xohduvl8MBzwv671rYkppzZdsmZdLVorAdbL5GL +-suhTjAS5yL3NBWNMRpeOgFsVr7YtPDEvo3CFsnzjg7THe0S6Y35oYukJtUzGUvSY +-kC3ApBTdjj0vAeow+dbt+AHKnQiEnon4ToSFmtnkru08Uxe7uyHCQ2sLUg0EPYc9 +-t9I8lviaHfK/mQoCzlme2O/H5Rher8dXCv8hVT1NKbsi28EpgpqcTLS+hn/Edc/q +-4dPDoO1Ozs+ixRzFeMpA+JrnAyARb6qbSrAPBgtIbQ== +------END CERTIFICATE----- +------BEGIN CERTIFICATE----- +-MIIEQDCCAyigAwIBAgIDAjp0MA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT +-MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +-YWwgQ0EwHhcNMTQwNjE2MTU0MjAyWhcNMjIwNTIwMTU0MjAyWjBiMRwwGgYDVQQD +-ExNBcHBsZSBJU1QgQ0EgMiAtIEcxMSAwHgYDVQQLExdDZXJ0aWZpY2F0aW9uIEF1 +-dGhvcml0eTETMBEGA1UEChMKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwggEiMA0G +-CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDQk6EdR0MgFrILa+vD1bTox5jN896/ +-6E3p4zaAB/xFG2p8RYauVtOkCX9hDWtdflJrfbTIOcT0Zzr3g84Zb4YvfkV+Rxxn +-UsqVBV3iNlGFwNRngDVvFd0+/R3S/Y80UNjsdiq+49Pa5P3I6ygClhGXF2Ec6cRZ +-O0LcMtEJHdqm0UOG/16yvIzPZtsBiwKulEjzOI/96jKoCOyGl1GUJD5JSZZT6Hmh +-QIHpBbuTlVH84/18EUv3ngizFUkVB/nRN6CbSzL2tcTcatH8Cu324MUpoKiLcf4N +-krz+VHAYCm3H7Qz7yS0Gw4yF/MuGXNY2jhKLCX/7GRo41fCUMHoPpozzAgMBAAGj +-ggEdMIIBGTAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1luMrMTjAdBgNVHQ4E +-FgQU2HqURHyQcJAWnt0XnAFEA4bWKikwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNV +-HQ8BAf8EBAMCAQYwNQYDVR0fBC4wLDAqoCigJoYkaHR0cDovL2cuc3ltY2IuY29t +-L2NybHMvZ3RnbG9iYWwuY3JsMC4GCCsGAQUFBwEBBCIwIDAeBggrBgEFBQcwAYYS +-aHR0cDovL2cuc3ltY2QuY29tMEwGA1UdIARFMEMwQQYKYIZIAYb4RQEHNjAzMDEG +-CCsGAQUFBwIBFiVodHRwOi8vd3d3Lmdlb3RydXN0LmNvbS9yZXNvdXJjZXMvY3Bz +-MA0GCSqGSIb3DQEBCwUAA4IBAQAWR3NvhaJi4ecqdruJlUIml7xKrKxwUzo/MYM9 +-PByrmuKxXRx2GqA8DHJXvtOeUODImdZY1wLqzg0pVHzN9cLGkClVo28UqAtCDTqY +-bQZ4nvBqox0CCqIopI3CgUY+bWfa3j/+hQ5CKhLetbf7uBunlux3n+zUU5V6/wf0 +-8goUwFFSsdaOUAsamVy8C8m97e34XsFW201+I6QRoSzUGwWa5BtS9nw4mQVLunKN +-QolgBGYq9P1o12v3mUEo1mwkq+YlUy7Igpnioo8jvjCDsSeL+mh/AUnoxphrEC6Y +-XorXykuxx8lYmtA225aV7LaB5PLNbxt5h0wQPInkTfpU3Kqm +------END CERTIFICATE----- +------BEGIN CERTIFICATE----- +-MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT +-MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +-YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG +-EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg +-R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 +-9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq +-fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv +-iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU +-1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ +-bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW +-MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA +-ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l +-uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn +-Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS +-tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF +-PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un +-hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV +-5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== +------END CERTIFICATE----- +diff --git a/jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/geotrustglobalca-chain.pem b/jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/geotrustglobalca-chain.pem +deleted file mode 100644 +index 3249716b..00000000 +--- a/jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/geotrustglobalca-chain.pem ++++ /dev/null +@@ -1,66 +0,0 @@ +------BEGIN CERTIFICATE----- +-MIIHBjCCBe6gAwIBAgIQanINWwJAuap0V7lFjnfUwTANBgkqhkiG9w0BAQsFADBE +-MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMU +-R2VvVHJ1c3QgU1NMIENBIC0gRzMwHhcNMTcwNTAzMDAwMDAwWhcNMjAwNTAyMjM1 +-OTU5WjCBkTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNV +-BAcMDU1vdW50YWluIFZpZXcxFzAVBgNVBAoMDkdlb1RydXN0LCBJbmMuMRgwFgYD +-VQQLDA9Sb290IDEwIC0gVkFMSUQxIjAgBgNVBAMMGXZhbGlkLXJvb3QxMC5nZW90 +-cnVzdC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTegUYhAh0 +-P7aF6jzk8dit4Vzddo3hM+J7Eak/+N1sqVUS2HpNd7VO50FrbEWKIRusv7QNtlpY +-1Cgrla8M4RAhCB0wkkHXZ1Evz6E1AEFQqNSjyuRQxeEXl+xCL+MF+yAMhDRnHh+E +-eSJ3ie0T66saOyaLM9fPpr3xomAQ/IRlP1atJ/Z8XbPo25HuxwzxiWFW+RjwVIfI +-gxHz4Okwc1uImDUIDlEu9Uaqqb4jHhxU1EkKMmgEncpqwCROcZMujUkogfB49Z7+ +-K17r6ARIrUuxqfNPrPwe+O88WgIeDSWffPM67UlvtomZOwuTNdv9OoCX1wUCLS7m +-/gZ3rqqqeJvfAgMBAAGjggOkMIIDoDAkBgNVHREEHTAbghl2YWxpZC1yb290MTAu +-Z2VvdHJ1c3QuY29tMAkGA1UdEwQCMAAwDgYDVR0PAQH/BAQDAgWgMCsGA1UdHwQk +-MCIwIKAeoByGGmh0dHA6Ly9nbi5zeW1jYi5jb20vZ24uY3JsMIGdBgNVHSAEgZUw +-gZIwgY8GBmeBDAECAjCBhDA/BggrBgEFBQcCARYzaHR0cHM6Ly93d3cuZ2VvdHJ1 +-c3QuY29tL3Jlc291cmNlcy9yZXBvc2l0b3J5L2xlZ2FsMEEGCCsGAQUFBwICMDUM +-M2h0dHBzOi8vd3d3Lmdlb3RydXN0LmNvbS9yZXNvdXJjZXMvcmVwb3NpdG9yeS9s +-ZWdhbDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHwYDVR0jBBgwFoAU +-0m/3lvSFP3I8MH0j2oV4m6N8WnwwVwYIKwYBBQUHAQEESzBJMB8GCCsGAQUFBzAB +-hhNodHRwOi8vZ24uc3ltY2QuY29tMCYGCCsGAQUFBzAChhpodHRwOi8vZ24uc3lt +-Y2IuY29tL2duLmNydDCCAfUGCisGAQQB1nkCBAIEggHlBIIB4QHfAHUA3esdK3oN +-T6Ygi4GtgWhwfi6OnQHVXIiNPRHEzbbsvswAAAFbz9h5vQAABAMARjBEAiAx/C0U +-5NdHxK4v2oHnstYksb1Vny8PcQkSvgpx9PsZEwIgNTOU70Zc5szG23xdbvtoH5lN +-SAoVswiF5gFQS5MGu1sAdgCkuQmQtBhYFIe7E6LMZ3AKPDWYBPkb37jjd80OyA3c +-EAAAAVvP2HnZAAAEAwBHMEUCIFGjB8r2H0VDwTUE/aY/Mv+M97sqAvEP1doOcHpg +-0qyfAiEArw/S2F7OEcmKGUY1WRBuApfAx5d7hzrTSV/jZv95qJwAdgDuS723dc5g +-uuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAVvP2HoDAAAEAwBHMEUCIQCH6MFZ +-tZF3Cqukt3/69fkU0Y5ePXXx8+xkOXRsIG3EGgIgSmCBWrnmPiiGA3x5QP8I8m4r +-Uee0y7s4NQNwjMgHrjwAdgC8eOHfxfY8aEZJM02hD6FfCXlpIAnAgbTz9pF/Ptm4 +-pQAAAVvP2HqcAAAEAwBHMEUCIA8e2kAVYYuQCtn4PqK98BuHnLm9rC40DboFLCle +-SmQsAiEApbCJR05hr9VkNWmjaaUUGGZdVyUu9XX504LHVWyXZDUwDQYJKoZIhvcN +-AQELBQADggEBAEtfBfZ2y5uTohvW3h00Kcuop6Nq7Y59GU3MeizPKtx48DB8qHyd +-y5bLFwXzsGA1WkwpKzPbROsTGcAAXJHh03bj24AemUr/J/eQcjkfSoNBdHDpiSsk +-VZkQK2fGJDiYJ/r9mxKZcgd2pyN3l2OtVtNMv2dnFGF35UkkeqO3jqImwbypAmRX +-HdQV9dvW2YDRjzkebNNey6UwY9+YTSzr4da2hcaMHrj588Eqa4DDgNcY9QnE2RzN +-giArA+4RlM4AZ3jC2A756I67hrlvH+lhumHLp06hGfMiQJF1aaauFVSa36HKc3C/ +-ty+sLdJbemEJLAr8uNXggFD+U8TKw1S4LSw= +------END CERTIFICATE----- +------BEGIN CERTIFICATE----- +-MIIETzCCAzegAwIBAgIDAjpvMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT +-MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +-YWwgQ0EwHhcNMTMxMTA1MjEzNjUwWhcNMjIwNTIwMjEzNjUwWjBEMQswCQYDVQQG +-EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg +-U1NMIENBIC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDjvn4K +-hqPPa209K6GXrUkkTdd3uTR5CKWeop7eRxKSPX7qGYax6E89X/fQp3eaWx8KA7UZ +-U9ulIZRpY51qTJEMEEe+EfpshiW3qwRoQjgJZfAU2hme+msLq2LvjafvY3AjqK+B +-89FuiGdT7BKkKXWKp/JXPaKDmJfyCn3U50NuMHhiIllZuHEnRaoPZsZVP/oyFysx +-j0ag+mkUfJ2fWuLrM04QprPtd2PYw5703d95mnrU7t7dmszDt6ldzBE6B7tvl6QB +-I0eVH6N3+liSxsfQvc+TGEK3fveeZerVO8rtrMVwof7UEJrwEgRErBpbeFBFV0xv +-vYDLgVwts7x2oR5lAgMBAAGjggFKMIIBRjAfBgNVHSMEGDAWgBTAephojYn7qwVk +-DBF9qn1luMrMTjAdBgNVHQ4EFgQU0m/3lvSFP3I8MH0j2oV4m6N8WnwwEgYDVR0T +-AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwNgYDVR0fBC8wLTAroCmgJ4Yl +-aHR0cDovL2cxLnN5bWNiLmNvbS9jcmxzL2d0Z2xvYmFsLmNybDAvBggrBgEFBQcB +-AQQjMCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly9nMi5zeW1jYi5jb20wTAYDVR0gBEUw +-QzBBBgpghkgBhvhFAQc2MDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly93d3cuZ2VvdHJ1 +-c3QuY29tL3Jlc291cmNlcy9jcHMwKQYDVR0RBCIwIKQeMBwxGjAYBgNVBAMTEVN5 +-bWFudGVjUEtJLTEtNTM5MA0GCSqGSIb3DQEBCwUAA4IBAQCg1Pcs+3QLf2TxzUNq +-n2JTHAJ8mJCi7k9o1CAacxI+d7NQ63K87oi+fxfqd4+DYZVPhKHLMk9sIb7SaZZ9 +-Y73cK6gf0BOEcP72NZWJ+aZ3sEbIu7cT9clgadZM/tKO79NgwYCA4ef7i28heUrg +-3Kkbwbf7w0lZXLV3B0TUl/xJAIlvBk4BcBmsLxHA4uYPL4ZLjXvDuacu9PGsFj45 +-SVGeF0tPEDpbpaiSb/361gsDTUdWVxnzy2v189bPsPX1oxHSIFMTNDcFLENaY9+N +-QNaFHlHpURceA1bJ8TCt55sRornQMYGbaLHZ6PPmlH7HrhMvh+3QJbBo+d4IWvMp +-zNSS +------END CERTIFICATE----- +-- +2.22.0 + diff --git a/8290705_fix_StringConcat_validate_mem_flow_asserts_with_unexpected_userStoreI.patch b/8290705_fix_StringConcat_validate_mem_flow_asserts_with_unexpected_userStoreI.patch new file mode 100644 index 0000000000000000000000000000000000000000..ae487e7ae7f18fe43d60b64919ba7516ba8ba78f --- /dev/null +++ b/8290705_fix_StringConcat_validate_mem_flow_asserts_with_unexpected_userStoreI.patch @@ -0,0 +1,145 @@ +diff --git a/hotspot/src/share/vm/opto/stringopts.cpp b/hotspot/src/share/vm/opto/stringopts.cpp +index d92a3d7a3..2d11b2257 100644 +--- a/hotspot/src/share/vm/opto/stringopts.cpp ++++ b/hotspot/src/share/vm/opto/stringopts.cpp +@@ -968,6 +968,21 @@ bool StringConcat::validate_control_flow() { + fail = true; + break; + } else if (ptr->is_Proj() && ptr->in(0)->is_Initialize()) { ++ // Check for side effect between Initialize and the constructor ++ for (SimpleDUIterator iter(ptr); iter.has_next(); iter.next()) { ++ Node* use = iter.get(); ++ if (!use->is_CFG() && !use->is_CheckCastPP() && !use->is_Load()) { ++#ifndef PRODUCT ++ if (PrintOptimizeStringConcat) { ++ tty->print_cr("unexpected control use of Initialize"); ++ ptr->in(0)->dump(); // Initialize node ++ use->dump(1); ++ } ++#endif ++ fail = true; ++ break; ++ } ++ } + ptr = ptr->in(0)->in(0); + } else if (ptr->is_Region()) { + Node* copy = ptr->as_Region()->is_copy(); +diff --git a/hotspot/test/compiler/stringopts/SideEffectBeforeConstructor.jasm b/hotspot/test/compiler/stringopts/SideEffectBeforeConstructor.jasm +new file mode 100644 +index 000000000..cbc6d754b +--- /dev/null ++++ b/hotspot/test/compiler/stringopts/SideEffectBeforeConstructor.jasm +@@ -0,0 +1,58 @@ ++/* ++ * Copyright (c) 2022, 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++super public class compiler/stringopts/SideEffectBeforeConstructor ++ version 51:0 ++{ ++ public static Field result:I; ++ ++ static Method "":"()V" ++ stack 2 locals 0 ++ { ++ iconst_0; ++ putstatic Field result:"I"; ++ return; ++ } ++ public Method "":"()V" ++ stack 1 locals 1 ++ { ++ aload_0; ++ invokespecial Method java/lang/Object."":"()V"; ++ return; ++ } ++ ++ public static Method test:"(Ljava/lang/String;)V" ++ stack 4 locals 1 ++ { ++ new class java/lang/StringBuffer; ++ dup; ++ getstatic Field result:"I"; ++ iconst_1; ++ iadd; ++ putstatic Field result:"I"; ++ aload_0; ++ invokespecial Method java/lang/StringBuffer."":"(Ljava/lang/String;)V"; ++ invokevirtual Method java/lang/StringBuffer.toString:"()Ljava/lang/String;"; ++ return; ++ } ++} +diff --git a/hotspot/test/compiler/stringopts/TestSideEffectBeforeConstructor.java b/hotspot/test/compiler/stringopts/TestSideEffectBeforeConstructor.java +new file mode 100644 +index 000000000..86c5eca1d +--- /dev/null ++++ b/hotspot/test/compiler/stringopts/TestSideEffectBeforeConstructor.java +@@ -0,0 +1,49 @@ ++/* ++ * Copyright (c) 2022, 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++/* ++ * @test ++ * @bug 8290705 ++ * @summary Test correctness of the string concatenation optimization with ++ * a store between StringBuffer allocation and constructor invocation. ++ * @compile SideEffectBeforeConstructor.jasm ++ * @run main/othervm -Xbatch compiler.stringopts.TestSideEffectBeforeConstructor ++ */ ++ ++package compiler.stringopts; ++ ++public class TestSideEffectBeforeConstructor { ++ ++ public static void main(String[] args) { ++ for (int i = 0; i < 100_000; ++i) { ++ try { ++ SideEffectBeforeConstructor.test(null); ++ } catch (NullPointerException npe) { ++ // Expected ++ } ++ } ++ if (SideEffectBeforeConstructor.result != 100_000) { ++ throw new RuntimeException("Unexpected result: " + SideEffectBeforeConstructor.result); ++ } ++ } ++} diff --git a/Dynamic-CDS-Archive.patch b/Dynamic-CDS-Archive.patch new file mode 100644 index 0000000000000000000000000000000000000000..edccdea2d34992af8b341928e6630b677a2897e3 --- /dev/null +++ b/Dynamic-CDS-Archive.patch @@ -0,0 +1,8657 @@ +From f1cba2dd8fe526f4ad5ea4913154a174bd19a080 Mon Sep 17 00:00:00 2001 +Date: Sat, 3 Sep 2022 14:25:11 +0000 +Subject: Dynamic-CDS-Archive + +--- + hotspot/src/os/linux/vm/os_linux.cpp | 3 +- + hotspot/src/share/vm/cds/archiveBuilder.cpp | 807 ++++++++++++++++ + hotspot/src/share/vm/cds/archiveBuilder.hpp | 368 +++++++ + hotspot/src/share/vm/cds/archiveUtils.cpp | 247 +++++ + hotspot/src/share/vm/cds/archiveUtils.hpp | 141 +++ + hotspot/src/share/vm/cds/dumpAllocStats.cpp | 109 +++ + hotspot/src/share/vm/cds/dumpAllocStats.hpp | 88 ++ + hotspot/src/share/vm/cds/dynamicArchive.cpp | 412 ++++++++ + hotspot/src/share/vm/cds/dynamicArchive.hpp | 54 ++ + .../share/vm/classfile/classFileParser.cpp | 7 + + .../src/share/vm/classfile/classLoaderExt.hpp | 2 +- + .../share/vm/classfile/compactHashtable.cpp | 216 +++++ + .../share/vm/classfile/compactHashtable.hpp | 349 +++++++ + .../share/vm/classfile/sharedClassUtil.hpp | 4 + + .../src/share/vm/classfile/symbolTable.cpp | 102 +- + .../src/share/vm/classfile/symbolTable.hpp | 12 + + .../share/vm/classfile/systemDictionary.cpp | 159 +-- + .../share/vm/classfile/systemDictionary.hpp | 1 + + .../vm/classfile/systemDictionaryShared.cpp | 911 ++++++++++++++++++ + .../vm/classfile/systemDictionaryShared.hpp | 167 +++- + hotspot/src/share/vm/memory/allocation.hpp | 12 + + .../src/share/vm/memory/allocation.inline.hpp | 53 +- + hotspot/src/share/vm/memory/filemap.cpp | 352 +++++-- + hotspot/src/share/vm/memory/filemap.hpp | 104 +- + hotspot/src/share/vm/memory/iterator.hpp | 7 + + hotspot/src/share/vm/memory/metaspace.cpp | 80 +- + hotspot/src/share/vm/memory/metaspace.hpp | 1 + + .../src/share/vm/memory/metaspaceClosure.cpp | 87 ++ + .../src/share/vm/memory/metaspaceClosure.hpp | 381 ++++++++ + .../src/share/vm/memory/metaspaceShared.cpp | 148 ++- + .../src/share/vm/memory/metaspaceShared.hpp | 51 +- + hotspot/src/share/vm/oops/annotations.cpp | 12 + + hotspot/src/share/vm/oops/annotations.hpp | 9 + + hotspot/src/share/vm/oops/arrayKlass.cpp | 22 + + hotspot/src/share/vm/oops/arrayKlass.hpp | 3 +- + hotspot/src/share/vm/oops/constMethod.cpp | 26 + + hotspot/src/share/vm/oops/constMethod.hpp | 8 +- + hotspot/src/share/vm/oops/constantPool.cpp | 93 +- + hotspot/src/share/vm/oops/constantPool.hpp | 12 + + hotspot/src/share/vm/oops/cpCache.cpp | 69 ++ + hotspot/src/share/vm/oops/cpCache.hpp | 25 +- + hotspot/src/share/vm/oops/instanceKlass.cpp | 131 ++- + hotspot/src/share/vm/oops/instanceKlass.hpp | 12 +- + hotspot/src/share/vm/oops/klass.cpp | 83 +- + hotspot/src/share/vm/oops/klass.hpp | 10 +- + hotspot/src/share/vm/oops/klassVtable.hpp | 3 + + hotspot/src/share/vm/oops/metadata.hpp | 4 +- + hotspot/src/share/vm/oops/method.cpp | 22 +- + hotspot/src/share/vm/oops/method.hpp | 7 +- + hotspot/src/share/vm/oops/methodCounters.hpp | 7 + + hotspot/src/share/vm/oops/methodData.cpp | 9 + + hotspot/src/share/vm/oops/methodData.hpp | 5 +- + hotspot/src/share/vm/oops/objArrayKlass.cpp | 7 + + hotspot/src/share/vm/oops/objArrayKlass.hpp | 3 +- + hotspot/src/share/vm/oops/symbol.hpp | 22 +- + hotspot/src/share/vm/runtime/arguments.cpp | 142 +++ + hotspot/src/share/vm/runtime/arguments.hpp | 19 +- + hotspot/src/share/vm/runtime/globals.hpp | 21 + + hotspot/src/share/vm/runtime/java.cpp | 8 + + hotspot/src/share/vm/runtime/mutexLocker.cpp | 5 +- + hotspot/src/share/vm/runtime/mutexLocker.hpp | 3 + + hotspot/src/share/vm/runtime/os.cpp | 9 +- + hotspot/src/share/vm/runtime/os.hpp | 2 + + hotspot/src/share/vm/runtime/thread.cpp | 10 + + .../share/vm/services/diagnosticCommand.cpp | 13 + + .../share/vm/services/diagnosticCommand.hpp | 23 + + hotspot/src/share/vm/utilities/array.hpp | 1 + + hotspot/src/share/vm/utilities/bitMap.cpp | 17 +- + hotspot/src/share/vm/utilities/bitMap.hpp | 1 + + .../src/share/vm/utilities/constantTag.hpp | 5 +- + .../share/vm/utilities/globalDefinitions.hpp | 11 +- + hotspot/src/share/vm/utilities/hashtable.cpp | 60 +- + hotspot/src/share/vm/utilities/hashtable.hpp | 98 +- + .../share/vm/utilities/hashtable.inline.hpp | 2 +- + hotspot/src/share/vm/utilities/ostream.cpp | 11 + + hotspot/src/share/vm/utilities/ostream.hpp | 2 +- + .../src/share/vm/utilities/resourceHash.hpp | 27 +- + 77 files changed, 6234 insertions(+), 295 deletions(-) + create mode 100644 hotspot/src/share/vm/cds/archiveBuilder.cpp + create mode 100644 hotspot/src/share/vm/cds/archiveBuilder.hpp + create mode 100644 hotspot/src/share/vm/cds/archiveUtils.cpp + create mode 100644 hotspot/src/share/vm/cds/archiveUtils.hpp + create mode 100644 hotspot/src/share/vm/cds/dumpAllocStats.cpp + create mode 100644 hotspot/src/share/vm/cds/dumpAllocStats.hpp + create mode 100644 hotspot/src/share/vm/cds/dynamicArchive.cpp + create mode 100644 hotspot/src/share/vm/cds/dynamicArchive.hpp + create mode 100644 hotspot/src/share/vm/classfile/compactHashtable.cpp + create mode 100644 hotspot/src/share/vm/classfile/compactHashtable.hpp + create mode 100644 hotspot/src/share/vm/classfile/systemDictionaryShared.cpp + create mode 100644 hotspot/src/share/vm/memory/metaspaceClosure.cpp + create mode 100644 hotspot/src/share/vm/memory/metaspaceClosure.hpp + +diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp +index f700335a3..6dbedf5c2 100644 +--- a/hotspot/src/os/linux/vm/os_linux.cpp ++++ b/hotspot/src/os/linux/vm/os_linux.cpp +@@ -2370,8 +2370,7 @@ void os::print_siginfo(outputStream* st, void* siginfo) { + #if INCLUDE_CDS + if (si && (si->si_signo == SIGBUS || si->si_signo == SIGSEGV) && + UseSharedSpaces) { +- FileMapInfo* mapinfo = FileMapInfo::current_info(); +- if (mapinfo->is_in_shared_space(si->si_addr)) { ++ if (MetaspaceShared::is_in_shared_space(si->si_addr)) { + st->print("\n\nError accessing class data sharing archive." \ + " Mapped file inaccessible during execution, " \ + " possible disk/network problem."); +diff --git a/hotspot/src/share/vm/cds/archiveBuilder.cpp b/hotspot/src/share/vm/cds/archiveBuilder.cpp +new file mode 100644 +index 000000000..144dedfa9 +--- /dev/null ++++ b/hotspot/src/share/vm/cds/archiveBuilder.cpp +@@ -0,0 +1,807 @@ ++/* ++ * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "cds/archiveBuilder.hpp" ++#include "cds/archiveUtils.hpp" ++#include "classfile/symbolTable.hpp" ++#include "classfile/systemDictionaryShared.hpp" ++#include "interpreter/abstractInterpreter.hpp" ++#include "memory/filemap.hpp" ++#include "memory/memRegion.hpp" ++#include "memory/metaspaceShared.hpp" ++#include "memory/resourceArea.hpp" ++#include "oops/instanceKlass.hpp" ++#include "oops/objArrayKlass.hpp" ++#include "runtime/arguments.hpp" ++#include "runtime/globals_extension.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/thread.hpp" ++#include "utilities/align.hpp" ++#include "utilities/bitMap.inline.hpp" ++#include "utilities/hashtable.inline.hpp" ++ ++ArchiveBuilder* ArchiveBuilder::_current = NULL; ++ ++ArchiveBuilder::OtherROAllocMark::~OtherROAllocMark() { ++ char* newtop = ArchiveBuilder::current()->_ro_region.top(); ++ ArchiveBuilder::alloc_stats()->record_other_type(int(newtop - _oldtop), true); ++} ++ ++ArchiveBuilder::SourceObjList::SourceObjList() : _ptrmap(16 * K, false) { ++ _total_bytes = 0; ++ _objs = new (ResourceObj::C_HEAP, mtClassShared) GrowableArray(128 * K, mtClassShared); ++} ++ ++ArchiveBuilder::SourceObjList::~SourceObjList() { ++ delete _objs; ++} ++ ++void ArchiveBuilder::SourceObjList::append(MetaspaceClosure::Ref* enclosing_ref, SourceObjInfo* src_info) { ++ // Save this source object for copying ++ _objs->append(src_info); ++ ++ // Prepare for marking the pointers in this source object ++ assert(is_aligned(_total_bytes, sizeof(address)), "must be"); ++ src_info->set_ptrmap_start(_total_bytes / sizeof(address)); ++ _total_bytes = align_up(_total_bytes + (uintx)src_info->size_in_bytes(), sizeof(address)); ++ src_info->set_ptrmap_end(_total_bytes / sizeof(address)); ++ ++ BitMap::idx_t bitmap_size_needed = BitMap::idx_t(src_info->ptrmap_end()); ++ if (_ptrmap.size() <= bitmap_size_needed) { ++ _ptrmap.resize((bitmap_size_needed + 1) * 2, false); ++ } ++} ++ ++class PrintBitMap : public BitMapClosure { ++ public: ++ bool do_bit(BitMap::idx_t bit_offset) { ++ tty->print_cr("PrintBitMap : " SIZE_FORMAT, bit_offset); ++ return true; ++ } ++}; ++ ++void ArchiveBuilder::SourceObjList::remember_embedded_pointer(SourceObjInfo* src_info, MetaspaceClosure::Ref* ref) { ++ // src_obj contains a pointer. Remember the location of this pointer in _ptrmap, ++ // so that we can copy/relocate it later. E.g., if we have ++ // class Foo { intx scala; Bar* ptr; } ++ // Foo *f = 0x100; ++ // To mark the f->ptr pointer on 64-bit platform, this function is called with ++ // src_info()->obj() == 0x100 ++ // ref->addr() == 0x108 ++ address src_obj = src_info->obj(); ++ address* field_addr = ref->addr(); ++ assert(src_info->ptrmap_start() < _total_bytes, "sanity"); ++ assert(src_info->ptrmap_end() <= _total_bytes, "sanity"); ++ assert(*field_addr != NULL, "should have checked"); ++ ++ intx field_offset_in_bytes = ((address)field_addr) - src_obj; ++ DEBUG_ONLY(int src_obj_size = src_info->size_in_bytes();) ++ assert(field_offset_in_bytes >= 0, "must be"); ++ assert(field_offset_in_bytes + intx(sizeof(intptr_t)) <= intx(src_obj_size), "must be"); ++ assert(is_aligned(field_offset_in_bytes, sizeof(address)), "must be"); ++ ++ BitMap::idx_t idx = BitMap::idx_t(src_info->ptrmap_start() + (uintx)(field_offset_in_bytes / sizeof(address))); ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("remember_embedded_pointer: _ptrmap_start: " SIZE_FORMAT ++ "_ptrmap_end: " SIZE_FORMAT ++ " field: " PTR_FORMAT" -> " PTR_FORMAT ++ " bit_index: " SIZE_FORMAT " ", ++ src_info->ptrmap_start(), src_info->ptrmap_end(), p2i(src_obj), p2i(field_addr), idx); ++ } ++ _ptrmap.set_bit(BitMap::idx_t(idx)); ++} ++ ++class RelocateEmbeddedPointers : public BitMapClosure { ++ ArchiveBuilder* _builder; ++ address _dumped_obj; ++ BitMap::idx_t _start_idx; ++public: ++ RelocateEmbeddedPointers(ArchiveBuilder* builder, address dumped_obj, BitMap::idx_t start_idx) : ++ _builder(builder), _dumped_obj(dumped_obj), _start_idx(start_idx) {} ++ ++ bool do_bit(BitMap::idx_t bit_offset) { ++ uintx FLAG_MASK = 0x03; // See comments around MetaspaceClosure::FLAG_MASK ++ size_t field_offset = size_t(bit_offset - _start_idx) * sizeof(address); ++ address* ptr_loc = (address*)(_dumped_obj + field_offset); ++ uintx old_p_and_bits = (uintx)(*ptr_loc); ++ uintx flag_bits = (old_p_and_bits & FLAG_MASK); ++ address old_p = (address)(old_p_and_bits & (~FLAG_MASK)); ++ address new_p = _builder->get_dumped_addr(old_p); ++ uintx new_p_and_bits = ((uintx)new_p) | flag_bits; ++ ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Ref: [" PTR_FORMAT "] -> " PTR_FORMAT " => " PTR_FORMAT, ++ p2i(ptr_loc), p2i(old_p), p2i(new_p)); ++ } ++ ArchivePtrMarker::set_and_mark_pointer(ptr_loc, (address)(new_p_and_bits)); ++ return true; // keep iterating the bitmap ++ } ++}; ++ ++void ArchiveBuilder::SourceObjList::relocate(int i, ArchiveBuilder* builder) { ++ SourceObjInfo* src_info = objs()->at(i); ++ assert(src_info->should_copy(), "must be"); ++ BitMap::idx_t start = BitMap::idx_t(src_info->ptrmap_start()); // inclusive ++ BitMap::idx_t end = BitMap::idx_t(src_info->ptrmap_end()); // exclusive ++ ++ RelocateEmbeddedPointers relocator(builder, src_info->dumped_addr(), start); ++ _ptrmap.iterate(&relocator, start, end); ++} ++ ++ArchiveBuilder::ArchiveBuilder() : ++ _current_dump_space(NULL), ++ _buffer_bottom(NULL), ++ _last_verified_top(NULL), ++ _num_dump_regions_used(0), ++ _other_region_used_bytes(0), ++ _requested_static_archive_bottom(NULL), ++ _requested_static_archive_top(NULL), ++ _requested_dynamic_archive_bottom(NULL), ++ _requested_dynamic_archive_top(NULL), ++ _mapped_static_archive_bottom(NULL), ++ _mapped_static_archive_top(NULL), ++ _buffer_to_requested_delta(0), ++ _rw_region("rw", MAX_SHARED_DELTA), ++ _ro_region("ro", MAX_SHARED_DELTA), ++ _rw_src_objs(), ++ _ro_src_objs(), ++ _src_obj_table(INITIAL_TABLE_SIZE), ++ _num_instance_klasses(0), ++ _num_obj_array_klasses(0), ++ _num_type_array_klasses(0), ++ _estimated_metaspaceobj_bytes(0), ++ _estimated_hashtable_bytes(0) { ++ _klasses = new (ResourceObj::C_HEAP, mtClassShared) GrowableArray(4 * K, mtClassShared); ++ _symbols = new (ResourceObj::C_HEAP, mtClassShared) GrowableArray(256 * K, mtClassShared); ++ ++ assert(_current == NULL, "must be"); ++ _current = this; ++} ++ ++ArchiveBuilder::~ArchiveBuilder() { ++ assert(_current == this, "must be"); ++ _current = NULL; ++ ++ clean_up_src_obj_table(); ++ ++ for (int i = 0; i < _symbols->length(); i++) { ++ _symbols->at(i)->decrement_refcount(); ++ } ++ ++ delete _klasses; ++ delete _symbols; ++ if (_shared_rs.is_reserved()) { ++ _shared_rs.release(); ++ } ++} ++ ++bool ArchiveBuilder::gather_one_source_obj(MetaspaceClosure::Ref* enclosing_ref, ++ MetaspaceClosure::Ref* ref, bool read_only) { ++ address src_obj = ref->obj(); ++ if (src_obj == NULL) { ++ return false; ++ } ++ ref->set_keep_after_pushing(); ++ remember_embedded_pointer_in_copied_obj(enclosing_ref, ref); ++ ++ FollowMode follow_mode = get_follow_mode(ref); ++ SourceObjInfo src_info(ref, read_only, follow_mode); ++ bool created; ++ SourceObjInfo* p = _src_obj_table.add_if_absent(src_obj, src_info, &created); ++ if (created) { ++ if (_src_obj_table.maybe_grow(MAX_TABLE_SIZE)) { ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Expanded _src_obj_table table to %d", _src_obj_table.table_size()); ++ } ++ } ++ } ++ ++ assert(p->read_only() == src_info.read_only(), "must be"); ++ ++ if (created && src_info.should_copy()) { ++ ref->set_user_data((void*)p); ++ if (read_only) { ++ _ro_src_objs.append(enclosing_ref, p); ++ } else { ++ _rw_src_objs.append(enclosing_ref, p); ++ } ++ return true; // Need to recurse into this ref only if we are copying it ++ } else { ++ return false; ++ } ++} ++ ++void ArchiveBuilder::iterate_sorted_roots(MetaspaceClosure* it, bool is_relocating_pointers) { ++ int i; ++ ++ if (!is_relocating_pointers) { ++ // Don't relocate _symbol, so we can safely call decrement_refcount on the ++ // original symbols. ++ int num_symbols = _symbols->length(); ++ for (i = 0; i < num_symbols; i++) { ++ it->push(_symbols->adr_at(i)); ++ } ++ } ++ ++ int num_klasses = _klasses->length(); ++ for (i = 0; i < num_klasses; i++) { ++ it->push(_klasses->adr_at(i)); ++ } ++ ++ iterate_roots(it, is_relocating_pointers); ++} ++ ++class GatherSortedSourceObjs : public MetaspaceClosure { ++ ArchiveBuilder* _builder; ++ ++public: ++ GatherSortedSourceObjs(ArchiveBuilder* builder) : _builder(builder) {} ++ ++ virtual bool do_ref(Ref* ref, bool read_only) { ++ return _builder->gather_one_source_obj(enclosing_ref(), ref, read_only); ++ } ++ ++ virtual void do_pending_ref(Ref* ref) { ++ if (ref->obj() != NULL) { ++ _builder->remember_embedded_pointer_in_copied_obj(enclosing_ref(), ref); ++ } ++ } ++}; ++ ++void ArchiveBuilder::gather_source_objs() { ++ ResourceMark rm; ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Gathering all archivable objects ... "); ++ } ++ gather_klasses_and_symbols(); ++ GatherSortedSourceObjs doit(this); ++ iterate_sorted_roots(&doit, /*is_relocating_pointers=*/false); ++ doit.finish(); ++} ++ ++bool ArchiveBuilder::is_excluded(Klass* klass) { ++ if (klass->oop_is_instance()) { ++ InstanceKlass* ik = InstanceKlass::cast(klass); ++ return SystemDictionaryShared::is_excluded_class(ik); ++ } else if (klass->oop_is_objArray()) { ++ if (DynamicDumpSharedSpaces) { ++ // Don't support archiving of array klasses for now (WHY???) ++ return true; ++ } ++ Klass* bottom = ObjArrayKlass::cast(klass)->bottom_klass(); ++ if (bottom->oop_is_instance()) { ++ return SystemDictionaryShared::is_excluded_class(InstanceKlass::cast(bottom)); ++ } ++ } ++ ++ return false; ++} ++ ++ArchiveBuilder::FollowMode ArchiveBuilder::get_follow_mode(MetaspaceClosure::Ref *ref) { ++ address obj = ref->obj(); ++ if (MetaspaceShared::is_in_shared_space(obj)) { ++ // Don't dump existing shared metadata again. ++ return point_to_it; ++ } else if (ref->msotype() == MetaspaceObj::MethodDataType) { ++ return set_to_null; ++ } else { ++ if (ref->msotype() == MetaspaceObj::ClassType) { ++ Klass* klass = (Klass*)ref->obj(); ++ assert(klass->is_klass(), "must be"); ++ if (is_excluded(klass)) { ++ if (TraceDynamicCDS) { ++ ResourceMark rm; ++ dynamic_cds_log->print_cr("Skipping class (excluded): %s", klass->external_name()); ++ } ++ return set_to_null; ++ } ++ } ++ ++ return make_a_copy; ++ } ++} ++ ++int ArchiveBuilder::compare_symbols_by_address(Symbol** a, Symbol** b) { ++ if (a[0] < b[0]) { ++ return -1; ++ } else { ++ assert(a[0] > b[0], "Duplicated symbol unexpected"); ++ return 1; ++ } ++} ++ ++int ArchiveBuilder::compare_klass_by_name(Klass** a, Klass** b) { ++ return a[0]->name()->fast_compare(b[0]->name()); ++} ++ ++void ArchiveBuilder::sort_klasses() { ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Sorting classes ... "); ++ } ++ _klasses->sort(compare_klass_by_name); ++} ++ ++class GatherKlassesAndSymbols : public UniqueMetaspaceClosure { ++ ArchiveBuilder* _builder; ++ ++public: ++ GatherKlassesAndSymbols(ArchiveBuilder* builder) : _builder(builder) { } ++ ++ virtual bool do_unique_ref(Ref* ref, bool read_only) { ++ return _builder->gather_klass_and_symbol(ref, read_only); ++ } ++}; ++ ++void ArchiveBuilder::gather_klasses_and_symbols() { ++ ResourceMark rm; ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Gathering classes and symbols ... "); ++ } ++ GatherKlassesAndSymbols doit(this); ++ iterate_roots(&doit, false); ++ doit.finish(); ++ ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Number of classes %d", _num_instance_klasses + _num_obj_array_klasses + _num_type_array_klasses); ++ dynamic_cds_log->print_cr(" instance classes = %5d", _num_instance_klasses); ++ dynamic_cds_log->print_cr(" obj array classes = %5d", _num_obj_array_klasses); ++ dynamic_cds_log->print_cr(" type array classes = %5d", _num_type_array_klasses); ++ dynamic_cds_log->print_cr(" symbols = %5d", _symbols->length()); ++ } ++} ++ ++bool ArchiveBuilder::gather_klass_and_symbol(MetaspaceClosure::Ref* ref, bool read_only) { ++ if (ref->obj() == NULL) { ++ return false; ++ } ++ if (get_follow_mode(ref) != make_a_copy) { ++ return false; ++ } ++ if (ref->msotype() == MetaspaceObj::ClassType) { ++ Klass* klass = (Klass*)ref->obj(); ++ assert(klass->is_klass(), "must be"); ++ if (!is_excluded(klass)) { ++ _klasses->append(klass); ++ if (klass->oop_is_instance()) { ++ _num_instance_klasses ++; ++ } else if (klass->oop_is_objArray()) { ++ _num_obj_array_klasses ++; ++ } else { ++ assert(klass->oop_is_typeArray(), "sanity"); ++ _num_type_array_klasses ++; ++ } ++ } ++ // See RunTimeSharedClassInfo::get_for() ++ _estimated_metaspaceobj_bytes += align_up(BytesPerWord, KlassAlignmentInBytes); ++ } else if (ref->msotype() == MetaspaceObj::SymbolType) { ++ // Make sure the symbol won't be GC'ed while we are dumping the archive. ++ Symbol* sym = (Symbol*)ref->obj(); ++ sym->increment_refcount(); ++ _symbols->append(sym); ++ } ++ ++ int bytes = ref->size() * BytesPerWord; ++ _estimated_metaspaceobj_bytes += align_up(bytes, KlassAlignmentInBytes); ++ return true; // recurse ++} ++ ++size_t ArchiveBuilder::estimate_archive_size() { ++ // size of the symbol table and two dictionaries, plus the RunTimeSharedClassInfo's ++ size_t symbol_table_est = SymbolTable::estimate_size_for_archive(); ++ size_t dictionary_est = SystemDictionaryShared::estimate_size_for_archive(); ++ _estimated_hashtable_bytes = symbol_table_est + dictionary_est; ++ ++ size_t total = 0; ++ ++ total += _estimated_metaspaceobj_bytes; ++ total += _estimated_hashtable_bytes; ++ ++ // allow fragmentation at the end of each dump region ++ total += _total_dump_regions * ((size_t)os::vm_allocation_granularity()); ++ ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("_estimated_hashtable_bytes = " SIZE_FORMAT " + " SIZE_FORMAT " = " SIZE_FORMAT, ++ symbol_table_est, dictionary_est, _estimated_hashtable_bytes); ++ dynamic_cds_log->print_cr("_estimated_metaspaceobj_bytes = " SIZE_FORMAT, _estimated_metaspaceobj_bytes); ++ dynamic_cds_log->print_cr("total estimate bytes = " SIZE_FORMAT, total); ++ } ++ ++ return align_up(total, (size_t)os::vm_allocation_granularity()); ++} ++ ++address ArchiveBuilder::reserve_buffer() { ++ size_t buffer_size = estimate_archive_size(); ++ ReservedSpace rs(buffer_size, os::vm_allocation_granularity(), false); ++ if (!rs.is_reserved()) { ++ tty->print_cr("Failed to reserve " SIZE_FORMAT " bytes of output buffer.", buffer_size); ++ vm_direct_exit(0); ++ } ++ ++ // buffer_bottom is the lowest address of the 2 core regions (rw, ro) when ++ // we are copying the class metadata into the buffer. ++ address buffer_bottom = (address)rs.base(); ++ _shared_rs = rs; ++ ++ _buffer_bottom = buffer_bottom; ++ _last_verified_top = buffer_bottom; ++ _current_dump_space = &_rw_region; ++ _num_dump_regions_used = 1; ++ _other_region_used_bytes = 0; ++ _current_dump_space->init(&_shared_rs, &_shared_vs); ++ ++ ArchivePtrMarker::initialize(&_ptrmap, &_shared_vs); ++ ++ // The bottom of the static archive should be mapped at this address by default. ++ _requested_static_archive_bottom = (address)MetaspaceShared::requested_base_address(); ++ ++ size_t static_archive_size = FileMapInfo::shared_spaces_size(); ++ _requested_static_archive_top = _requested_static_archive_bottom + static_archive_size; ++ ++ _mapped_static_archive_bottom = (address)MetaspaceShared::shared_metaspace_static_bottom(); ++ _mapped_static_archive_top = _mapped_static_archive_bottom + static_archive_size; ++ ++ _requested_dynamic_archive_bottom = align_up(_requested_static_archive_top, (size_t)os::vm_allocation_granularity()); ++ ++ _buffer_to_requested_delta = _requested_dynamic_archive_bottom - _buffer_bottom; ++ ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Reserved output buffer space at " PTR_FORMAT " [" SIZE_FORMAT " bytes]", ++ p2i(buffer_bottom), buffer_size); ++ dynamic_cds_log->print_cr("Dynamic archive mapped space at " PTR_FORMAT, p2i(_requested_dynamic_archive_bottom)); ++ } ++ ++ return buffer_bottom; ++} ++ ++void ArchiveBuilder::verify_estimate_size(size_t estimate, const char* which) { ++ address bottom = _last_verified_top; ++ address top = (address)(current_dump_space()->top()); ++ size_t used = size_t(top - bottom) + _other_region_used_bytes; ++ int diff = int(estimate) - int(used); ++ ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("%s estimate = " SIZE_FORMAT " used = " SIZE_FORMAT "; diff = %d bytes", which, estimate, used, diff); ++ } ++ assert(diff >= 0, "Estimate is too small"); ++ ++ _last_verified_top = top; ++ _other_region_used_bytes = 0; ++} ++ ++void ArchiveBuilder::dump_rw_metadata() { ++ ResourceMark rm; ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Allocating RW objects ... "); ++ } ++ make_shallow_copies(&_rw_region, &_rw_src_objs); ++} ++ ++void ArchiveBuilder::dump_ro_metadata() { ++ ResourceMark rm; ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Allocating RO objects ... "); ++ } ++ start_dump_space(&_ro_region); ++ make_shallow_copies(&_ro_region, &_ro_src_objs); ++} ++ ++void ArchiveBuilder::start_dump_space(DumpRegion* next) { ++ address bottom = _last_verified_top; ++ address top = (address)(_current_dump_space->top()); ++ _other_region_used_bytes += size_t(top - bottom); ++ _current_dump_space->pack(next); ++ _current_dump_space = next; ++ _num_dump_regions_used ++; ++ _last_verified_top = (address)(_current_dump_space->top()); ++} ++ ++void ArchiveBuilder::patch_shared_obj_vtable() { ++ SourceObjList* objs = &_rw_src_objs; ++ ++ for (int i = 0; i < objs->objs()->length(); i++) { ++ SourceObjInfo* src_info = objs->objs()->at(i); ++ address dest = src_info->dumped_addr(); ++ MetaspaceClosure::Ref* ref = src_info->ref(); ++ intptr_t* archived_vtable = MetaspaceShared::get_archived_vtable(ref->msotype(), dest); ++ if (archived_vtable != NULL) { ++ // When we copy archived vtable from base archive into dynamic archive's objs, we can't call ++ // virtual function before restore dynamic archive. ++ *(intptr_t**)dest = archived_vtable; ++ ArchivePtrMarker::mark_pointer((address*)dest); ++ } ++ } ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("patch vtable done (%d objects)", objs->objs()->length()); ++ } ++} ++ ++void ArchiveBuilder::remember_embedded_pointer_in_copied_obj(MetaspaceClosure::Ref* enclosing_ref, ++ MetaspaceClosure::Ref* ref) { ++ assert(ref->obj() != NULL, "should have checked"); ++ ++ if (enclosing_ref != NULL) { ++ SourceObjInfo* src_info = (SourceObjInfo*)enclosing_ref->user_data(); ++ if (src_info == NULL) { ++ // source objects of point_to_it/set_to_null types are not copied ++ // so we don't need to remember their pointers. ++ } else { ++ if (src_info->read_only()) { ++ _ro_src_objs.remember_embedded_pointer(src_info, ref); ++ } else { ++ _rw_src_objs.remember_embedded_pointer(src_info, ref); ++ } ++ } ++ } ++} ++ ++void ArchiveBuilder::make_shallow_copies(DumpRegion *dump_region, ++ const ArchiveBuilder::SourceObjList* src_objs) { ++ for (int i = 0; i < src_objs->objs()->length(); i++) { ++ make_shallow_copy(dump_region, src_objs->objs()->at(i)); ++ } ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("done (%d objects)", src_objs->objs()->length()); ++ } ++} ++ ++void ArchiveBuilder::make_shallow_copy(DumpRegion *dump_region, SourceObjInfo* src_info) { ++ MetaspaceClosure::Ref* ref = src_info->ref(); ++ address src = ref->obj(); ++ int bytes = src_info->size_in_bytes(); ++ char* dest; ++ char* oldtop; ++ char* newtop; ++ ++ oldtop = dump_region->top(); ++ if (ref->msotype() == MetaspaceObj::ClassType) { ++ // Save a pointer immediate in front of an InstanceKlass, so ++ // we can do a quick lookup from InstanceKlass* -> RunTimeSharedClassInfo* ++ // without building another hashtable. See RunTimeSharedClassInfo::get_for() ++ // in systemDictionaryShared.cpp. ++ Klass* klass = (Klass*)src; ++ if (klass->oop_is_instance()) { ++ dump_region->allocate(sizeof(address)); ++ } ++ } ++ dest = dump_region->allocate(bytes); ++ newtop = dump_region->top(); ++ ++ memcpy(dest, src, bytes); ++ ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Copy: " PTR_FORMAT " ==> " PTR_FORMAT " %d", p2i(src), p2i(dest), bytes); ++ } ++ src_info->set_dumped_addr((address)dest); ++ ++ _alloc_stats.record(ref->msotype(), int(newtop - oldtop), src_info->read_only()); ++} ++ ++address ArchiveBuilder::get_dumped_addr(address src_obj) { ++ SourceObjInfo* p = _src_obj_table.lookup(src_obj); ++ assert(p != NULL, "must be"); ++ ++ return p->dumped_addr(); ++} ++ ++void ArchiveBuilder::relocate_embedded_pointers(ArchiveBuilder::SourceObjList* src_objs) { ++ for (int i = 0; i < src_objs->objs()->length(); i++) { ++ src_objs->relocate(i, this); ++ } ++} ++ ++void ArchiveBuilder::print_stats() { ++ _alloc_stats.print_stats(int(_ro_region.used()), int(_rw_region.used())); ++} ++ ++void ArchiveBuilder::make_klasses_shareable() { ++ for (int i = 0; i < klasses()->length(); i++) { ++ Klass* k = klasses()->at(i); ++ k->remove_java_mirror(); ++ if (k->oop_is_objArray()) { ++ // InstanceKlass and TypeArrayKlass will in turn call remove_unshareable_info ++ // on their array classes. ++ } else if (k->oop_is_typeArray()) { ++ k->remove_unshareable_info(); ++ } else { ++ assert(k->oop_is_instance(), " must be"); ++ InstanceKlass* ik = InstanceKlass::cast(k); ++ // High version introduce fast bytecode, jdk8 no need do it. ++ // MetaspaceShared::rewrite_nofast_bytecodes_and_calculate_fingerprints(Thread::current(), ik); ++ ik->remove_unshareable_info(); // assign_class_loader_type is in Klass::remove_unshareable_info ++ ++ if (DebugDynamicCDS) { ++ ResourceMark rm; ++ dynamic_cds_log->print_cr("klasses[%4d] = " PTR_FORMAT " => " PTR_FORMAT " %s", i, p2i(ik), p2i(to_requested(ik)), ik->external_name()); ++ } ++ } ++ } ++} ++ ++uintx ArchiveBuilder::buffer_to_offset(address p) const { ++ address requested_p = to_requested(p); ++ assert(requested_p >= _requested_static_archive_bottom, "must be"); ++ return requested_p - _requested_static_archive_bottom; ++} ++ ++uintx ArchiveBuilder::any_to_offset(address p) const { ++ if (is_in_mapped_static_archive(p)) { ++ assert(DynamicDumpSharedSpaces, "must be"); ++ return p - _mapped_static_archive_bottom; ++ } ++ return buffer_to_offset(p); ++} ++ ++// RelocateBufferToRequested --- Relocate all the pointers in rw/ro, ++// so that the archive can be mapped to the "requested" location without runtime relocation. ++// ++// - See ArchiveBuilder header for the definition of "buffer", "mapped" and "requested" ++// - ArchivePtrMarker::ptrmap() marks all the pointers in the rw/ro regions ++// - Every pointer must have one of the following values: ++// [a] NULL: ++// No relocation is needed. Remove this pointer from ptrmap so we don't need to ++// consider it at runtime. ++// [b] Points into an object X which is inside the buffer: ++// Adjust this pointer by _buffer_to_requested_delta, so it points to X ++// when the archive is mapped at the requested location. ++// [c] Points into an object Y which is inside mapped static archive: ++// - This happens only during dynamic dump ++// - Adjust this pointer by _mapped_to_requested_static_archive_delta, ++// so it points to Y when the static archive is mapped at the requested location. ++class RelocateBufferToRequested : public BitMapClosure { ++ ArchiveBuilder* _builder; ++ address _buffer_bottom; ++ intx _buffer_to_requested_delta; ++ intx _mapped_to_requested_static_archive_delta; ++ size_t _max_non_null_offset; ++ ++ public: ++ RelocateBufferToRequested(ArchiveBuilder* builder) { ++ _builder = builder; ++ _buffer_bottom = _builder->buffer_bottom(); ++ _buffer_to_requested_delta = builder->buffer_to_requested_delta(); ++ _mapped_to_requested_static_archive_delta = builder->requested_static_archive_bottom() - builder->mapped_static_archive_bottom(); ++ _max_non_null_offset = 0; ++ ++ address bottom = _builder->buffer_bottom(); ++ address top = _builder->buffer_top(); ++ address new_bottom = bottom + _buffer_to_requested_delta; ++ address new_top = top + _buffer_to_requested_delta; ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Relocating archive from [" INTPTR_FORMAT " - " INTPTR_FORMAT "] to " ++ "[" INTPTR_FORMAT " - " INTPTR_FORMAT "]", ++ p2i(bottom), p2i(top), ++ p2i(new_bottom), p2i(new_top)); ++ } ++ } ++ ++ bool do_bit(size_t offset) { ++ address* p = (address*)_buffer_bottom + offset; ++ assert(_builder->is_in_buffer_space(p), "pointer must live in buffer space"); ++ ++ if (*p == NULL) { ++ // todo -- clear bit, etc ++ ArchivePtrMarker::ptrmap()->clear_bit(offset); ++ } else { ++ if (_builder->is_in_buffer_space(*p)) { ++ *p += _buffer_to_requested_delta; ++ // assert is in requested dynamic archive ++ } else { ++ assert(_builder->is_in_mapped_static_archive(*p), "old pointer must point inside buffer space or mapped static archive"); ++ *p += _mapped_to_requested_static_archive_delta; ++ assert(_builder->is_in_requested_static_archive(*p), "new pointer must point inside requested archive"); ++ } ++ ++ _max_non_null_offset = offset; ++ } ++ ++ return true; // keep iterating ++ } ++ ++ void doit() { ++ ArchivePtrMarker::ptrmap()->iterate(this); ++ ArchivePtrMarker::compact(_max_non_null_offset); ++ } ++}; ++ ++void ArchiveBuilder::relocate_to_requested() { ++ ro_region()->pack(); ++ ++ size_t my_archive_size = buffer_top() - buffer_bottom(); ++ ++ assert(DynamicDumpSharedSpaces, "must be"); ++ _requested_dynamic_archive_top = _requested_dynamic_archive_bottom + my_archive_size; ++ RelocateBufferToRequested patcher(this); ++ patcher.doit(); ++} ++ ++void ArchiveBuilder::clean_up_src_obj_table() { ++ SrcObjTableCleaner cleaner; ++ _src_obj_table.iterate(&cleaner); ++} ++ ++void ArchiveBuilder::write_archive(FileMapInfo* mapinfo) { ++ assert(mapinfo->header()->magic() == CDS_DYNAMIC_ARCHIVE_MAGIC, "Dynamic CDS calls only"); ++ ++ mapinfo->write_dynamic_header(); ++ ++ write_region(mapinfo, MetaspaceShared::d_rw, &_rw_region, /*read_only=*/false,/*allow_exec=*/false); ++ write_region(mapinfo, MetaspaceShared::d_ro, &_ro_region, /*read_only=*/true, /*allow_exec=*/false); ++ ++ char* bitmap = mapinfo->write_bitmap_region(ArchivePtrMarker::ptrmap()); ++ ++ if (InfoDynamicCDS && mapinfo->is_open()) { ++ print_stats(); ++ } ++ ++ mapinfo->close(); ++ FREE_C_HEAP_ARRAY(char, bitmap, mtClassShared); ++} ++ ++void ArchiveBuilder::write_region(FileMapInfo* mapinfo, int region_idx, DumpRegion* dump_region, bool read_only, bool allow_exec) { ++ mapinfo->write_region(region_idx, dump_region->base(), dump_region->used(), dump_region->used(), read_only, allow_exec); ++} ++ ++class RefRelocator: public MetaspaceClosure { ++ ArchiveBuilder* _builder; ++ ++public: ++ RefRelocator(ArchiveBuilder* builder) : _builder(builder) {} ++ ++ virtual bool do_ref(Ref* ref, bool read_only) { ++ if (ref->not_null()) { ++ ref->update(_builder->get_dumped_addr(ref->obj())); ++ ArchivePtrMarker::mark_pointer(ref->addr()); ++ } ++ return false; // Do not recurse. ++ } ++}; ++ ++void ArchiveBuilder::relocate_roots() { ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Relocating external roots ... "); ++ } ++ ResourceMark rm; ++ RefRelocator doit(this); ++ iterate_sorted_roots(&doit, /*is_relocating_pointers=*/true); ++ doit.finish(); ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("done"); ++ } ++} ++ ++void ArchiveBuilder::relocate_metaspaceobj_embedded_pointers() { ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Relocating embedded pointers in core regions ... "); ++ } ++ relocate_embedded_pointers(&_rw_src_objs); ++ relocate_embedded_pointers(&_ro_src_objs); ++} ++ ++#ifndef PRODUCT ++void ArchiveBuilder::assert_is_vm_thread() { ++ assert(Thread::current()->is_VM_thread(), "ArchiveBuilder should be used only inside the VMThread"); ++} ++#endif +diff --git a/hotspot/src/share/vm/cds/archiveBuilder.hpp b/hotspot/src/share/vm/cds/archiveBuilder.hpp +new file mode 100644 +index 000000000..18cd3c622 +--- /dev/null ++++ b/hotspot/src/share/vm/cds/archiveBuilder.hpp +@@ -0,0 +1,368 @@ ++/* ++ * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef SHARE_VM_CDS_ARCHIVEBUILDER_HPP ++#define SHARE_VM_CDS_ARCHIVEBUILDER_HPP ++ ++#include "cds/archiveUtils.hpp" ++#include "cds/dumpAllocStats.hpp" ++#include "memory/metaspaceClosure.hpp" ++//#include "oops/array.hpp" ++#include "oops/klass.hpp" ++#include "runtime/os.hpp" ++#include "utilities/align.hpp" ++#include "utilities/bitMap.hpp" ++#include "utilities/growableArray.hpp" ++#include "utilities/hashtable.hpp" ++#include "utilities/resourceHash.hpp" ++ ++class FileMapInfo; ++// Overview of CDS archive creation (for both static??? and dynamic dump): ++// ++// [1] Load all classes (static dump: from the classlist, dynamic dump: as part of app execution) ++// [2] Allocate "output buffer" ++// [3] Copy contents of the 2 "core" regions (rw/ro) into the output buffer. ++// - allocate the cpp vtables in rw (static dump only) ++// - memcpy the MetaspaceObjs into rw/ro: ++// dump_rw_region(); ++// dump_ro_region(); ++// - fix all the pointers in the MetaspaceObjs to point to the copies ++// relocate_metaspaceobj_embedded_pointers() ++// [4] Copy symbol table, dictionary, etc, into the ro region ++// [5] Relocate all the pointers in rw/ro, so that the archive can be mapped to ++// the "requested" location without runtime relocation. See relocate_to_requested() ++class ArchiveBuilder : public StackObj { ++protected: ++ DumpRegion* _current_dump_space; ++ address _buffer_bottom; // for writing the contents of rw/ro regions ++ address _last_verified_top; ++ int _num_dump_regions_used; ++ size_t _other_region_used_bytes; ++ ++ // These are the addresses where we will request the static and dynamic archives to be ++ // mapped at run time. If the request fails (due to ASLR), we will map the archives at ++ // os-selected addresses. ++ address _requested_static_archive_bottom; // This is determined solely by the value of ++ // SharedBaseAddress during -Xshare:dump. ++ address _requested_static_archive_top; ++ address _requested_dynamic_archive_bottom; // Used only during dynamic dump. It's placed ++ // immediately above _requested_static_archive_top. ++ address _requested_dynamic_archive_top; ++ ++ // (Used only during dynamic dump) where the static archive is actually mapped. This ++ // may be different than _requested_static_archive_{bottom,top} due to ASLR ++ address _mapped_static_archive_bottom; ++ address _mapped_static_archive_top; ++ ++ intx _buffer_to_requested_delta; ++ ++ DumpRegion* current_dump_space() const { return _current_dump_space; } ++ ++public: ++ enum FollowMode { ++ make_a_copy, point_to_it, set_to_null ++ }; ++ ++private: ++ class SourceObjInfo { ++ MetaspaceClosure::Ref* _ref; ++ uintx _ptrmap_start; // The bit-offset of the start of this object (inclusive) ++ uintx _ptrmap_end; // The bit-offset of the end of this object (exclusive) ++ bool _read_only; ++ FollowMode _follow_mode; ++ int _size_in_bytes; ++ MetaspaceObj::Type _msotype; ++ address _dumped_addr; // Address this->obj(), as used by the dumped archive. ++ address _orig_obj; // The value of the original object (_ref->obj()) when this ++ // SourceObjInfo was created. Note that _ref->obj() may change ++ // later if _ref is relocated. ++ ++ public: ++ SourceObjInfo(MetaspaceClosure::Ref* ref, bool read_only, FollowMode follow_mode) : ++ _ref(ref), _ptrmap_start(0), _ptrmap_end(0), _read_only(read_only), _follow_mode(follow_mode), ++ _size_in_bytes(ref->size() * BytesPerWord), _msotype(ref->msotype()), ++ _orig_obj(ref->obj()) { ++ if (follow_mode == point_to_it) { ++ _dumped_addr = ref->obj(); ++ } else { ++ _dumped_addr = NULL; ++ } ++ } ++ ++ bool should_copy() const { return _follow_mode == make_a_copy; } ++ MetaspaceClosure::Ref* ref() const { return _ref; } ++ void set_dumped_addr(address dumped_addr) { ++ assert(should_copy(), "must be"); ++ assert(_dumped_addr == NULL, "cannot be copied twice"); ++ assert(dumped_addr != NULL, "must be a valid copy"); ++ _dumped_addr = dumped_addr; ++ } ++ void set_ptrmap_start(uintx v) { _ptrmap_start = v; } ++ void set_ptrmap_end(uintx v) { _ptrmap_end = v; } ++ uintx ptrmap_start() const { return _ptrmap_start; } // inclusive ++ uintx ptrmap_end() const { return _ptrmap_end; } // exclusive ++ bool read_only() const { return _read_only; } ++ int size_in_bytes() const { return _size_in_bytes; } ++ address orig_obj() const { return _orig_obj; } ++ address dumped_addr() const { return _dumped_addr; } ++ MetaspaceObj::Type msotype() const { return _msotype; } ++ ++ // convenience accessor ++ address obj() const { return ref()->obj(); } ++ }; ++ ++ class SourceObjList { ++ uintx _total_bytes; ++ GrowableArray* _objs; // Source objects to be archived ++ BitMap _ptrmap; // Marks the addresses of the pointer fields ++ // in the source objects ++ public: ++ SourceObjList(); ++ ~SourceObjList(); ++ GrowableArray* objs() const { return _objs; } ++ ++ void append(MetaspaceClosure::Ref* enclosing_ref, SourceObjInfo* src_info); ++ void remember_embedded_pointer(SourceObjInfo* pointing_obj, MetaspaceClosure::Ref* ref); ++ void relocate(int i, ArchiveBuilder* builder); ++ ++ // convenience accessor ++ SourceObjInfo* at(int i) const { return objs()->at(i); } ++ }; ++ ++ class SrcObjTableCleaner { ++ public: ++ bool do_entry(address key, const SourceObjInfo* value) { ++ delete value->ref(); ++ return true; ++ } ++ }; ++ ++ static const int INITIAL_TABLE_SIZE = 15889; ++ static const int MAX_TABLE_SIZE = 1000000; ++ ++ ReservedSpace _shared_rs; ++ VirtualSpace _shared_vs; ++ ++ DumpRegion _rw_region; ++ DumpRegion _ro_region; ++ BitMap _ptrmap; ++ ++ SourceObjList _rw_src_objs; // objs to put in rw region ++ SourceObjList _ro_src_objs; // objs to put in ro region ++ KVHashtable _src_obj_table; ++ GrowableArray* _klasses; ++ GrowableArray* _symbols; ++ ++ // statistics ++ int _num_instance_klasses; ++ int _num_obj_array_klasses; ++ int _num_type_array_klasses; ++ DumpAllocStats _alloc_stats; ++ ++ // For global access. ++ static ArchiveBuilder* _current; ++ ++public: ++ // Use this when you allocate space outside of ArchiveBuilder::dump_{rw,ro}_region. ++ // These are usually for misc tables that are allocated in the RO space. ++ class OtherROAllocMark { ++ char* _oldtop; ++ public: ++ OtherROAllocMark() { ++ _oldtop = _current->_ro_region.top(); ++ } ++ ~OtherROAllocMark(); ++ }; ++ ++private: ++ FollowMode get_follow_mode(MetaspaceClosure::Ref *ref); ++ ++ void iterate_sorted_roots(MetaspaceClosure* it, bool is_relocating_pointers); ++ void sort_klasses(); ++ static int compare_symbols_by_address(Symbol** a, Symbol** b); ++ static int compare_klass_by_name(Klass** a, Klass** b); ++ ++ bool is_excluded(Klass* k); ++ void clean_up_src_obj_table(); ++ ++ void make_shallow_copies(DumpRegion *dump_region, const SourceObjList* src_objs); ++ void make_shallow_copy(DumpRegion *dump_region, SourceObjInfo* src_info); ++ void relocate_embedded_pointers(SourceObjList* src_objs); ++ ++protected: ++ virtual void iterate_roots(MetaspaceClosure* it, bool is_relocating_pointers) = 0; ++ ++ // Conservative estimate for number of bytes needed for: ++ size_t _estimated_metaspaceobj_bytes; // all archived MetaspaceObj's. ++ size_t _estimated_hashtable_bytes; // symbol table and dictionaries ++ ++ size_t estimate_archive_size(); ++ ++ static const int _total_dump_regions = 2; ++ ++ void start_dump_space(DumpRegion* next); ++ void verify_estimate_size(size_t estimate, const char* which); ++ ++public: ++ address reserve_buffer(); ++ ++ address buffer_bottom() const { return _buffer_bottom; } ++ address buffer_top() const { return (address)current_dump_space()->top(); } ++ address requested_static_archive_bottom() const { return _requested_static_archive_bottom; } ++ address mapped_static_archive_bottom() const { return _mapped_static_archive_bottom; } ++ intx buffer_to_requested_delta() const { return _buffer_to_requested_delta; } ++ ++ bool is_in_buffer_space(address p) const { ++ return (buffer_bottom() <= p && p < buffer_top()); ++ } ++ ++ template bool is_in_buffer_space(T obj) const { ++ return is_in_buffer_space(address(obj)); ++ } ++ ++ template bool is_in_requested_static_archive(T p) const { ++ return _requested_static_archive_bottom <= (address)p && (address)p < _requested_static_archive_top; ++ } ++ ++ template bool is_in_mapped_static_archive(T p) const { ++ return _mapped_static_archive_bottom <= (address)p && (address)p < _mapped_static_archive_top; ++ } ++ ++ template T to_requested(T obj) const { ++ assert(is_in_buffer_space(obj), "must be"); ++ return (T)(address(obj) + _buffer_to_requested_delta); ++ } ++ ++public: ++ static const uintx MAX_SHARED_DELTA = 0x7FFFFFFF; ++ ++ // The address p points to an object inside the output buffer. When the archive is mapped ++ // at the requested address, what's the offset of this object from _requested_static_archive_bottom? ++ uintx buffer_to_offset(address p) const; ++ ++ // Same as buffer_to_offset, except that the address p points to either (a) an object ++ // inside the output buffer, or (b), an object in the currently mapped static archive. ++ uintx any_to_offset(address p) const; ++ ++ template ++ u4 buffer_to_offset_u4(T p) const { ++ uintx offset = buffer_to_offset((address)p); ++ guarantee(offset <= MAX_SHARED_DELTA, "must be 32-bit offset"); ++ return (u4)offset; ++ } ++ ++ template ++ u4 any_to_offset_u4(T p) const { ++ uintx offset = any_to_offset((address)p); ++ guarantee(offset <= MAX_SHARED_DELTA, "must be 32-bit offset"); ++ return (u4)offset; ++ } ++ ++ static void assert_is_vm_thread() PRODUCT_RETURN; ++ ++public: ++ ArchiveBuilder(); ++ ~ArchiveBuilder(); ++ ++ void gather_klasses_and_symbols(); ++ void replace_klass_in_constanPool(); ++ void gather_source_objs(); ++ bool gather_klass_and_symbol(MetaspaceClosure::Ref* ref, bool read_only); ++ bool gather_one_source_obj(MetaspaceClosure::Ref* enclosing_ref, MetaspaceClosure::Ref* ref, bool read_only); ++ void remember_embedded_pointer_in_copied_obj(MetaspaceClosure::Ref* enclosing_ref, MetaspaceClosure::Ref* ref); ++ ++ DumpRegion* rw_region() { return &_rw_region; } ++ DumpRegion* ro_region() { return &_ro_region; } ++ ++ static char* rw_region_alloc(size_t num_bytes) { ++ return current()->rw_region()->allocate(num_bytes); ++ } ++ static char* ro_region_alloc(size_t num_bytes) { ++ return current()->ro_region()->allocate(num_bytes); ++ } ++ ++ template ++ static Array* new_ro_array(int length) { ++ size_t byte_size = Array::byte_sizeof(length); ++ Array* array = (Array*)ro_region_alloc(byte_size); ++ array->initialize(length); ++ return array; ++ } ++ ++ template ++ static Array* new_rw_array(int length) { ++ size_t byte_size = Array::byte_sizeof(length); ++ Array* array = (Array*)rw_region_alloc(byte_size); ++ array->initialize(length); ++ return array; ++ } ++ ++ template ++ static size_t ro_array_bytesize(int length) { ++ size_t byte_size = Array::byte_sizeof(length); ++ return align_up(byte_size, KlassAlignmentInBytes); ++ } ++ ++ void dump_rw_metadata(); ++ void dump_ro_metadata(); ++ void relocate_metaspaceobj_embedded_pointers(); ++ void relocate_roots(); ++ void make_klasses_shareable(); ++ void relocate_to_requested(); ++ void write_archive(FileMapInfo* mapinfo); ++ void write_region(FileMapInfo* mapinfo, int region_idx, DumpRegion* dump_region, bool read_only, bool allow_exec); ++ address get_dumped_addr(address src_obj); ++ void patch_shared_obj_vtable(); ++ ++ // All klasses and symbols that will be copied into the archive ++ GrowableArray* klasses() const { return _klasses; } ++ GrowableArray* symbols() const { return _symbols; } ++ ++ static bool is_active() { ++ return (_current != NULL); ++ } ++ ++ static ArchiveBuilder* current() { ++ assert_is_vm_thread(); ++ assert(_current != NULL, "ArchiveBuilder must be active"); ++ return _current; ++ } ++ ++ static DumpAllocStats* alloc_stats() { ++ return &(current()->_alloc_stats); ++ } ++ ++ static Symbol* get_relocated_symbol(Symbol* orig_symbol) { ++ return (Symbol*)current()->get_dumped_addr((address)orig_symbol); ++ } ++ ++ static CompactHashtableStats* symbol_stats() { ++ return alloc_stats()->symbol_stats(); ++ } ++ ++ void print_stats(); ++}; ++ ++#endif // SHARE_VM_CDS_ARCHIVEBUILDER_HPP +diff --git a/hotspot/src/share/vm/cds/archiveUtils.cpp b/hotspot/src/share/vm/cds/archiveUtils.cpp +new file mode 100644 +index 000000000..88c04241d +--- /dev/null ++++ b/hotspot/src/share/vm/cds/archiveUtils.cpp +@@ -0,0 +1,247 @@ ++/* ++ * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "cds/archiveBuilder.hpp" ++#include "cds/archiveUtils.hpp" ++#include "cds/dynamicArchive.hpp" ++#include "classfile/systemDictionaryShared.hpp" ++#include "memory/filemap.hpp" ++#include "memory/resourceArea.hpp" ++#include "runtime/arguments.hpp" ++#include "utilities/bitMap.inline.hpp" ++#include "utilities/align.hpp" ++ ++BitMap* ArchivePtrMarker::_ptrmap = NULL; ++VirtualSpace* ArchivePtrMarker::_vs; ++ ++bool ArchivePtrMarker::_compacted; ++ ++void ArchivePtrMarker::initialize(BitMap* ptrmap, VirtualSpace* vs) { ++ assert(_ptrmap == NULL, "initialize only once"); ++ _vs = vs; ++ _compacted = false; ++ _ptrmap = ptrmap; ++ ++ // Use this as initial guesstimate. We should need less space in the ++ // archive, but if we're wrong the bitmap will be expanded automatically. ++ size_t estimated_archive_size = MetaspaceGC::capacity_until_GC(); ++ // But set it smaller in debug builds so we always test the expansion code. ++ // (Default archive is about 12MB). ++ DEBUG_ONLY(estimated_archive_size = 6 * M); ++ ++ // We need one bit per pointer in the archive. ++ _ptrmap->resize(estimated_archive_size / sizeof(intptr_t), false); ++} ++ ++void ArchivePtrMarker::mark_pointer(address* ptr_loc) { ++ assert(_ptrmap != NULL, "not initialized"); ++ assert(!_compacted, "cannot mark anymore"); ++ ++ if (ptr_base() <= ptr_loc && ptr_loc < ptr_end()) { ++ address value = *ptr_loc; ++ // We don't want any pointer that points to very bottom of the archive, otherwise when ++ // MetaspaceShared::default_base_address()==0, we can't distinguish between a pointer ++ // to nothing (NULL) vs a pointer to an objects that happens to be at the very bottom ++ // of the archive. ++ assert(value != (address)ptr_base(), "don't point to the bottom of the archive"); ++ ++ if (value != NULL) { ++ assert(uintx(ptr_loc) % sizeof(intptr_t) == 0, "pointers must be stored in aligned addresses"); ++ size_t idx = ptr_loc - ptr_base(); ++ if (_ptrmap->size() <= idx) { ++ _ptrmap->resize((idx + 1) * 2, false); ++ } ++ assert(idx < _ptrmap->size(), "must be"); ++ _ptrmap->set_bit(idx); ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Marking pointer [" PTR_FORMAT "] -> " PTR_FORMAT " @ " SIZE_FORMAT_W(5), p2i(ptr_loc), p2i(*ptr_loc), idx); ++ } ++ } ++ } ++} ++ ++void ArchivePtrMarker::clear_pointer(address* ptr_loc) { ++ assert(_ptrmap != NULL, "not initialized"); ++ assert(!_compacted, "cannot clear anymore"); ++ ++ assert(ptr_base() <= ptr_loc && ptr_loc < ptr_end(), "must be"); ++ assert(uintx(ptr_loc) % sizeof(intptr_t) == 0, "pointers must be stored in aligned addresses"); ++ size_t idx = ptr_loc - ptr_base(); ++ assert(idx < _ptrmap->size(), "cannot clear pointers that have not been marked"); ++ _ptrmap->clear_bit(idx); ++ if (TraceDynamicCDS) ++ dynamic_cds_log->print_cr("Clearing pointer [" PTR_FORMAT "] -> " PTR_FORMAT " @ " SIZE_FORMAT_W(5), p2i(ptr_loc), p2i(*ptr_loc), idx); ++} ++ ++class ArchivePtrBitmapCleaner: public BitMapClosure { ++ BitMap* _ptrmap; ++ address* _ptr_base; ++ address _relocatable_base; ++ address _relocatable_end; ++ size_t _max_non_null_offset; ++ ++public: ++ ArchivePtrBitmapCleaner(BitMap* ptrmap, address* ptr_base, address relocatable_base, address relocatable_end) : ++ _ptrmap(ptrmap), _ptr_base(ptr_base), ++ _relocatable_base(relocatable_base), _relocatable_end(relocatable_end), _max_non_null_offset(0) {} ++ ++ bool do_bit(size_t offset) { ++ address* ptr_loc = _ptr_base + offset; ++ address ptr_value = *ptr_loc; ++ if (ptr_value != NULL) { ++ assert(_relocatable_base <= ptr_value && ptr_value < _relocatable_end, "do not point to arbitrary locations!"); ++ if (_max_non_null_offset < offset) { ++ _max_non_null_offset = offset; ++ } ++ } else { ++ _ptrmap->clear_bit(offset); ++ } ++ ++ return true; ++ } ++ ++ size_t max_non_null_offset() const { return _max_non_null_offset; } ++}; ++ ++void ArchivePtrMarker::compact(address relocatable_base, address relocatable_end) { ++ assert(!_compacted, "cannot compact again"); ++ ArchivePtrBitmapCleaner cleaner(_ptrmap, ptr_base(), relocatable_base, relocatable_end); ++ _ptrmap->iterate(&cleaner); ++ compact(cleaner.max_non_null_offset()); ++} ++ ++void ArchivePtrMarker::compact(size_t max_non_null_offset) { ++ assert(!_compacted, "cannot compact again"); ++ _ptrmap->resize(max_non_null_offset + 1, false); ++ _compacted = true; ++} ++ ++char* DumpRegion::expand_top_to(char* newtop) { ++ assert(is_allocatable(), "must be initialized and not packed"); ++ assert(newtop >= _top, "must not grow backwards"); ++ if (newtop > _end) { ++ vm_exit_during_initialization("Unable to allocate memory", ++ "Please reduce the number of shared classes."); ++ ShouldNotReachHere(); ++ } ++ ++ commit_to(newtop); ++ _top = newtop; ++ ++ if (_max_delta > 0) { ++ uintx delta = ArchiveBuilder::current()->buffer_to_offset((address)(newtop-1)); ++ if (delta > _max_delta) { ++ // This is just a sanity check and should not appear in any real world usage. This ++ // happens only if you allocate more than 2GB of shared objects and would require ++ // millions of shared classes. ++ vm_exit_during_initialization("Out of memory in the CDS archive", ++ "Please reduce the number of shared classes."); ++ } ++ } ++ ++ return _top; ++} ++ ++void DumpRegion::commit_to(char* newtop) { ++ Arguments::assert_is_dumping_archive(); ++ char* base = _rs->base(); ++ size_t need_committed_size = newtop - base; ++ size_t has_committed_size = _vs->committed_size(); ++ if (need_committed_size < has_committed_size) { ++ return; ++ } ++ ++ size_t min_bytes = need_committed_size - has_committed_size; ++ size_t preferred_bytes = 1 * M; ++ size_t uncommitted = _vs->reserved_size() - has_committed_size; ++ ++ size_t commit = MAX2(min_bytes, preferred_bytes); ++ commit = MIN2(commit, uncommitted); ++ assert(commit <= uncommitted, "sanity"); ++ ++ if (!_vs->expand_by(commit, false)) { ++ vm_exit_during_initialization(err_msg("Failed to expand shared space to " SIZE_FORMAT " bytes", ++ need_committed_size)); ++ } ++ ++ if (DebugDynamicCDS) { ++ dynamic_cds_log->print_cr("Expanding shared spaces by " SIZE_FORMAT_W(7) " bytes [total " SIZE_FORMAT_W(9) " bytes ending at %p]", ++ commit, _vs->actual_committed_size(), _vs->high()); ++ } ++} ++ ++char* DumpRegion::allocate(size_t num_bytes) { ++ char* p = (char*)align_up(_top, (size_t)KlassAlignmentInBytes); ++ char* newtop = p + align_up(num_bytes, (size_t)KlassAlignmentInBytes); ++ expand_top_to(newtop); ++ memset(p, 0, newtop - p); ++ return p; ++} ++ ++void DumpRegion::append_intptr_t(intptr_t n, bool need_to_mark) { ++ assert(is_aligned(_top, sizeof(intptr_t)), "bad alignment"); ++ intptr_t *p = (intptr_t*)_top; ++ char* newtop = _top + sizeof(intptr_t); ++ expand_top_to(newtop); ++ *p = n; ++ if (need_to_mark) { ++ ArchivePtrMarker::mark_pointer(p); ++ } ++} ++ ++void DumpRegion::init(ReservedSpace* rs, VirtualSpace* vs) { ++ _rs = rs; ++ _vs = vs; ++ // Start with 0 committed bytes. The memory will be committed as needed. ++ if (!_vs->initialize(*_rs, 0)) { ++ fatal("Unable to allocate memory for shared space"); ++ } ++ _base = _top = _rs->base(); ++ _end = _rs->base() + _rs->size(); ++} ++ ++void DumpRegion::pack(DumpRegion* next) { ++ assert(!is_packed(), "sanity"); ++ _end = (char*)align_up(_top, (size_t)os::vm_allocation_granularity()); ++ _is_packed = true; ++ if (next != NULL) { ++ next->_rs = _rs; ++ next->_vs = _vs; ++ next->_base = next->_top = this->_end; ++ next->_end = _rs->base() + _rs->size(); ++ } ++} ++ ++void DynamicWriteClosure::do_region(u_char* start, size_t size) { ++ assert((intptr_t)start % sizeof(intptr_t) == 0, "bad alignment"); ++ assert(size % sizeof(intptr_t) == 0, "bad size"); ++ do_tag((int)size); ++ while (size > 0) { ++ _dump_region->append_intptr_t(*(intptr_t*)start, true); ++ start += sizeof(intptr_t); ++ size -= sizeof(intptr_t); ++ } ++} +diff --git a/hotspot/src/share/vm/cds/archiveUtils.hpp b/hotspot/src/share/vm/cds/archiveUtils.hpp +new file mode 100644 +index 000000000..55c2431a0 +--- /dev/null ++++ b/hotspot/src/share/vm/cds/archiveUtils.hpp +@@ -0,0 +1,141 @@ ++/* ++ * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef SHARE_VM_CDS_ARCHIVEUTILS_HPP ++#define SHARE_VM_CDS_ARCHIVEUTILS_HPP ++ ++#include "memory/iterator.hpp" ++#include "runtime/virtualspace.hpp" ++#include "utilities/bitMap.hpp" ++ ++class ArchivePtrMarker : AllStatic { ++ static BitMap* _ptrmap; ++ static VirtualSpace* _vs; ++ ++ // Once _ptrmap is compacted, we don't allow bit marking anymore. This is to ++ // avoid unintentional copy operations after the bitmap has been finalized and written. ++ static bool _compacted; ++ ++ static address* ptr_base() { return (address*)_vs->low(); } // committed lower bound (inclusive) ++ static address* ptr_end() { return (address*)_vs->high(); } // committed upper bound (exclusive) ++ ++public: ++ static void initialize(BitMap* ptrmap, VirtualSpace* vs); ++ static void mark_pointer(address* ptr_loc); ++ static void clear_pointer(address* ptr_loc); ++ static void compact(address relocatable_base, address relocatable_end); ++ static void compact(size_t max_non_null_offset); ++ ++ template ++ static void mark_pointer(T* ptr_loc) { ++ mark_pointer((address*)ptr_loc); ++ } ++ ++ template ++ static void set_and_mark_pointer(T* ptr_loc, T ptr_value) { ++ *ptr_loc = ptr_value; ++ mark_pointer(ptr_loc); ++ } ++ ++ static BitMap* ptrmap() { ++ return _ptrmap; ++ } ++}; ++ ++class DumpRegion { ++private: ++ const char* _name; ++ char* _base; ++ char* _top; ++ char* _end; ++ uintx _max_delta; ++ bool _is_packed; ++ ReservedSpace* _rs; ++ VirtualSpace* _vs; ++ ++ void commit_to(char* newtop); ++ ++public: ++ DumpRegion(const char* name, uintx max_delta = 0) ++ : _name(name), _base(NULL), _top(NULL), _end(NULL), ++ _max_delta(max_delta), _is_packed(false) {} ++ ++ char* expand_top_to(char* newtop); ++ char* allocate(size_t num_bytes); ++ ++ void append_intptr_t(intptr_t n, bool need_to_mark = false); ++ ++ char* base() const { return _base; } ++ char* top() const { return _top; } ++ char* end() const { return _end; } ++ size_t reserved() const { return _end - _base; } ++ size_t used() const { return _top - _base; } ++ bool is_packed() const { return _is_packed; } ++ bool is_allocatable() const { ++ return !is_packed() && _base != NULL; ++ } ++ ++ void print(size_t total_bytes) const; ++ void print_out_of_space_msg(const char* failing_region, size_t needed_bytes); ++ ++ void init(ReservedSpace* rs, VirtualSpace* vs); ++ ++ void pack(DumpRegion* next = NULL); ++ ++ bool contains(char* p) const { ++ return base() <= p && p < top(); ++ } ++}; ++ ++// Closure for serializing initialization data out to a data area to be ++// written to the shared file. ++ ++class DynamicWriteClosure : public SerializeClosure { ++private: ++ DumpRegion* _dump_region; ++ ++public: ++ DynamicWriteClosure(DumpRegion* r) { ++ _dump_region = r; ++ } ++ ++ void do_ptr(void** p) { ++ _dump_region->append_intptr_t((intptr_t)*p, true); ++ } ++ ++ void do_u4(u4* p) { ++ _dump_region->append_intptr_t((intptr_t)(*p)); ++ } ++ ++ void do_tag(int tag) { ++ _dump_region->append_intptr_t((intptr_t)tag); ++ } ++ ++ //void do_oop(oop* o); ++ void do_region(u_char* start, size_t size); ++ bool reading() const { return false; } ++}; ++ ++#endif // SHARE_VM_CDS_ARCHIVEUTILS_HPP +diff --git a/hotspot/src/share/vm/cds/dumpAllocStats.cpp b/hotspot/src/share/vm/cds/dumpAllocStats.cpp +new file mode 100644 +index 000000000..e9146555d +--- /dev/null ++++ b/hotspot/src/share/vm/cds/dumpAllocStats.cpp +@@ -0,0 +1,109 @@ ++/* ++ * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "cds/dumpAllocStats.hpp" ++ ++// Returns numerator/denominator as percentage value from 0 to 100. If denominator ++// is zero, return 0.0. ++static inline double percent_of(int numerator, int denominator) { ++ return denominator != 0 ? (double)numerator / denominator * 100.0 : 0.0; ++} ++ ++void DumpAllocStats::print_stats(int ro_all, int rw_all) { ++ if (!DebugDynamicCDS) { ++ return; ++ } ++ ++ // symbols ++ _counts[RO][SymbolHashentryType] = _symbol_stats.hashentry_count; ++ _bytes [RO][SymbolHashentryType] = _symbol_stats.hashentry_bytes; ++ ++ _counts[RO][SymbolBucketType] = _symbol_stats.bucket_count; ++ _bytes [RO][SymbolBucketType] = _symbol_stats.bucket_bytes; ++ ++ // prevent divide-by-zero ++ if (ro_all < 1) { ++ ro_all = 1; ++ } ++ if (rw_all < 1) { ++ rw_all = 1; ++ } ++ ++ int all_ro_count = 0; ++ int all_ro_bytes = 0; ++ int all_rw_count = 0; ++ int all_rw_bytes = 0; ++ ++// To make fmt_stats be a syntactic constant (for format warnings), use #define. ++#define fmt_stats "%-20s: %8d %10d %5.1f | %8d %10d %5.1f | %8d %10d %5.1f" ++ const char *sep = "--------------------+---------------------------+---------------------------+--------------------------"; ++ const char *hdr = " ro_cnt ro_bytes % | rw_cnt rw_bytes % | all_cnt all_bytes %"; ++ ++ dynamic_cds_log->print_cr("Detailed metadata info (excluding heap regions):"); ++ dynamic_cds_log->print_cr("%s", hdr); ++ dynamic_cds_log->print_cr("%s", sep); ++ for (int type = 0; type < int(_number_of_types); type ++) { ++ const char *name = type_name((Type)type); ++ int ro_count = _counts[RO][type]; ++ int ro_bytes = _bytes [RO][type]; ++ int rw_count = _counts[RW][type]; ++ int rw_bytes = _bytes [RW][type]; ++ int count = ro_count + rw_count; ++ int bytes = ro_bytes + rw_bytes; ++ ++ double ro_perc = percent_of(ro_bytes, ro_all); ++ double rw_perc = percent_of(rw_bytes, rw_all); ++ double perc = percent_of(bytes, ro_all + rw_all); ++ ++ dynamic_cds_log->print_cr(fmt_stats, name, ++ ro_count, ro_bytes, ro_perc, ++ rw_count, rw_bytes, rw_perc, ++ count, bytes, perc); ++ ++ all_ro_count += ro_count; ++ all_ro_bytes += ro_bytes; ++ all_rw_count += rw_count; ++ all_rw_bytes += rw_bytes; ++ } ++ ++ int all_count = all_ro_count + all_rw_count; ++ int all_bytes = all_ro_bytes + all_rw_bytes; ++ ++ double all_ro_perc = percent_of(all_ro_bytes, ro_all); ++ double all_rw_perc = percent_of(all_rw_bytes, rw_all); ++ double all_perc = percent_of(all_bytes, ro_all + rw_all); ++ ++ dynamic_cds_log->print_cr("%s", sep); ++ dynamic_cds_log->print_cr(fmt_stats, "Total", ++ all_ro_count, all_ro_bytes, all_ro_perc, ++ all_rw_count, all_rw_bytes, all_rw_perc, ++ all_count, all_bytes, all_perc); ++ ++ assert(all_ro_bytes == ro_all, "everything should have been counted"); ++ assert(all_rw_bytes == rw_all, "everything should have been counted"); ++ ++#undef fmt_stats ++} +diff --git a/hotspot/src/share/vm/cds/dumpAllocStats.hpp b/hotspot/src/share/vm/cds/dumpAllocStats.hpp +new file mode 100644 +index 000000000..2f9247bcb +--- /dev/null ++++ b/hotspot/src/share/vm/cds/dumpAllocStats.hpp +@@ -0,0 +1,88 @@ ++/* ++ * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef SHARE_VM_CDS_DUMPALLOCSTATS_HPP ++#define SHARE_VM_CDS_DUMPALLOCSTATS_HPP ++ ++#include "classfile/compactHashtable.hpp" ++#include "memory/allocation.hpp" ++ ++// This is for dumping detailed statistics for the allocations ++// in the shared spaces. ++class DumpAllocStats : public ResourceObj { ++public: ++ // Here's poor man's enum inheritance ++#define SHAREDSPACE_OBJ_TYPES_DO(f) \ ++ METASPACE_OBJ_TYPES_DO(f) \ ++ f(SymbolHashentry) \ ++ f(SymbolBucket) \ ++ f(Other) ++ ++ enum Type { ++ // Types are MetaspaceObj::ClassType, MetaspaceObj::SymbolType, etc ++ SHAREDSPACE_OBJ_TYPES_DO(METASPACE_OBJ_TYPE_DECLARE) ++ _number_of_types ++ }; ++ ++ static const char* type_name(Type type) { ++ switch(type) { ++ SHAREDSPACE_OBJ_TYPES_DO(METASPACE_OBJ_TYPE_NAME_CASE) ++ default: ++ ShouldNotReachHere(); ++ return NULL; ++ } ++ } ++ ++ CompactHashtableStats _symbol_stats; ++ ++ int _counts[2][_number_of_types]; ++ int _bytes [2][_number_of_types]; ++ ++public: ++ enum { RO = 0, RW = 1 }; ++ ++ DumpAllocStats() { ++ memset(_counts, 0, sizeof(_counts)); ++ memset(_bytes, 0, sizeof(_bytes)); ++ }; ++ ++ CompactHashtableStats* symbol_stats() { return &_symbol_stats; } ++ ++ void record(MetaspaceObj::Type type, int byte_size, bool read_only) { ++ assert(int(type) >= 0 && type < MetaspaceObj::_number_of_types, "sanity"); ++ int which = (read_only) ? RO : RW; ++ _counts[which][type] ++; ++ _bytes [which][type] += byte_size; ++ } ++ ++ void record_other_type(int byte_size, bool read_only) { ++ int which = (read_only) ? RO : RW; ++ _bytes [which][OtherType] += byte_size; ++ } ++ ++ void print_stats(int ro_all, int rw_all); ++}; ++ ++#endif // SHARE_VM_CDS_DUMPALLOCSTATS_HPP +diff --git a/hotspot/src/share/vm/cds/dynamicArchive.cpp b/hotspot/src/share/vm/cds/dynamicArchive.cpp +new file mode 100644 +index 000000000..efed275c8 +--- /dev/null ++++ b/hotspot/src/share/vm/cds/dynamicArchive.cpp +@@ -0,0 +1,412 @@ ++/* ++ * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "cds/archiveBuilder.hpp" ++#include "cds/archiveUtils.hpp" ++#include "cds/dynamicArchive.hpp" ++#include "classfile/systemDictionaryShared.hpp" ++#include "runtime/vm_operations.hpp" ++#include "runtime/arguments.hpp" ++#include "runtime/vmThread.hpp" ++#include "memory/metaspaceShared.hpp" ++#include "memory/filemap.hpp" ++#include "memory/metaspaceClosure.hpp" ++#include "utilities/exceptions.hpp" ++#include "utilities/align.hpp" ++#include "utilities/bitMap.hpp" ++#include "utilities/exceptions.hpp" ++ ++class DynamicArchiveBuilder : public ArchiveBuilder { ++public: ++ static int dynamic_dump_method_comparator(Method* a, Method* b) { ++ Symbol* a_name = a->name(); ++ Symbol* b_name = b->name(); ++ ++ if (a_name == b_name) { ++ return 0; ++ } ++ ++ u4 a_offset = ArchiveBuilder::current()->any_to_offset_u4(a_name); ++ u4 b_offset = ArchiveBuilder::current()->any_to_offset_u4(b_name); ++ ++ if (a_offset < b_offset) { ++ return -1; ++ } else { ++ assert(a_offset > b_offset, "must be"); ++ return 1; ++ } ++ } ++ ++public: ++ FileMapInfo::DynamicArchiveHeader* _header; ++ ++ void init_header(); ++ void release_header(); ++ void sort_methods(); ++ void sort_methods(InstanceKlass* ik) const; ++ void remark_pointers_for_instance_klass(InstanceKlass* k, bool should_mark) const; ++ void write_archive(char* serialized_data); ++ virtual void iterate_roots(MetaspaceClosure* it, bool is_relocating_pointers) { ++ SystemDictionaryShared::dumptime_classes_do(it); ++ } ++ ++ // Do this before and after the archive dump to see if any corruption ++ // is caused by dynamic dumping. ++ void verify_universe(const char* info) { ++ if (VerifyBeforeExit) { ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Verify %s", info); ++ } ++ // Among other things, this ensures that Eden top is correct. ++ Universe::heap()->prepare_for_verify(); ++ Universe::verify(info); ++ } ++ } ++ ++ void doit() { ++ SystemDictionaryShared::start_dumping(); ++ ++ verify_universe("Before CDS dynamic dump"); ++ DEBUG_ONLY(SystemDictionaryShared::NoClassLoadingMark nclm); ++ ++ // No need DumpTimeTable_lock, since jdk8 doesn't support jcmd dump. ++ // Just remains this lock. ++ MutexLockerEx ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); ++ SystemDictionaryShared::check_excluded_classes(); ++ SystemDictionaryShared::replace_klass_in_constantPool(); ++ ++ init_header(); ++ gather_source_objs(); ++ if (klasses()->length() == 0) { ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("No classes gathered, so do not generate Dynamic CDS jsa"); ++ } ++ return; ++ } ++ reserve_buffer(); ++ ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Copying %d klasses and %d symbols", ++ klasses()->length(), symbols()->length()); ++ } ++ dump_rw_metadata(); ++ dump_ro_metadata(); ++ relocate_metaspaceobj_embedded_pointers(); ++ relocate_roots(); ++ ++ verify_estimate_size(_estimated_metaspaceobj_bytes, "MetaspaceObjs"); ++ ++ char* serialized_data; ++ { ++ // Write the symbol table and system dictionaries to the RO space. ++ // Note that these tables still point to the *original* objects, so ++ // they would need to get the correct addresses. ++ assert(current_dump_space() == ro_region(), "Must be RO space"); ++ SymbolTable::write_to_archive(symbols()); ++ ++ ArchiveBuilder::OtherROAllocMark mark; ++ SystemDictionaryShared::write_to_archive(); ++ ++ serialized_data = ro_region()->top(); ++ DynamicWriteClosure wc(ro_region()); ++ SymbolTable::serialize_shared_table_header(&wc); ++ SystemDictionaryShared::serialize_dictionary_headers(&wc); ++ } ++ ++ verify_estimate_size(_estimated_hashtable_bytes, "Hashtables"); ++ ++ sort_methods(); ++ ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Make classes shareable"); ++ } ++ make_klasses_shareable(); ++ ++ patch_shared_obj_vtable(); ++ ++ relocate_to_requested(); ++ ++ write_archive(serialized_data); ++ release_header(); ++ ++ assert(_num_dump_regions_used == _total_dump_regions, "must be"); ++ verify_universe("After CDS dynamic dump"); ++ } ++}; ++ ++void DynamicArchiveBuilder::init_header() { ++ FileMapInfo* mapinfo = new FileMapInfo(false); ++ assert(FileMapInfo::dynamic_info() == mapinfo, "must be"); ++ _header = mapinfo->dynamic_header(); ++ ++ FileMapInfo* base_info = FileMapInfo::current_info(); ++ _header->set_base_header_crc(base_info->header()->crc()); ++ for (int i = 0; i < MetaspaceShared::n_regions; i++) { ++ _header->set_base_region_crc(i, base_info->header()->space_crc(i)); ++ } ++ ++ _header->populate(base_info, base_info->alignment()); ++} ++ ++void DynamicArchiveBuilder::release_header() { ++ // We temporarily allocated a dynamic FileMapInfo for dumping, which makes it appear we ++ // have mapped a dynamic archive, but we actually have not. We are in a safepoint now. ++ // Let's free it so that if class loading happens after we leave the safepoint, nothing ++ // bad will happen. ++ assert(SafepointSynchronize::is_at_safepoint(), "must be"); ++ FileMapInfo *mapinfo = FileMapInfo::dynamic_info(); ++ assert(mapinfo != NULL && _header == mapinfo->dynamic_header(), "must be"); ++ delete mapinfo; ++ assert(!DynamicArchive::is_mapped(), "must be"); ++ _header = NULL; ++} ++ ++void DynamicArchiveBuilder::sort_methods() { ++ // Because high version support jcmd dynamic cds dump, jvm need go on after dump. ++ // Jdk8 no need as so, just exit after dump. ++ InstanceKlass::disable_method_binary_search(); ++ for (int i = 0; i < klasses()->length(); i++) { ++ Klass* k = klasses()->at(i); ++ if (k->oop_is_instance()) { ++ sort_methods(InstanceKlass::cast(k)); ++ } ++ } ++} ++ ++// The address order of the copied Symbols may be different than when the original ++// klasses were created. Re-sort all the tables. See Method::sort_methods(). ++void DynamicArchiveBuilder::sort_methods(InstanceKlass* ik) const { ++ assert(ik != NULL, "DynamicArchiveBuilder currently doesn't support dumping the base archive"); ++ if (MetaspaceShared::is_in_shared_space(ik)) { ++ // We have reached a supertype that's already in the base archive ++ return; ++ } ++ ++ if (ik->java_mirror() == NULL) { ++ // NULL mirror means this class has already been visited and methods are already sorted ++ return; ++ } ++ ik->remove_java_mirror(); ++ ++ if (DebugDynamicCDS) { ++ ResourceMark rm; ++ dynamic_cds_log->print_cr("sorting methods for " PTR_FORMAT " (" PTR_FORMAT ") %s", ++ p2i(ik), p2i(to_requested(ik)), ik->external_name()); ++ } ++ // Method sorting may re-layout the [iv]tables, which would change the offset(s) ++ // of the locations in an InstanceKlass that would contain pointers. Let's clear ++ // all the existing pointer marking bits, and re-mark the pointers after sorting. ++ remark_pointers_for_instance_klass(ik, false); ++ ++ // Make sure all supertypes have been sorted ++ sort_methods(ik->java_super()); ++ Array* interfaces = ik->local_interfaces(); ++ int len = interfaces->length(); ++ for (int i = 0; i < len; i++) { ++ sort_methods(InstanceKlass::cast(interfaces->at(i))); ++ } ++ ++#ifdef ASSERT ++ if (ik->methods() != NULL) { ++ for (int m = 0; m < ik->methods()->length(); m++) { ++ Symbol* name = ik->methods()->at(m)->name(); ++ assert(MetaspaceShared::is_in_shared_space(name) || is_in_buffer_space(name), "must be"); ++ } ++ } ++ if (ik->default_methods() != NULL) { ++ for (int m = 0; m < ik->default_methods()->length(); m++) { ++ Symbol* name = ik->default_methods()->at(m)->name(); ++ assert(MetaspaceShared::is_in_shared_space(name) || is_in_buffer_space(name), "must be"); ++ } ++ } ++#endif ++ ++ Method::sort_methods(ik->methods(), /*idempotent=*/false, /*set_idnums=*/true, dynamic_dump_method_comparator); ++ if (ik->default_methods() != NULL) { ++ Method::sort_methods(ik->default_methods(), /*idempotent=*/false, /*set_idnums=*/false, dynamic_dump_method_comparator); ++ } ++ ++ EXCEPTION_MARK; ++ ++ ik->vtable()->initialize_vtable(false, CATCH); // No need checkconstraints ++ CLEAR_PENDING_EXCEPTION; ++ ik->itable()->initialize_itable(false, CATCH); ++ CLEAR_PENDING_EXCEPTION; ++ ++ // Set all the pointer marking bits after sorting. ++ remark_pointers_for_instance_klass(ik, true); ++} ++ ++template ++class PointerRemarker: public MetaspaceClosure { ++public: ++ virtual bool do_ref(Ref* ref, bool read_only) { ++ if (should_mark) { ++ ArchivePtrMarker::mark_pointer(ref->addr()); ++ } else { ++ ArchivePtrMarker::clear_pointer(ref->addr()); ++ } ++ return false; // don't recurse ++ } ++}; ++ ++void DynamicArchiveBuilder::remark_pointers_for_instance_klass(InstanceKlass* k, bool should_mark) const { ++ if (should_mark) { ++ PointerRemarker marker; ++ k->metaspace_pointers_do(&marker); ++ marker.finish(); ++ } else { ++ PointerRemarker marker; ++ k->metaspace_pointers_do(&marker); ++ marker.finish(); ++ } ++} ++ ++void DynamicArchiveBuilder::write_archive(char* serialized_data) { ++ _header->set_serialized_data(serialized_data); ++ ++ FileMapInfo* dynamic_info = FileMapInfo::dynamic_info(); ++ assert(dynamic_info != NULL, "Sanity"); ++ ++ // Update file offset ++ ArchiveBuilder::write_archive(dynamic_info); ++ ++ // Write into file ++ dynamic_info->open_for_write(); ++ dynamic_info->set_requested_base((char*)MetaspaceShared::requested_base_address()); ++ dynamic_info->set_header_base_archive_name_size(strlen(Arguments::GetSharedArchivePath()) + 1); ++ dynamic_info->set_header_crc(dynamic_info->compute_header_crc()); ++ ArchiveBuilder::write_archive(dynamic_info); ++ ++ address base = _requested_dynamic_archive_bottom; ++ address top = _requested_dynamic_archive_top; ++ size_t file_size = pointer_delta(top, base, sizeof(char)); ++ ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Written dynamic archive " PTR_FORMAT " - " PTR_FORMAT ++ " , " SIZE_FORMAT " bytes total]", ++ p2i(base), p2i(top), file_size); ++ ++ dynamic_cds_log->print_cr("%d klasses; %d symbols", klasses()->length(), symbols()->length()); ++ } ++} ++ ++class VM_GC_Sync_Operation : public VM_Operation { ++public: ++ ++ VM_GC_Sync_Operation() : VM_Operation() { } ++ ++ // Acquires the Heap_lock. ++ virtual bool doit_prologue() { ++ Heap_lock->lock(); ++ return true; ++ } ++ // Releases the Heap_lock. ++ virtual void doit_epilogue() { ++ Heap_lock->unlock(); ++ } ++}; ++ ++class VM_PopulateDynamicDumpSharedSpace : public VM_GC_Sync_Operation { ++ DynamicArchiveBuilder builder; ++public: ++ VM_PopulateDynamicDumpSharedSpace() : VM_GC_Sync_Operation() {} ++ VMOp_Type type() const { return VMOp_PopulateDumpSharedSpace; } ++ void doit() { ++ if (DynamicDumpSharedSpaces == false) { ++ return; ++ } ++ ResourceMark rm; ++ ++ if (SystemDictionaryShared::empty_dumptime_table()) { ++ tty->print_cr("There is no class to be included in the dynamic archive."); ++ return; ++ } ++ ++ builder.doit(); ++ ++ DynamicDumpSharedSpaces = false; ++ exit(0); ++ } ++}; ++ ++bool DynamicArchive::_has_been_dumped_once = false; ++ ++void DynamicArchive::prepare_for_dynamic_dumping_at_exit() { ++ { ++ MutexLockerEx ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); ++ if (DynamicArchive::has_been_dumped_once()) { ++ return; ++ } else { ++ DynamicArchive::set_has_been_dumped_once(); ++ } ++ } ++ EXCEPTION_MARK; ++ ResourceMark rm(THREAD); ++ MetaspaceShared::link_and_cleanup_shared_classes(THREAD); ++ ++ if (HAS_PENDING_EXCEPTION) { ++ tty->print_cr("ArchiveClassesAtExit has failed"); ++ tty->print_cr("%s: %s", PENDING_EXCEPTION->klass()->external_name(), ++ java_lang_String::as_utf8_string(java_lang_Throwable::message(PENDING_EXCEPTION))); ++ // We cannot continue to dump the archive anymore. ++ DynamicDumpSharedSpaces = false; ++ CLEAR_PENDING_EXCEPTION; ++ } ++} ++ ++void DynamicArchive::dump() { ++ if (Arguments::GetSharedDynamicArchivePath() == NULL) { ++ tty->print_cr("SharedDynamicArchivePath is not specified"); ++ return; ++ } ++ ++ VM_PopulateDynamicDumpSharedSpace op; ++ VMThread::execute(&op); ++} ++ ++bool DynamicArchive::validate(FileMapInfo* dynamic_info) { ++ assert(!dynamic_info->is_static(), "must be"); ++ // Check if the recorded base archive matches with the current one ++ FileMapInfo* base_info = FileMapInfo::current_info(); ++ FileMapInfo::DynamicArchiveHeader* dynamic_header = dynamic_info->dynamic_header(); ++ ++ // Check the header crc ++ if (dynamic_header->base_header_crc() != base_info->crc()) { ++ FileMapInfo::fail_continue("Dynamic archive cannot be used: static archive header checksum verification failed."); ++ return false; ++ } ++ ++ // Check each space's crc ++ for (int i = 0; i < MetaspaceShared::n_regions; i++) { ++ if (dynamic_header->base_region_crc(i) != base_info->space_crc(i)) { ++ FileMapInfo::fail_continue("Dynamic archive cannot be used: static archive region #%d checksum verification failed.", i); ++ return false; ++ } ++ } ++ ++ return true; ++} +diff --git a/hotspot/src/share/vm/cds/dynamicArchive.hpp b/hotspot/src/share/vm/cds/dynamicArchive.hpp +new file mode 100644 +index 000000000..1d5b71221 +--- /dev/null ++++ b/hotspot/src/share/vm/cds/dynamicArchive.hpp +@@ -0,0 +1,54 @@ ++/* ++ * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef SHARE_VM_CDS_DYNAMICARCHIVE_HPP ++#define SHARE_VM_CDS_DYNAMICARCHIVE_HPP ++ ++//#include "classfile/compactHashtable.hpp" ++#include "memory/allocation.hpp" ++#include "memory/filemap.hpp" ++#include "memory/memRegion.hpp" ++#include "runtime/virtualspace.hpp" ++#include "oops/oop.hpp" ++#include "utilities/exceptions.hpp" ++#include "utilities/macros.hpp" ++#include "utilities/resourceHash.hpp" ++ ++#if INCLUDE_CDS ++ ++// Fixme ++class DynamicArchive : AllStatic { ++ static bool _has_been_dumped_once; ++public: ++ static void prepare_for_dynamic_dumping_at_exit(); ++ static void dump(); ++ static bool has_been_dumped_once() { return _has_been_dumped_once; } ++ static void set_has_been_dumped_once() { _has_been_dumped_once = true; } ++ static bool is_mapped() { return FileMapInfo::dynamic_info() != NULL; } ++ static bool validate(FileMapInfo* dynamic_info); ++}; ++ ++#endif // INCLUDE_CDS ++#endif // SHARE_VM_CDS_DYNAMICARCHIVE_HPP +diff --git a/hotspot/src/share/vm/classfile/classFileParser.cpp b/hotspot/src/share/vm/classfile/classFileParser.cpp +index 5c36a9d6f..ae9199525 100644 +--- a/hotspot/src/share/vm/classfile/classFileParser.cpp ++++ b/hotspot/src/share/vm/classfile/classFileParser.cpp +@@ -4376,6 +4376,13 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, + instanceKlassHandle this_klass (THREAD, preserve_this_klass); + debug_only(this_klass->verify();) + ++#if INCLUDE_CDS ++ if (DynamicDumpSharedSpaces && !SystemDictionary::is_builtin_loader(class_loader)) { ++ this_klass->set_shared_classpath_index(UNREGISTERED_INDEX); ++ SystemDictionaryShared::set_shared_class_misc_info(this_klass(), cfs); ++ } ++#endif // INCLUDE_CDS ++ + // Clear class if no error has occurred so destructor doesn't deallocate it + _klass = NULL; + return this_klass; +diff --git a/hotspot/src/share/vm/classfile/classLoaderExt.hpp b/hotspot/src/share/vm/classfile/classLoaderExt.hpp +index 7b2360af9..3bd4f3bde 100644 +--- a/hotspot/src/share/vm/classfile/classLoaderExt.hpp ++++ b/hotspot/src/share/vm/classfile/classLoaderExt.hpp +@@ -48,7 +48,7 @@ public: + instanceKlassHandle record_result(const int classpath_index, + ClassPathEntry* e, instanceKlassHandle result, TRAPS) { + if (ClassLoader::add_package(_file_name, classpath_index, THREAD)) { +- if (DumpSharedSpaces) { ++ if (DumpSharedSpaces || DynamicDumpSharedSpaces) { + result->set_shared_classpath_index(classpath_index); + } + return result; +diff --git a/hotspot/src/share/vm/classfile/compactHashtable.cpp b/hotspot/src/share/vm/classfile/compactHashtable.cpp +new file mode 100644 +index 000000000..232a89fa1 +--- /dev/null ++++ b/hotspot/src/share/vm/classfile/compactHashtable.cpp +@@ -0,0 +1,216 @@ ++/* ++ * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "jvm.h" ++#include "cds/archiveBuilder.hpp" ++#include "classfile/compactHashtable.hpp" ++#include "classfile/javaClasses.hpp" ++#include "memory/metadataFactory.hpp" ++#include "runtime/arguments.hpp" ++#include "runtime/globals.hpp" ++#include "runtime/vmThread.hpp" ++#include "utilities/align.hpp" ++#include "utilities/numberSeq.hpp" ++ ++///////////////////////////////////////////////////// ++// ++// The compact hash table writer implementations ++// ++CompactHashtableWriter::CompactHashtableWriter(int num_entries, ++ CompactHashtableStats* stats) { ++ Arguments::assert_is_dumping_archive(); ++ assert(num_entries >= 0, "sanity"); ++ _num_buckets = calculate_num_buckets(num_entries); ++ assert(_num_buckets > 0, "no buckets"); ++ ++ _num_entries_written = 0; ++ _buckets = NEW_C_HEAP_ARRAY(GrowableArray*, _num_buckets, mtSymbol); ++ for (int i = 0; i < _num_buckets; i++) { ++ _buckets[i] = new (ResourceObj::C_HEAP, mtSymbol) GrowableArray(0, true, mtSymbol); ++ } ++ ++ _stats = stats; ++ _compact_buckets = NULL; ++ _compact_entries = NULL; ++ _num_empty_buckets = 0; ++ _num_value_only_buckets = 0; ++ _num_other_buckets = 0; ++} ++ ++CompactHashtableWriter::~CompactHashtableWriter() { ++ for (int index = 0; index < _num_buckets; index++) { ++ GrowableArray* bucket = _buckets[index]; ++ delete bucket; ++ } ++ ++ FREE_C_HEAP_ARRAY(GrowableArray*, _buckets, mtSymbol); ++} ++ ++size_t CompactHashtableWriter::estimate_size(int num_entries) { ++ int num_buckets = calculate_num_buckets(num_entries); ++ size_t bucket_bytes = ArchiveBuilder::ro_array_bytesize(num_buckets + 1); ++ ++ // In worst case, we have no VALUE_ONLY_BUCKET_TYPE, so each entry takes 2 slots ++ int entries_space = 2 * num_entries; ++ size_t entry_bytes = ArchiveBuilder::ro_array_bytesize(entries_space); ++ ++ return bucket_bytes ++ + entry_bytes ++ + SimpleCompactHashtable::calculate_header_size(); ++} ++ ++// Add a symbol entry to the temporary hash table ++void CompactHashtableWriter::add(unsigned int hash, u4 value) { ++ int index = hash % _num_buckets; ++ _buckets[index]->append_if_missing(Entry(hash, value)); ++ _num_entries_written++; ++} ++ ++void CompactHashtableWriter::allocate_table() { ++ int entries_space = 0; ++ for (int index = 0; index < _num_buckets; index++) { ++ GrowableArray* bucket = _buckets[index]; ++ int bucket_size = bucket->length(); ++ if (bucket_size == 1) { ++ entries_space++; ++ } else if (bucket_size > 1) { ++ entries_space += 2 * bucket_size; ++ } ++ } ++ ++ if (entries_space & ~BUCKET_OFFSET_MASK) { ++ vm_exit_during_initialization("CompactHashtableWriter::allocate_table: Overflow! " ++ "Too many entries."); ++ } ++ ++ _compact_buckets = ArchiveBuilder::new_ro_array(_num_buckets + 1); ++ _compact_entries = ArchiveBuilder::new_ro_array(entries_space); ++ ++ _stats->bucket_count = _num_buckets; ++ _stats->bucket_bytes = align_up(_compact_buckets->size() * BytesPerWord, ++ KlassAlignmentInBytes); ++ _stats->hashentry_count = _num_entries_written; ++ _stats->hashentry_bytes = align_up(_compact_entries->size() * BytesPerWord, ++ KlassAlignmentInBytes); ++} ++ ++// Write the compact table's buckets ++void CompactHashtableWriter::dump_table(NumberSeq* summary) { ++ u4 offset = 0; ++ for (int index = 0; index < _num_buckets; index++) { ++ GrowableArray* bucket = _buckets[index]; ++ int bucket_size = bucket->length(); ++ if (bucket_size == 1) { ++ // bucket with one entry is compacted and only has the symbol offset ++ _compact_buckets->at_put(index, BUCKET_INFO(offset, VALUE_ONLY_BUCKET_TYPE)); ++ ++ Entry ent = bucket->at(0); ++ _compact_entries->at_put(offset++, ent.value()); ++ _num_value_only_buckets++; ++ } else { ++ // regular bucket, each entry is a symbol (hash, offset) pair ++ _compact_buckets->at_put(index, BUCKET_INFO(offset, REGULAR_BUCKET_TYPE)); ++ ++ for (int i=0; iat(i); ++ _compact_entries->at_put(offset++, u4(ent.hash())); // write entry hash ++ _compact_entries->at_put(offset++, ent.value()); ++ } ++ if (bucket_size == 0) { ++ _num_empty_buckets++; ++ } else { ++ _num_other_buckets++; ++ } ++ } ++ summary->add(bucket_size); ++ } ++ ++ // Mark the end of the buckets ++ _compact_buckets->at_put(_num_buckets, BUCKET_INFO(offset, TABLEEND_BUCKET_TYPE)); ++ assert(offset == (u4)_compact_entries->length(), "sanity"); ++} ++ ++// Write the compact table ++void CompactHashtableWriter::dump(SimpleCompactHashtable *cht, const char* table_name) { ++ NumberSeq summary; ++ allocate_table(); ++ dump_table(&summary); ++ ++ int table_bytes = _stats->bucket_bytes + _stats->hashentry_bytes; ++ address base_address = address(SharedBaseAddress); ++ cht->init(base_address, _num_entries_written, _num_buckets, ++ _compact_buckets->data(), _compact_entries->data()); ++ ++ if (InfoDynamicCDS) { ++ double avg_cost = 0.0; ++ if (_num_entries_written > 0) { ++ avg_cost = double(table_bytes)/double(_num_entries_written); ++ } ++ dynamic_cds_log->print_cr("Shared %s table stats -------- base: " PTR_FORMAT, ++ table_name, (intptr_t)base_address); ++ dynamic_cds_log->print_cr("Number of entries : %9d", _num_entries_written); ++ dynamic_cds_log->print_cr("Total bytes used : %9d", table_bytes); ++ dynamic_cds_log->print_cr("Average bytes per entry : %9.3f", avg_cost); ++ dynamic_cds_log->print_cr("Average bucket size : %9.3f", summary.avg()); ++ dynamic_cds_log->print_cr("Variance of bucket size : %9.3f", summary.variance()); ++ dynamic_cds_log->print_cr("Std. dev. of bucket size: %9.3f", summary.sd()); ++ dynamic_cds_log->print_cr("Maximum bucket size : %9d", (int)summary.maximum()); ++ dynamic_cds_log->print_cr("Empty buckets : %9d", _num_empty_buckets); ++ dynamic_cds_log->print_cr("Value_Only buckets : %9d", _num_value_only_buckets); ++ dynamic_cds_log->print_cr("Other buckets : %9d", _num_other_buckets); ++ } ++} ++ ++///////////////////////////////////////////////////////////// ++// ++// The CompactHashtable implementation ++// ++ ++void SimpleCompactHashtable::init(address base_address, u4 entry_count, u4 bucket_count, u4* buckets, u4* entries) { ++ _bucket_count = bucket_count; ++ _entry_count = entry_count; ++ _base_address = base_address; ++ _buckets = buckets; ++ _entries = entries; ++} ++ ++size_t SimpleCompactHashtable::calculate_header_size() { ++ // We have 5 fields. Each takes up sizeof(intptr_t). See WriteClosure::do_u4 ++ size_t bytes = sizeof(intptr_t) * 5; ++ return bytes; ++} ++ ++void SimpleCompactHashtable::serialize_header(SerializeClosure* soc) { ++ // NOTE: if you change this function, you MUST change the number 5 in ++ // calculate_header_size() accordingly. ++ soc->do_u4(&_entry_count); ++ soc->do_u4(&_bucket_count); ++ soc->do_ptr((void**)&_buckets); ++ soc->do_ptr((void**)&_entries); ++ if (soc->reading()) { ++ _base_address = (address)SharedBaseAddress; ++ } ++} +diff --git a/hotspot/src/share/vm/classfile/compactHashtable.hpp b/hotspot/src/share/vm/classfile/compactHashtable.hpp +new file mode 100644 +index 000000000..727b3ebfb +--- /dev/null ++++ b/hotspot/src/share/vm/classfile/compactHashtable.hpp +@@ -0,0 +1,349 @@ ++/* ++ * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef SHARE_VM_CLASSFILE_COMPACTHASHTABLE_HPP ++#define SHARE_VM_CLASSFILE_COMPACTHASHTABLE_HPP ++ ++#include "oops/symbol.hpp" ++#include "runtime/globals.hpp" ++#include "utilities/array.hpp" ++#include "utilities/growableArray.hpp" ++#include "utilities/numberSeq.hpp" ++ ++ ++template < ++ typename K, ++ typename V, ++ V (*DECODE)(address base_address, u4 offset), ++ bool (*EQUALS)(V value, K key, int len) ++ > ++class CompactHashtable; ++class NumberSeq; ++class SimpleCompactHashtable; ++ ++// Stats for symbol tables in the CDS archive ++class CompactHashtableStats { ++public: ++ int hashentry_count; ++ int hashentry_bytes; ++ int bucket_count; ++ int bucket_bytes; ++ ++ CompactHashtableStats() : ++ hashentry_count(0), hashentry_bytes(0), ++ bucket_count(0), bucket_bytes(0) {} ++}; ++ ++///////////////////////////////////////////////////////////////////////// ++// ++// The compact hash table writer. Used at dump time for writing out ++// the compact table to the shared archive. ++// ++// At dump time, the CompactHashtableWriter obtains all entries from the ++// symbol/string table and adds them to a new temporary hash table. The hash ++// table size (number of buckets) is calculated using ++// '(num_entries + bucket_size - 1) / bucket_size'. The default bucket ++// size is 4 and can be changed by -XX:SharedSymbolTableBucketSize option. ++// 4 is chosen because it produces smaller sized bucket on average for ++// faster lookup. It also has relatively small number of empty buckets and ++// good distribution of the entries. ++// ++// We use a simple hash function (hash % num_bucket) for the table. ++// The new table is compacted when written out. Please see comments ++// above the CompactHashtable class for the table layout detail. The bucket ++// offsets are written to the archive as part of the compact table. The ++// bucket offset is encoded in the low 30-bit (0-29) and the bucket type ++// (regular or compact) are encoded in bit[31, 30]. For buckets with more ++// than one entry, both hash and entry offset are written to the ++// table. For buckets with only one entry, only the entry offset is written ++// to the table and the buckets are tagged as compact in their type bits. ++// Buckets without entry are skipped from the table. Their offsets are ++// still written out for faster lookup. ++// ++class CompactHashtableWriter: public StackObj { ++public: ++ class Entry { ++ unsigned int _hash; ++ u4 _value; ++ ++ public: ++ Entry() {} ++ Entry(unsigned int hash, u4 val) : _hash(hash), _value(val) {} ++ ++ u4 value() { ++ return _value; ++ } ++ unsigned int hash() { ++ return _hash; ++ } ++ ++ bool operator==(const CompactHashtableWriter::Entry& other) { ++ return (_value == other._value && _hash == other._hash); ++ } ++ }; // class CompactHashtableWriter::Entry ++ ++private: ++ int _num_entries_written; ++ int _num_buckets; ++ int _num_empty_buckets; ++ int _num_value_only_buckets; ++ int _num_other_buckets; ++ GrowableArray** _buckets; ++ CompactHashtableStats* _stats; ++ Array* _compact_buckets; ++ Array* _compact_entries; ++ ++public: ++ // This is called at dump-time only ++ CompactHashtableWriter(int num_entries, CompactHashtableStats* stats); ++ ~CompactHashtableWriter(); ++ ++ void add(unsigned int hash, u4 value); ++ ++private: ++ void allocate_table(); ++ void dump_table(NumberSeq* summary); ++ ++ static int calculate_num_buckets(int num_entries) { ++ int num_buckets = num_entries / SharedSymbolTableBucketSize; ++ // calculation of num_buckets can result in zero buckets, we need at least one ++ return (num_buckets < 1) ? 1 : num_buckets; ++ } ++ ++public: ++ void dump(SimpleCompactHashtable *cht, const char* table_name); ++ ++ static size_t estimate_size(int num_entries); ++}; ++ ++#define REGULAR_BUCKET_TYPE 0 ++#define VALUE_ONLY_BUCKET_TYPE 1 ++#define TABLEEND_BUCKET_TYPE 3 ++#define BUCKET_OFFSET_MASK 0x3FFFFFFF ++#define BUCKET_OFFSET(info) ((info) & BUCKET_OFFSET_MASK) ++#define BUCKET_TYPE_SHIFT 30 ++#define BUCKET_TYPE(info) (((info) & ~BUCKET_OFFSET_MASK) >> BUCKET_TYPE_SHIFT) ++#define BUCKET_INFO(offset, type) (((type) << BUCKET_TYPE_SHIFT) | ((offset) & BUCKET_OFFSET_MASK)) ++ ++///////////////////////////////////////////////////////////////////////////// ++// ++// CompactHashtable is used to store the CDS archive's symbol/string tables. ++// ++// Because these tables are read-only (no entries can be added/deleted) at run-time ++// and tend to have large number of entries, we try to minimize the footprint ++// cost per entry. ++// ++// The CompactHashtable is split into two arrays ++// ++// u4 buckets[num_buckets+1]; // bit[31,30]: type; bit[29-0]: offset ++// u4 entries[] ++// ++// The size of buckets[] is 'num_buckets + 1'. Each entry of ++// buckets[] is a 32-bit encoding of the bucket type and bucket offset, ++// with the type in the left-most 2-bit and offset in the remaining 30-bit. ++// The last entry is a special type. It contains the end of the last ++// bucket. ++// ++// There are two types of buckets, regular buckets and value_only buckets. The ++// value_only buckets have '01' in their highest 2-bit, and regular buckets have ++// '00' in their highest 2-bit. ++// ++// For normal buckets, each entry is 8 bytes in the entries[]: ++// u4 hash; /* symbol/string hash */ ++// union { ++// u4 offset; /* Symbol* sym = (Symbol*)(base_address + offset) */ ++// narrowOop str; /* String narrowOop encoding */ ++// } ++// ++// ++// For value_only buckets, each entry has only the 4-byte 'offset' in the entries[]. ++// ++// Example -- note that the second bucket is a VALUE_ONLY_BUCKET_TYPE so the hash code ++// is skipped. ++// buckets[0, 4, 5, ....] ++// | | | ++// | | +---+ ++// | | | ++// | +----+ | ++// v v v ++// entries[H,O,H,O,O,H,O,H,O.....] ++// ++// See CompactHashtable::lookup() for how the table is searched at runtime. ++// See CompactHashtableWriter::dump() for how the table is written at CDS ++// dump time. ++// ++class SimpleCompactHashtable { ++protected: ++ address _base_address; ++ u4 _bucket_count; ++ u4 _entry_count; ++ u4* _buckets; ++ u4* _entries; ++ ++public: ++ SimpleCompactHashtable() { ++ _entry_count = 0; ++ _bucket_count = 0; ++ _buckets = 0; ++ _entries = 0; ++ } ++ ++ void reset() { ++ _bucket_count = 0; ++ _entry_count = 0; ++ _buckets = 0; ++ _entries = 0; ++ } ++ ++ void init(address base_address, u4 entry_count, u4 bucket_count, u4* buckets, u4* entries); ++ ++ // Read/Write the table's header from/to the CDS archive ++ void serialize_header(SerializeClosure* soc) NOT_CDS_RETURN; ++ ++ inline bool empty() const { ++ return (_entry_count == 0); ++ } ++ ++ inline size_t entry_count() const { ++ return _entry_count; ++ } ++ ++ static size_t calculate_header_size(); ++}; ++ ++template < ++ typename K, ++ typename V, ++ V (*DECODE)(address base_address, u4 offset), ++ bool (*EQUALS)(V value, K key, int len) ++ > ++class CompactHashtable : public SimpleCompactHashtable { ++ friend class VMStructs; ++ ++ V decode(u4 offset) const { ++ return DECODE(_base_address, offset); ++ } ++ ++public: ++ // Lookup a value V from the compact table using key K ++ inline V lookup(K key, unsigned int hash, int len) const { ++ if (_entry_count > 0) { ++ int index = hash % _bucket_count; ++ u4 bucket_info = _buckets[index]; ++ u4 bucket_offset = BUCKET_OFFSET(bucket_info); ++ int bucket_type = BUCKET_TYPE(bucket_info); ++ u4* entry = _entries + bucket_offset; ++ ++ if (bucket_type == VALUE_ONLY_BUCKET_TYPE) { ++ V value = decode(entry[0]); ++ if (EQUALS(value, key, len)) { ++ return value; ++ } ++ } else { ++ // This is a regular bucket, which has more than one ++ // entries. Each entry is a pair of entry (hash, offset). ++ // Seek until the end of the bucket. ++ u4* entry_max = _entries + BUCKET_OFFSET(_buckets[index + 1]); ++ while (entry < entry_max) { ++ unsigned int h = (unsigned int)(entry[0]); ++ if (h == hash) { ++ V value = decode(entry[1]); ++ if (EQUALS(value, key, len)) { ++ return value; ++ } ++ } ++ entry += 2; ++ } ++ } ++ } ++ return NULL; ++ } ++ ++ template ++ inline void iterate(ITER* iter) const { ++ for (u4 i = 0; i < _bucket_count; i++) { ++ u4 bucket_info = _buckets[i]; ++ u4 bucket_offset = BUCKET_OFFSET(bucket_info); ++ int bucket_type = BUCKET_TYPE(bucket_info); ++ u4* entry = _entries + bucket_offset; ++ ++ if (bucket_type == VALUE_ONLY_BUCKET_TYPE) { ++ iter->do_value(decode(entry[0])); ++ } else { ++ u4*entry_max = _entries + BUCKET_OFFSET(_buckets[i + 1]); ++ while (entry < entry_max) { ++ iter->do_value(decode(entry[1])); ++ entry += 2; ++ } ++ } ++ } ++ } ++ ++ void print_table_statistics(outputStream* st, const char* name) { ++ st->print_cr("%s statistics:", name); ++ int total_entries = 0; ++ int max_bucket = 0; ++ for (u4 i = 0; i < _bucket_count; i++) { ++ u4 bucket_info = _buckets[i]; ++ int bucket_type = BUCKET_TYPE(bucket_info); ++ int bucket_size; ++ ++ if (bucket_type == VALUE_ONLY_BUCKET_TYPE) { ++ bucket_size = 1; ++ } else { ++ bucket_size = (BUCKET_OFFSET(_buckets[i + 1]) - BUCKET_OFFSET(bucket_info)) / 2; ++ } ++ total_entries += bucket_size; ++ if (max_bucket < bucket_size) { ++ max_bucket = bucket_size; ++ } ++ } ++ st->print_cr("Number of buckets : %9d", _bucket_count); ++ st->print_cr("Number of entries : %9d", total_entries); ++ st->print_cr("Maximum bucket size : %9d", max_bucket); ++ } ++}; ++ ++//////////////////////////////////////////////////////////////////////// ++// ++// OffsetCompactHashtable -- This is used to store many types of objects ++// in the CDS archive. On 64-bit platforms, we save space by using a 32-bit ++// offset from the CDS base address. ++ ++template ++inline V read_value_from_compact_hashtable(address base_address, u4 offset) { ++ return (V)(base_address + offset); ++} ++ ++template < ++ typename K, ++ typename V, ++ bool (*EQUALS)(V value, K key, int len) ++ > ++class OffsetCompactHashtable : public CompactHashtable< ++ K, V, read_value_from_compact_hashtable, EQUALS> { ++}; ++ ++#endif // SHARE_VM_CLASSFILE_COMPACTHASHTABLE_HPP +diff --git a/hotspot/src/share/vm/classfile/sharedClassUtil.hpp b/hotspot/src/share/vm/classfile/sharedClassUtil.hpp +index 13be2b1b5..b24e84d45 100644 +--- a/hotspot/src/share/vm/classfile/sharedClassUtil.hpp ++++ b/hotspot/src/share/vm/classfile/sharedClassUtil.hpp +@@ -43,6 +43,10 @@ public: + return new FileMapInfo::FileMapHeader(); + } + ++ static FileMapInfo::DynamicArchiveHeader* allocate_dynamic_archive_header() { ++ return new FileMapInfo::DynamicArchiveHeader(); ++ } ++ + static size_t file_map_header_size() { + return sizeof(FileMapInfo::FileMapHeader); + } +diff --git a/hotspot/src/share/vm/classfile/symbolTable.cpp b/hotspot/src/share/vm/classfile/symbolTable.cpp +index 8dd4e6b21..6a2d8077f 100644 +--- a/hotspot/src/share/vm/classfile/symbolTable.cpp ++++ b/hotspot/src/share/vm/classfile/symbolTable.cpp +@@ -23,6 +23,8 @@ + */ + + #include "precompiled.hpp" ++#include "cds/archiveBuilder.hpp" ++#include "cds/dynamicArchive.hpp" + #include "classfile/altHashing.hpp" + #include "classfile/javaClasses.hpp" + #include "classfile/symbolTable.hpp" +@@ -42,6 +44,19 @@ + + PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + ++inline bool symbol_equals_compact_hashtable_entry(Symbol* value, const char* key, int len) { ++ if (value->equals(key, len)) { ++ return true; ++ } else { ++ return false; ++ } ++} ++ ++static OffsetCompactHashtable< ++ const char*, Symbol*, ++ symbol_equals_compact_hashtable_entry ++> _dynamic_shared_table; ++ + // -------------------------------------------------------------------------- + + // the number of buckets a thread claims +@@ -95,6 +110,7 @@ void SymbolTable::symbols_do(SymbolClosure *cl) { + int SymbolTable::_symbols_removed = 0; + int SymbolTable::_symbols_counted = 0; + volatile int SymbolTable::_parallel_claimed_idx = 0; ++volatile bool _lookup_shared_first = false; + + void SymbolTable::buckets_unlink(int start_idx, int end_idx, BucketUnlinkContext* context, size_t* memory_total) { + for (int i = start_idx; i < end_idx; ++i) { +@@ -225,10 +241,25 @@ Symbol* SymbolTable::lookup(int index, const char* name, + unsigned int SymbolTable::hash_symbol(const char* s, int len) { + return use_alternate_hashcode() ? + AltHashing::halfsiphash_32(seed(), (const uint8_t*)s, len) : +- java_lang_String::hash_code(s, len); ++ java_lang_String::hash_code((const jbyte*)s, len); + } + ++#if INCLUDE_CDS ++Symbol* SymbolTable::lookup_shared(const char* name, ++ int len, unsigned int hash) { ++ Symbol* sym = NULL; ++ if (DynamicArchive::is_mapped()) { ++ if (use_alternate_hashcode()) { ++ // hash_code parameter may use alternate hashing algorithm but the shared table ++ // always uses the same original hash code. ++ hash = java_lang_String::hash_code((const jbyte*)name, len); ++ } + ++ sym = _dynamic_shared_table.lookup(name, hash, len); ++ } ++ return sym; ++} ++#endif + // We take care not to be blocking while holding the + // SymbolTable_lock. Otherwise, the system might deadlock, since the + // symboltable is used during compilation (VM_thread) The lock free +@@ -236,12 +267,32 @@ unsigned int SymbolTable::hash_symbol(const char* s, int len) { + // entries in the symbol table during normal execution (only during + // safepoints). + +-Symbol* SymbolTable::lookup(const char* name, int len, TRAPS) { ++Symbol* SymbolTable::lookup_common(const char* name, int len) { + unsigned int hashValue = hash_symbol(name, len); + int index = the_table()->hash_to_index(hashValue); ++ Symbol* s; ++ if (_lookup_shared_first) { ++ s = lookup_shared(name, len, hashValue); ++ if (s == NULL) { ++ _lookup_shared_first = false; ++ s = the_table()->lookup(index, name, len, hashValue); ++ } ++ } else { ++ s = the_table()->lookup(index, name, len, hashValue); ++ if (s == NULL) { ++ s = lookup_shared(name, len, hashValue); ++ if (s!= NULL) { ++ _lookup_shared_first = true; ++ } ++ } ++ } ++ return s; ++} + +- Symbol* s = the_table()->lookup(index, name, len, hashValue); +- ++Symbol* SymbolTable::lookup(const char* name, int len, TRAPS) { ++ unsigned int hashValue = hash_symbol(name, len); ++ int index = the_table()->hash_to_index(hashValue); ++ Symbol* s = lookup_common(name, len); + // Found + if (s != NULL) return s; + +@@ -264,8 +315,7 @@ Symbol* SymbolTable::lookup(const Symbol* sym, int begin, int end, TRAPS) { + len = end - begin; + hashValue = hash_symbol(name, len); + index = the_table()->hash_to_index(hashValue); +- Symbol* s = the_table()->lookup(index, name, len, hashValue); +- ++ Symbol* s = lookup_common(name, len); + // Found + if (s != NULL) return s; + } +@@ -294,9 +344,7 @@ Symbol* SymbolTable::lookup(const Symbol* sym, int begin, int end, TRAPS) { + Symbol* SymbolTable::lookup_only(const char* name, int len, + unsigned int& hash) { + hash = hash_symbol(name, len); +- int index = the_table()->hash_to_index(hash); +- +- Symbol* s = the_table()->lookup(index, name, len, hash); ++ Symbol* s = lookup_common(name, len); + return s; + } + +@@ -501,6 +549,42 @@ void SymbolTable::dump(outputStream* st) { + the_table()->dump_table(st, "SymbolTable"); + } + ++static uintx hash_shared_symbol(const char* s, int len) { ++ return java_lang_String::hash_code((const jbyte*)s, len); ++} ++ ++void SymbolTable::copy_shared_symbol_table(GrowableArray* symbols, ++ CompactHashtableWriter* writer) { ++ ArchiveBuilder* builder = ArchiveBuilder::current(); ++ int len = symbols->length(); ++ for (int i = 0; i < len; i++) { ++ Symbol* sym = ArchiveBuilder::get_relocated_symbol(symbols->at(i)); ++ unsigned int fixed_hash = hash_shared_symbol((const char*)sym->bytes(), sym->utf8_length()); ++ assert(fixed_hash == hash_symbol((const char*)sym->bytes(), sym->utf8_length()), ++ "must not rehash during dumping"); ++ sym->set_permanent(); ++ writer->add(fixed_hash, builder->buffer_to_offset_u4((address)sym)); ++ } ++} ++ ++size_t SymbolTable::estimate_size_for_archive() { ++ return CompactHashtableWriter::estimate_size(the_table()->number_of_entries()); ++} ++ ++void SymbolTable::write_to_archive(GrowableArray* symbols) { ++ CompactHashtableWriter writer(symbols->length(), ArchiveBuilder::symbol_stats()); ++ copy_shared_symbol_table(symbols, &writer); ++ _dynamic_shared_table.reset(); ++ writer.dump(&_dynamic_shared_table, "symbol"); ++} ++ ++void SymbolTable::serialize_shared_table_header(SerializeClosure* soc) { ++ _dynamic_shared_table.serialize_header(soc); ++ if (soc->writing()) { ++ // Sanity. Make sure we don't use the shared table at dump time ++ _dynamic_shared_table.reset(); ++ } ++} + + //--------------------------------------------------------------------------- + // Non-product code +diff --git a/hotspot/src/share/vm/classfile/symbolTable.hpp b/hotspot/src/share/vm/classfile/symbolTable.hpp +index 58fd22343..96eb173d1 100644 +--- a/hotspot/src/share/vm/classfile/symbolTable.hpp ++++ b/hotspot/src/share/vm/classfile/symbolTable.hpp +@@ -25,6 +25,7 @@ + #ifndef SHARE_VM_CLASSFILE_SYMBOLTABLE_HPP + #define SHARE_VM_CLASSFILE_SYMBOLTABLE_HPP + ++#include "classfile/compactHashtable.hpp" + #include "memory/allocation.inline.hpp" + #include "oops/symbol.hpp" + #include "utilities/hashtable.hpp" +@@ -107,6 +108,10 @@ private: + add(loader_data, cp, names_count, name, lengths, cp_indices, hashValues, THREAD); + } + ++ static Symbol* lookup_shared(const char* name, int len, unsigned int hash) NOT_CDS_RETURN_(NULL); ++ ++ static Symbol* lookup_common(const char* name, int len); ++ + Symbol* lookup(int index, const char* name, int len, unsigned int hash); + + SymbolTable() +@@ -237,6 +242,10 @@ public: + static void dump(outputStream* st); + + // Sharing ++private: ++ static void copy_shared_symbol_table(GrowableArray* symbols, ++ CompactHashtableWriter* ch_table); ++public: + static void copy_buckets(char** top, char*end) { + the_table()->Hashtable::copy_buckets(top, end); + } +@@ -246,6 +255,9 @@ public: + static void reverse(void* boundary = NULL) { + the_table()->Hashtable::reverse(boundary); + } ++ static size_t estimate_size_for_archive(); ++ static void write_to_archive(GrowableArray* symbols); ++ static void serialize_shared_table_header(SerializeClosure* soc); + + // Rehash the symbol table if it gets out of balance + static void rehash_table(); +diff --git a/hotspot/src/share/vm/classfile/systemDictionary.cpp b/hotspot/src/share/vm/classfile/systemDictionary.cpp +index 0d937c3ba..0ea2d9b79 100644 +--- a/hotspot/src/share/vm/classfile/systemDictionary.cpp ++++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp +@@ -31,6 +31,7 @@ + #include "classfile/resolutionErrors.hpp" + #include "classfile/systemDictionary.hpp" + #if INCLUDE_CDS ++#include "cds/dynamicArchive.hpp" + #include "classfile/sharedClassUtil.hpp" + #include "classfile/systemDictionaryShared.hpp" + #endif +@@ -185,6 +186,11 @@ bool SystemDictionary::is_app_class_loader(Handle class_loader) { + return (class_loader->klass()->name() == vmSymbols::sun_misc_Launcher_AppClassLoader()); + } + ++bool SystemDictionary::is_builtin_loader(Handle class_loader) { ++ return class_loader.is_null() || ++ class_loader->klass()->name() == vmSymbols::sun_misc_Launcher_AppClassLoader() || ++ class_loader->klass()->name() == vmSymbols::sun_misc_Launcher_ExtClassLoader(); ++} + // ---------------------------------------------------------------------------- + // Resolving of classes + +@@ -1131,76 +1137,92 @@ Klass* SystemDictionary::resolve_from_stream(Symbol* class_name, + check_loader_lock_contention(lockObject, THREAD); + ObjectLocker ol(lockObject, THREAD, DoObjectLock); + ++ instanceKlassHandle k; + TempNewSymbol parsed_name = NULL; + +- // Parse the stream. Note that we do this even though this klass might +- // already be present in the SystemDictionary, otherwise we would not +- // throw potential ClassFormatErrors. +- // +- // Note: "name" is updated. ++#if INCLUDE_CDS ++ if (DynamicArchive::is_mapped()) { ++ k = SystemDictionaryShared::lookup_from_stream(class_name, ++ class_loader, ++ protection_domain, ++ st, ++ CHECK_NULL); ++ } ++#endif + +- // Callers are expected to declare a ResourceMark to determine +- // the lifetime of any updated (resource) allocated under +- // this call to parseClassFile +- ResourceMark rm(THREAD); +- ClassFileParser parser(st); +- instanceKlassHandle k = parser.parseClassFile(class_name, +- loader_data, +- protection_domain, +- parsed_name, +- verify, +- THREAD); +- +- const char* pkg = "java/"; +- size_t pkglen = strlen(pkg); +- if (!HAS_PENDING_EXCEPTION && +- !class_loader.is_null() && +- parsed_name != NULL && +- parsed_name->utf8_length() >= (int)pkglen) { +- ResourceMark rm(THREAD); +- bool prohibited; +- const jbyte* base = parsed_name->base(); +- if ((base[0] | base[1] | base[2] | base[3] | base[4]) & 0x80) { +- prohibited = is_prohibited_package_slow(parsed_name); +- } else { +- char* name = parsed_name->as_C_string(); +- prohibited = (strncmp(name, pkg, pkglen) == 0); +- } +- if (prohibited) { +- // It is illegal to define classes in the "java." package from +- // JVM_DefineClass or jni_DefineClass unless you're the bootclassloader +- char* name = parsed_name->as_C_string(); +- char* index = strrchr(name, '/'); +- assert(index != NULL, "must be"); +- *index = '\0'; // chop to just the package name +- while ((index = strchr(name, '/')) != NULL) { +- *index = '.'; // replace '/' with '.' in package name ++ if (k() != NULL) { ++ parsed_name = k->name(); ++ } else { ++ // Parse the stream. Note that we do this even though this klass might ++ // already be present in the SystemDictionary, otherwise we would not ++ // throw potential ClassFormatErrors. ++ // ++ // Note: "name" is updated. ++ ++ // Callers are expected to declare a ResourceMark to determine ++ // the lifetime of any updated (resource) allocated under ++ // this call to parseClassFile ++ ResourceMark rm(THREAD); ++ ClassFileParser parser(st); ++ k = parser.parseClassFile(class_name, ++ loader_data, ++ protection_domain, ++ parsed_name, ++ verify, ++ THREAD); ++ const char* pkg = "java/"; ++ size_t pkglen = strlen(pkg); ++ if (!HAS_PENDING_EXCEPTION && ++ !class_loader.is_null() && ++ parsed_name != NULL && ++ parsed_name->utf8_length() >= (int)pkglen) { ++ ResourceMark rm(THREAD); ++ bool prohibited; ++ const jbyte* base = parsed_name->base(); ++ if ((base[0] | base[1] | base[2] | base[3] | base[4]) & 0x80) { ++ prohibited = is_prohibited_package_slow(parsed_name); ++ } else { ++ char* name = parsed_name->as_C_string(); ++ prohibited = (strncmp(name, pkg, pkglen) == 0); + } +- const char* fmt = "Prohibited package name: %s"; +- size_t len = strlen(fmt) + strlen(name); +- char* message = NEW_RESOURCE_ARRAY(char, len); +- jio_snprintf(message, len, fmt, name); +- Exceptions::_throw_msg(THREAD_AND_LOCATION, +- vmSymbols::java_lang_SecurityException(), message); +- } +- } ++ if (prohibited) { ++ // It is illegal to define classes in the "java." package from ++ // JVM_DefineClass or jni_DefineClass unless you're the bootclassloader ++ char* name = parsed_name->as_C_string(); ++ char* index = strrchr(name, '/'); ++ assert(index != NULL, "must be"); ++ *index = '\0'; // chop to just the package name ++ while ((index = strchr(name, '/')) != NULL) { ++ *index = '.'; // replace '/' with '.' in package name ++ } ++ const char* fmt = "Prohibited package name: %s"; ++ size_t len = strlen(fmt) + strlen(name); ++ char* message = NEW_RESOURCE_ARRAY(char, len); ++ jio_snprintf(message, len, fmt, name); ++ Exceptions::_throw_msg(THREAD_AND_LOCATION, ++ vmSymbols::java_lang_SecurityException(), message); ++ } ++ } + +- if (!HAS_PENDING_EXCEPTION) { +- assert(parsed_name != NULL, "Sanity"); +- assert(class_name == NULL || class_name == parsed_name, "name mismatch"); +- // Verification prevents us from creating names with dots in them, this +- // asserts that that's the case. +- assert(is_internal_format(parsed_name), +- "external class name format used internally"); ++ if (!HAS_PENDING_EXCEPTION) { ++ assert(parsed_name != NULL, "Sanity"); ++ assert(class_name == NULL || class_name == parsed_name, "name mismatch"); ++ // Verification prevents us from creating names with dots in them, this ++ // asserts that that's the case. ++ assert(is_internal_format(parsed_name), ++ "external class name format used internally"); + + #if INCLUDE_JFR +- { +- InstanceKlass* ik = k(); +- ON_KLASS_CREATION(ik, parser, THREAD); +- k = instanceKlassHandle(ik); +- } ++ { ++ InstanceKlass* ik = k(); ++ ON_KLASS_CREATION(ik, parser, THREAD); ++ k = instanceKlassHandle(ik); ++ } + #endif ++ } ++ } + ++ if (!HAS_PENDING_EXCEPTION) { + // Add class just loaded + // If a class loader supports parallel classloading handle parallel define requests + // find_or_define_instance_class may return a different InstanceKlass +@@ -1274,14 +1296,19 @@ Klass* SystemDictionary::find_shared_class(Symbol* class_name) { + + instanceKlassHandle SystemDictionary::load_shared_class( + Symbol* class_name, Handle class_loader, TRAPS) { +- if (!(class_loader.is_null() || SystemDictionary::is_app_class_loader(class_loader) || ++ if (!(class_loader.is_null() || SystemDictionary::is_app_class_loader(class_loader) || + SystemDictionary::is_ext_class_loader(class_loader))) { + return instanceKlassHandle(); + } + +- instanceKlassHandle ik (THREAD, find_shared_class(class_name)); // InstanceKlass is find with null class loader. ++ Klass* klass = SystemDictionaryShared::find_dynamic_builtin_class(class_name); ++ if (klass == NULL) { ++ klass = find_shared_class(class_name); ++ } ++ ++ instanceKlassHandle ik (THREAD, klass); // InstanceKlass is find with null class loader. + if (ik.not_null()) { +- if (!UseAppCDS) { ++ if (!(UseAppCDS || DynamicArchive::is_mapped())) { + // CDS logic + if (SharedClassUtil::is_shared_boot_class(ik()) && class_loader.is_null()) { + // CDS record boot class load index. +@@ -1289,7 +1316,7 @@ instanceKlassHandle SystemDictionary::load_shared_class( + return load_shared_class(ik, class_loader, protection_domain, THREAD); + } + } else { +- // AppCDS logic. Only use null loader only to load classes that ++ // AppCDS and dynamic CDS logic. Only use null loader only to load classes that + // have been dumped by null loader. For non-null class loaders, + // either the class loader data is not initialized (but also not + // null) or the same class loader is used to load previously +@@ -1424,7 +1451,7 @@ instanceKlassHandle SystemDictionary::load_shared_class(instanceKlassHandle ik, + true /* shared class */); + + // register package for this class, if necessary +- if (UseAppCDS && class_loader.not_null()) { ++ if (SystemDictionary::is_app_class_loader(class_loader) || SystemDictionary::is_ext_class_loader(class_loader)) { + + ResourceMark rm(THREAD); + char* name = ik->name()->as_C_string(); +diff --git a/hotspot/src/share/vm/classfile/systemDictionary.hpp b/hotspot/src/share/vm/classfile/systemDictionary.hpp +index 3b9be4430..320f71865 100644 +--- a/hotspot/src/share/vm/classfile/systemDictionary.hpp ++++ b/hotspot/src/share/vm/classfile/systemDictionary.hpp +@@ -652,6 +652,7 @@ public: + TRAPS); + static bool is_ext_class_loader(Handle class_loader); + static bool is_app_class_loader(Handle class_loader); ++ static bool is_builtin_loader(Handle class_loader); + + protected: + static Klass* find_shared_class(Symbol* class_name); +diff --git a/hotspot/src/share/vm/classfile/systemDictionaryShared.cpp b/hotspot/src/share/vm/classfile/systemDictionaryShared.cpp +new file mode 100644 +index 000000000..99354cd4b +--- /dev/null ++++ b/hotspot/src/share/vm/classfile/systemDictionaryShared.cpp +@@ -0,0 +1,911 @@ ++/* ++ * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "cds/archiveBuilder.hpp" ++#include "cds/dynamicArchive.hpp" ++#include "classfile/systemDictionaryShared.hpp" ++#include "classfile/classLoaderData.inline.hpp" ++#include "runtime/arguments.hpp" ++#include "runtime/mutexLocker.hpp" ++#include "memory/metaspaceShared.hpp" ++#include "memory/metaspaceClosure.hpp" ++#include "utilities/resourceHash.hpp" ++#include "runtime/mutexLocker.hpp" ++#include "utilities/ostream.hpp" ++ ++DEBUG_ONLY(bool SystemDictionaryShared::_no_class_loading_should_happen = false;) ++bool SystemDictionaryShared::_dump_in_progress = false; ++ ++class DumpTimeSharedClassInfo: public CHeapObj { ++ bool _excluded; ++ bool _has_checked_exclusion; ++public: ++ struct DTLoaderConstraint { ++ Symbol* _name; ++ char _loader_type1; ++ char _loader_type2; ++ DTLoaderConstraint(Symbol* name, char l1, char l2) : _name(name), _loader_type1(l1), _loader_type2(l2) { ++ _name->increment_refcount(); ++ } ++ DTLoaderConstraint() : _name(NULL), _loader_type1('0'), _loader_type2('0') {} ++ bool equals(const DTLoaderConstraint& t) { ++ return t._name == _name && ++ ((t._loader_type1 == _loader_type1 && t._loader_type2 == _loader_type2) || ++ (t._loader_type2 == _loader_type1 && t._loader_type1 == _loader_type2)); ++ } ++ }; ++ ++ struct DTVerifierConstraint { ++ Symbol* _name; ++ Symbol* _from_name; ++ DTVerifierConstraint() : _name(NULL), _from_name(NULL) {} ++ DTVerifierConstraint(Symbol* n, Symbol* fn) : _name(n), _from_name(fn) { ++ _name->increment_refcount(); ++ _from_name->increment_refcount(); ++ } ++ }; ++ ++ InstanceKlass* _klass; ++ InstanceKlass* _nest_host; ++ bool _failed_verification; ++ bool _is_archived_lambda_proxy; ++ int _id; ++ int _clsfile_size; ++ int _clsfile_crc32; ++ GrowableArray* _verifier_constraints; ++ GrowableArray* _verifier_constraint_flags; ++ GrowableArray* _loader_constraints; ++ ++ DumpTimeSharedClassInfo() { ++ _klass = NULL; ++ _nest_host = NULL; ++ _failed_verification = false; ++ _is_archived_lambda_proxy = false; ++ _has_checked_exclusion = false; ++ _id = -1; ++ _clsfile_size = -1; ++ _clsfile_crc32 = -1; ++ _excluded = false; ++ _verifier_constraints = NULL; ++ _verifier_constraint_flags = NULL; ++ _loader_constraints = NULL; ++ } ++ ++ void add_verification_constraint(InstanceKlass* k, Symbol* name, ++ Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object); ++ void record_linking_constraint(Symbol* name, Handle loader1, Handle loader2); ++ ++ bool is_builtin() { ++ return SystemDictionaryShared::is_builtin(_klass); ++ } ++ ++ int num_verifier_constraints() { ++ if (_verifier_constraint_flags != NULL) { ++ return _verifier_constraint_flags->length(); ++ } else { ++ return 0; ++ } ++ } ++ ++ int num_loader_constraints() { ++ if (_loader_constraints != NULL) { ++ return _loader_constraints->length(); ++ } else { ++ return 0; ++ } ++ } ++ ++ void metaspace_pointers_do(MetaspaceClosure* it) { ++ it->push(&_klass); ++ it->push(&_nest_host); ++ if (_verifier_constraints != NULL) { ++ for (int i = 0; i < _verifier_constraints->length(); i++) { ++ DTVerifierConstraint* cons = _verifier_constraints->adr_at(i); ++ it->push(&cons->_name); ++ it->push(&cons->_from_name); ++ } ++ } ++ if (_loader_constraints != NULL) { ++ for (int i = 0; i < _loader_constraints->length(); i++) { ++ DTLoaderConstraint* lc = _loader_constraints->adr_at(i); ++ it->push(&lc->_name); ++ } ++ } ++ } ++ ++ bool is_excluded() { ++ // _klass may become NULL due to DynamicArchiveBuilder::set_to_null ++ return _excluded || _failed_verification || _klass == NULL; ++ } ++ ++ // simple accessors ++ void set_excluded() { _excluded = true; } ++ bool has_checked_exclusion() const { return _has_checked_exclusion; } ++ void set_has_checked_exclusion() { _has_checked_exclusion = true; } ++ bool failed_verification() const { return _failed_verification; } ++ void set_failed_verification() { _failed_verification = true; } ++ InstanceKlass* nest_host() const { return _nest_host; } ++ void set_nest_host(InstanceKlass* nest_host) { _nest_host = nest_host; } ++}; ++ ++inline unsigned DumpTimeSharedClassTable_hash(InstanceKlass* const& k) { ++ // Deterministic archive is not possible because classes can be loaded ++ // in multiple threads. ++ return primitive_hash(k); ++} ++ ++class DumpTimeSharedClassTable: public ResourceHashtable< ++ InstanceKlass*, ++ DumpTimeSharedClassInfo, ++ &DumpTimeSharedClassTable_hash, ++ primitive_equals, ++ 15889, // prime number ++ ResourceObj::C_HEAP> ++{ ++ int _builtin_count; ++ int _unregistered_count; ++public: ++ DumpTimeSharedClassInfo* find_or_allocate_info_for(InstanceKlass* k, bool dump_in_progress) { ++ bool created = false; ++ DumpTimeSharedClassInfo* p; ++ if (!dump_in_progress) { ++ p = put_if_absent(k, &created); ++ } else { ++ p = get(k); ++ } ++ if (created) { ++ assert(!SystemDictionaryShared::no_class_loading_should_happen(), ++ "no new classes can be loaded while dumping archive"); ++ p->_klass = k; ++ } else { ++ if (!dump_in_progress) { ++ assert(p->_klass == k, "Sanity"); ++ } ++ } ++ return p; ++ } ++ ++ class CountClassByCategory : StackObj { ++ DumpTimeSharedClassTable* _table; ++ public: ++ CountClassByCategory(DumpTimeSharedClassTable* table) : _table(table) {} ++ bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { ++ if (!info.is_excluded()) { ++ if (info.is_builtin()) { ++ ++ _table->_builtin_count; ++ } else { ++ ++ _table->_unregistered_count; ++ } ++ } ++ return true; // keep on iterating ++ } ++ }; ++ ++ void update_counts() { ++ _builtin_count = 0; ++ _unregistered_count = 0; ++ CountClassByCategory counter(this); ++ iterate(&counter); ++ } ++ ++ int count_of(bool is_builtin) const { ++ if (is_builtin) { ++ return _builtin_count; ++ } else { ++ return _unregistered_count; ++ } ++ } ++}; ++ ++class RunTimeSharedClassInfo { ++public: ++ struct CrcInfo { ++ int _clsfile_size; ++ int _clsfile_crc32; ++ }; ++ ++ // This is different than DumpTimeSharedClassInfo::DTVerifierConstraint. We use ++ // u4 instead of Symbol* to save space on 64-bit CPU. ++ struct RTVerifierConstraint { ++ u4 _name; ++ u4 _from_name; ++ Symbol* name() { return (Symbol*)(SharedBaseAddress + _name);} ++ Symbol* from_name() { return (Symbol*)(SharedBaseAddress + _from_name); } ++ }; ++ ++ struct RTLoaderConstraint { ++ u4 _name; ++ char _loader_type1; ++ char _loader_type2; ++ Symbol* constraint_name() { ++ return (Symbol*)(SharedBaseAddress + _name); ++ } ++ }; ++ ++ InstanceKlass* _klass; ++ int _num_verifier_constraints; ++ int _num_loader_constraints; ++ ++ // optional CrcInfo _crc; (only for UNREGISTERED classes) ++ // optional InstanceKlass* _nest_host ++ // optional RTLoaderConstraint _loader_constraint_types[_num_loader_constraints] ++ // optional RTVerifierConstraint _verifier_constraints[_num_verifier_constraints] ++ // optional char _verifier_constraint_flags[_num_verifier_constraints] ++ ++private: ++ static size_t header_size_size() { ++ return sizeof(RunTimeSharedClassInfo); ++ } ++ static size_t crc_size(InstanceKlass* klass) { ++ if (!SystemDictionaryShared::is_builtin(klass)) { ++ return sizeof(CrcInfo); ++ } else { ++ return 0; ++ } ++ } ++ static size_t verifier_constraints_size(int num_verifier_constraints) { ++ return sizeof(RTVerifierConstraint) * num_verifier_constraints; ++ } ++ static size_t verifier_constraint_flags_size(int num_verifier_constraints) { ++ return sizeof(char) * num_verifier_constraints; ++ } ++ static size_t loader_constraints_size(int num_loader_constraints) { ++ return sizeof(RTLoaderConstraint) * num_loader_constraints; ++ } ++ static size_t nest_host_size(InstanceKlass* klass) { ++ assert(!klass->is_anonymous(), "klass should not be hidden right now."); ++ if (klass->is_anonymous()) { ++ return sizeof(InstanceKlass*); ++ } else { ++ return 0; ++ } ++ } ++ ++public: ++ static size_t byte_size(InstanceKlass* klass, int num_verifier_constraints, int num_loader_constraints) { ++ return header_size_size() + ++ crc_size(klass) + ++ nest_host_size(klass) + ++ loader_constraints_size(num_loader_constraints) + ++ verifier_constraints_size(num_verifier_constraints) + ++ verifier_constraint_flags_size(num_verifier_constraints); ++ } ++ ++private: ++ size_t crc_offset() const { ++ return header_size_size(); ++ } ++ ++ size_t nest_host_offset() const { ++ return crc_offset() + crc_size(_klass); ++ } ++ ++ size_t loader_constraints_offset() const { ++ return nest_host_offset() + nest_host_size(_klass); ++ } ++ size_t verifier_constraints_offset() const { ++ return loader_constraints_offset() + loader_constraints_size(_num_loader_constraints); ++ } ++ size_t verifier_constraint_flags_offset() const { ++ return verifier_constraints_offset() + verifier_constraints_size(_num_verifier_constraints); ++ } ++ ++ void check_verifier_constraint_offset(int i) const { ++ assert(0 <= i && i < _num_verifier_constraints, "sanity"); ++ } ++ ++ void check_loader_constraint_offset(int i) const { ++ assert(0 <= i && i < _num_loader_constraints, "sanity"); ++ } ++ ++public: ++ CrcInfo* crc() const { ++ assert(crc_size(_klass) > 0, "must be"); ++ return (CrcInfo*)(address(this) + crc_offset()); ++ } ++ RTVerifierConstraint* verifier_constraints() { ++ assert(_num_verifier_constraints > 0, "sanity"); ++ return (RTVerifierConstraint*)(address(this) + verifier_constraints_offset()); ++ } ++ RTVerifierConstraint* verifier_constraint_at(int i) { ++ check_verifier_constraint_offset(i); ++ return verifier_constraints() + i; ++ } ++ ++ char* verifier_constraint_flags() { ++ assert(_num_verifier_constraints > 0, "sanity"); ++ return (char*)(address(this) + verifier_constraint_flags_offset()); ++ } ++ ++ RTLoaderConstraint* loader_constraints() { ++ assert(_num_loader_constraints > 0, "sanity"); ++ return (RTLoaderConstraint*)(address(this) + loader_constraints_offset()); ++ } ++ ++ RTLoaderConstraint* loader_constraint_at(int i) { ++ check_loader_constraint_offset(i); ++ return loader_constraints() + i; ++ } ++ ++ void init(DumpTimeSharedClassInfo& info) { ++ ArchiveBuilder* builder = ArchiveBuilder::current(); ++ assert(builder->is_in_buffer_space(info._klass), "must be"); ++ _klass = info._klass; ++ if (!SystemDictionaryShared::is_builtin(_klass)) { ++ CrcInfo* c = crc(); ++ c->_clsfile_size = info._clsfile_size; ++ c->_clsfile_crc32 = info._clsfile_crc32; ++ } ++ _num_verifier_constraints = info.num_verifier_constraints(); ++ _num_loader_constraints = info.num_loader_constraints(); ++ int i; ++ if (_num_verifier_constraints > 0) { ++ RTVerifierConstraint* vf_constraints = verifier_constraints(); ++ char* flags = verifier_constraint_flags(); ++ for (i = 0; i < _num_verifier_constraints; i++) { ++ vf_constraints[i]._name = builder->any_to_offset_u4(info._verifier_constraints->at(i)._name); ++ vf_constraints[i]._from_name = builder->any_to_offset_u4(info._verifier_constraints->at(i)._from_name); ++ } ++ for (i = 0; i < _num_verifier_constraints; i++) { ++ flags[i] = info._verifier_constraint_flags->at(i); ++ } ++ } ++ ++ if (_num_loader_constraints > 0) { ++ RTLoaderConstraint* ld_constraints = loader_constraints(); ++ for (i = 0; i < _num_loader_constraints; i++) { ++ ld_constraints[i]._name = builder->any_to_offset_u4(info._loader_constraints->at(i)._name); ++ ld_constraints[i]._loader_type1 = info._loader_constraints->at(i)._loader_type1; ++ ld_constraints[i]._loader_type2 = info._loader_constraints->at(i)._loader_type2; ++ } ++ } ++ ++ ArchivePtrMarker::mark_pointer(&_klass); ++ } ++ ++ bool matches(int clsfile_size, int clsfile_crc32) const { ++ return crc()->_clsfile_size == clsfile_size && ++ crc()->_clsfile_crc32 == clsfile_crc32; ++ } ++ ++ char verifier_constraint_flag(int i) { ++ check_verifier_constraint_offset(i); ++ return verifier_constraint_flags()[i]; ++ } ++ ++private: ++ // ArchiveBuilder::make_shallow_copy() has reserved a pointer immediately ++ // before archived InstanceKlasses. We can use this slot to do a quick ++ // lookup of InstanceKlass* -> RunTimeSharedClassInfo* without ++ // building a new hashtable. ++ // ++ // info_pointer_addr(klass) --> 0x0100 RunTimeSharedClassInfo* ++ // InstanceKlass* klass --> 0x0108 ++ // 0x0110 fields from Klass ... ++ static RunTimeSharedClassInfo** info_pointer_addr(InstanceKlass* klass) { ++ return &((RunTimeSharedClassInfo**)klass)[-1]; ++ } ++ ++public: ++ static RunTimeSharedClassInfo* get_for(InstanceKlass* klass) { ++ assert(klass->is_shared(), "don't call for non-shared class"); ++ return *info_pointer_addr(klass); ++ } ++ static void set_for(InstanceKlass* klass, RunTimeSharedClassInfo* record) { ++ assert(ArchiveBuilder::current()->is_in_buffer_space(klass), "must be"); ++ assert(ArchiveBuilder::current()->is_in_buffer_space(record), "must be"); ++ *info_pointer_addr(klass) = record; ++ ArchivePtrMarker::mark_pointer(info_pointer_addr(klass)); ++ } ++ ++ // Used by RunTimeSharedDictionary to implement OffsetCompactHashtable::EQUALS ++ static inline bool EQUALS( ++ const RunTimeSharedClassInfo* value, Symbol* key, int len_unused) { ++ return (value->_klass->name() == key); ++ } ++}; ++ ++class RunTimeSharedDictionary : public OffsetCompactHashtable< ++ Symbol*, ++ const RunTimeSharedClassInfo*, ++ RunTimeSharedClassInfo::EQUALS> {}; ++ ++static DumpTimeSharedClassTable* _dumptime_table = NULL; ++// SystemDictionaries in the top layer dynamic archive ++static RunTimeSharedDictionary _dynamic_builtin_dictionary; ++static RunTimeSharedDictionary _dynamic_unregistered_dictionary; ++ ++void SystemDictionaryShared::set_class_has_failed_verification(InstanceKlass* ik) { ++ Arguments::assert_is_dumping_archive(); ++ DumpTimeSharedClassInfo* p = find_or_allocate_info_for(ik); ++ if (p != NULL) { ++ p->set_failed_verification(); ++ } ++} ++ ++void SystemDictionaryShared::start_dumping() { ++ MutexLockerEx ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); ++ _dump_in_progress = true; ++} ++ ++void SystemDictionaryShared::init_dumptime_info(InstanceKlass* k) { ++ (void)find_or_allocate_info_for(k); ++} ++ ++void SystemDictionaryShared::remove_dumptime_info(InstanceKlass* k) { ++ MutexLockerEx ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); ++ DumpTimeSharedClassInfo* p = _dumptime_table->get(k); ++ if (p == NULL) { ++ return; ++ } ++ _dumptime_table->remove(k); ++} ++ ++DumpTimeSharedClassInfo* SystemDictionaryShared::find_or_allocate_info_for(InstanceKlass* k) { ++ MutexLockerEx ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); ++ return find_or_allocate_info_for_locked(k); ++} ++ ++DumpTimeSharedClassInfo* SystemDictionaryShared::find_or_allocate_info_for_locked(InstanceKlass* k) { ++ assert_lock_strong(DumpTimeTable_lock); ++ if (_dumptime_table == NULL) { ++ _dumptime_table = new (ResourceObj::C_HEAP, mtClass)DumpTimeSharedClassTable(); ++ } ++ return _dumptime_table->find_or_allocate_info_for(k, _dump_in_progress); ++} ++ ++bool SystemDictionaryShared::empty_dumptime_table() { ++ if (_dumptime_table == NULL) { ++ return true; ++ } ++ _dumptime_table->update_counts(); ++ if (_dumptime_table->count_of(true) == 0 && _dumptime_table->count_of(false) == 0) { ++ return true; ++ } ++ return false; ++} ++ ++class ExcludeDumpTimeSharedClasses : StackObj { ++public: ++ bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { ++ SystemDictionaryShared::check_for_exclusion(k, &info); ++ return true; // keep on iterating ++ } ++}; ++ ++class IterateDumpTimeSharedClassTable : StackObj { ++ MetaspaceClosure *_it; ++public: ++ IterateDumpTimeSharedClassTable(MetaspaceClosure* it) : _it(it) {} ++ ++ bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { ++ assert_lock_strong(DumpTimeTable_lock); ++ if (!info.is_excluded()) { ++ info.metaspace_pointers_do(_it); ++ } ++ return true; // keep on iterating ++ } ++}; ++ ++class IterateDumpTimeTableReplaceKlass : StackObj { ++public: ++ IterateDumpTimeTableReplaceKlass() { } ++ ++ bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { ++ if (k->oop_is_instance() && !info.is_excluded()) { ++ k->constants()->symbol_replace_excluded_klass(); ++ } ++ return true; ++ } ++}; ++ ++void SystemDictionaryShared::check_excluded_classes() { ++ assert(no_class_loading_should_happen(), "sanity"); ++ assert_lock_strong(DumpTimeTable_lock); ++ ExcludeDumpTimeSharedClasses excl; ++ _dumptime_table->iterate(&excl); ++ _dumptime_table->update_counts(); ++} ++ ++bool SystemDictionaryShared::check_for_exclusion(InstanceKlass* k, DumpTimeSharedClassInfo* info) { ++ if (MetaspaceShared::is_in_shared_space(k)) { ++ // We have reached a super type that's already in the base archive. Treat it ++ // as "not excluded". ++ assert(DynamicDumpSharedSpaces, "must be"); ++ return false; ++ } ++ ++ if (info == NULL) { ++ info = _dumptime_table->get(k); ++ assert(info != NULL, "supertypes of any classes in _dumptime_table must either be shared, or must also be in _dumptime_table"); ++ } ++ ++ if (!info->has_checked_exclusion()) { ++ if (check_for_exclusion_impl(k)) { ++ info->set_excluded(); ++ } ++ info->set_has_checked_exclusion(); ++ } ++ ++ return info->is_excluded(); ++} ++ ++// Check if a class or any of its supertypes has been redefined. ++bool SystemDictionaryShared::has_been_redefined(InstanceKlass* k) { ++ if (k->has_been_redefined()) { ++ return true; ++ } ++ if (k->java_super() != NULL && has_been_redefined(k->java_super())) { ++ return true; ++ } ++ Array* interfaces = k->local_interfaces(); ++ int len = interfaces->length(); ++ for (int i = 0; i < len; i++) { ++ if (has_been_redefined((InstanceKlass*)interfaces->at(i))) { ++ return true; ++ } ++ } ++ return false; ++} ++ ++bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) { ++ if (k->is_in_error_state()) { ++ return warn_excluded(k, "In error state"); ++ } ++ if (k->init_state() < InstanceKlass::loaded) { ++ return warn_excluded(k, "not loaded klass"); ++ } ++ if (has_been_redefined(k)) { ++ return warn_excluded(k, "Has been redefined"); ++ } ++ if (k->signers() != NULL) { ++ // We cannot include signed classes in the archive because the certificates ++ // used during dump time may be different than those used during ++ // runtime (due to expiration, etc). ++ return warn_excluded(k, "Signed JAR"); ++ } ++ if (is_jfr_event_class(k)) { ++ // We cannot include JFR event classes because they need runtime-specific ++ // instrumentation in order to work with -XX:FlightRecorderOptions:retransform=false. ++ // There are only a small number of these classes, so it's not worthwhile to ++ // support them and make CDS more complicated. ++ return warn_excluded(k, "JFR event class"); ++ } ++ if (k->init_state() < InstanceKlass::linked) { ++ // In CDS dumping, we will attempt to link all classes. Those that fail to link will ++ // be recorded in DumpTimeSharedClassInfo. ++ Arguments::assert_is_dumping_archive(); ++ ++ // TODO -- rethink how this can be handled. ++ // We should try to link ik, however, we can't do it here because ++ // 1. We are at VM exit ++ // 2. linking a class may cause other classes to be loaded, which means ++ // a custom ClassLoader.loadClass() may be called, at a point where the ++ // class loader doesn't expect it. ++ if (has_class_failed_verification(k)) { ++ return warn_excluded(k, "Failed verification"); ++ } else { ++ if (k->can_be_verified_at_dumptime()) { ++ return warn_excluded(k, "Not linked"); ++ } ++ } ++ } ++ if (DynamicDumpSharedSpaces && k->major_version() < 50 /*JAVA_6_VERSION*/) { ++ // In order to support old classes during dynamic dump, class rewriting needs to ++ // be reverted. This would result in more complex code and testing but not much gain. ++ ResourceMark rm; ++ dynamic_cds_log->print_cr("Pre JDK 6 class not supported by CDS: %u.%u %s", ++ k->major_version(), k->minor_version(), k->name()->as_C_string()); ++ return true; ++ } ++ ++ if (!k->can_be_verified_at_dumptime() && k->is_linked()) { ++ return warn_excluded(k, "Old class has been linked"); ++ } ++ ++ if (k->is_anonymous() /* && !is_registered_lambda_proxy_class(k) */) { ++ return warn_excluded(k, "Hidden class"); ++ } ++ ++ InstanceKlass* super = k->java_super(); ++ if (super != NULL && check_for_exclusion(super, NULL)) { ++ ResourceMark rm; ++ dynamic_cds_log->print_cr("Skipping %s: super class %s is excluded", k->name()->as_C_string(), super->name()->as_C_string()); ++ return true; ++ } ++ ++ Array* interfaces = k->local_interfaces(); ++ int len = interfaces->length(); ++ for (int i = 0; i < len; i++) { ++ InstanceKlass* intf = (InstanceKlass*)interfaces->at(i); ++ if (check_for_exclusion(intf, NULL)) { ++ dynamic_cds_log->print_cr("Skipping %s: interface %s is excluded", k->name()->as_C_string(), intf->name()->as_C_string()); ++ return true; ++ } ++ } ++ ++ return false; // false == k should NOT be excluded ++} ++ ++// Returns true so the caller can do: return warn_excluded("....."); ++bool SystemDictionaryShared::warn_excluded(InstanceKlass* k, const char* reason) { ++ ResourceMark rm; ++ dynamic_cds_log->print_cr("Skipping %s: %s", k->name()->as_C_string(), reason); ++ return true; ++} ++ ++bool SystemDictionaryShared::is_jfr_event_class(InstanceKlass *k) { ++ while (k) { ++ if (k->name()->equals("jdk/jfr/Event")) { ++ return true; ++ } ++ k = k->java_super(); ++ } ++ return false; ++} ++ ++bool SystemDictionaryShared::has_class_failed_verification(InstanceKlass* ik) { ++ if (_dumptime_table == NULL) { ++ assert(DynamicDumpSharedSpaces, "sanity"); ++ assert(ik->is_shared(), "must be a shared class in the static archive"); ++ return false; ++ } ++ DumpTimeSharedClassInfo* p = _dumptime_table->get(ik); ++ return (p == NULL) ? false : p->failed_verification(); ++} ++ ++void SystemDictionaryShared::dumptime_classes_do(class MetaspaceClosure* it) { ++ assert_lock_strong(DumpTimeTable_lock); ++ IterateDumpTimeSharedClassTable iter(it); ++ _dumptime_table->iterate(&iter); ++} ++ ++void SystemDictionaryShared::replace_klass_in_constantPool() { ++ IterateDumpTimeTableReplaceKlass iter; ++ _dumptime_table->iterate(&iter); ++} ++ ++bool SystemDictionaryShared::is_excluded_class(InstanceKlass* k) { ++ assert(_no_class_loading_should_happen, "sanity"); ++ assert_lock_strong(DumpTimeTable_lock); ++ Arguments::assert_is_dumping_archive(); ++ DumpTimeSharedClassInfo* p = find_or_allocate_info_for_locked(k); ++ return (p == NULL) ? true : p->is_excluded(); ++} ++ ++class EstimateSizeForArchive : StackObj { ++ size_t _shared_class_info_size; ++ int _num_builtin_klasses; ++ int _num_unregistered_klasses; ++ ++public: ++ EstimateSizeForArchive() { ++ _shared_class_info_size = 0; ++ _num_builtin_klasses = 0; ++ _num_unregistered_klasses = 0; ++ } ++ ++ bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { ++ if (!info.is_excluded()) { ++ size_t byte_size = RunTimeSharedClassInfo::byte_size(info._klass, info.num_verifier_constraints(), info.num_loader_constraints()); ++ _shared_class_info_size += align_up(byte_size, KlassAlignmentInBytes); ++ } ++ return true; // keep on iterating ++ } ++ ++ size_t total() { ++ return _shared_class_info_size; ++ } ++}; ++ ++size_t SystemDictionaryShared::estimate_size_for_archive() { ++ EstimateSizeForArchive est; ++ _dumptime_table->iterate(&est); ++ size_t total_size = est.total() + ++ CompactHashtableWriter::estimate_size(_dumptime_table->count_of(true)) + ++ CompactHashtableWriter::estimate_size(_dumptime_table->count_of(false)); ++ total_size += CompactHashtableWriter::estimate_size(0); ++ return total_size; ++} ++ ++unsigned int SystemDictionaryShared::hash_for_shared_dictionary(address ptr) { ++ if (ArchiveBuilder::is_active()) { ++ uintx offset = ArchiveBuilder::current()->any_to_offset(ptr); ++ unsigned int hash = primitive_hash(offset); ++ DEBUG_ONLY({ ++ if (((const MetaspaceObj*)ptr)->is_shared()) { ++ assert(hash == SystemDictionaryShared::hash_for_shared_dictionary_quick(ptr), "must be"); ++ } ++ }); ++ return hash; ++ } else { ++ return SystemDictionaryShared::hash_for_shared_dictionary_quick(ptr); ++ } ++} ++ ++class CopySharedClassInfoToArchive : StackObj { ++ CompactHashtableWriter* _writer; ++ bool _is_builtin; ++ ArchiveBuilder *_builder; ++public: ++ CopySharedClassInfoToArchive(CompactHashtableWriter* writer, ++ bool is_builtin) ++ : _writer(writer), _is_builtin(is_builtin), _builder(ArchiveBuilder::current()) {} ++ ++ bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { ++ if (!info.is_excluded() && info.is_builtin() == _is_builtin) { ++ size_t byte_size = RunTimeSharedClassInfo::byte_size(info._klass, info.num_verifier_constraints(), info.num_loader_constraints()); ++ RunTimeSharedClassInfo* record; ++ record = (RunTimeSharedClassInfo*)ArchiveBuilder::ro_region_alloc(byte_size); ++ record->init(info); ++ ++ unsigned int hash; ++ Symbol* name = info._klass->name(); ++ hash = SystemDictionaryShared::hash_for_shared_dictionary((address)name); ++ u4 delta = _builder->buffer_to_offset_u4((address)record); ++ if (_is_builtin && info._klass->is_anonymous()) { ++ // skip ++ } else { ++ _writer->add(hash, delta); ++ } ++ if (TraceDynamicCDS) { ++ ResourceMark rm; ++ dynamic_cds_log->print_cr("%s dictionary: %s", (_is_builtin ? "builtin" : "unregistered"), info._klass->external_name()); ++ } ++ ++ // Save this for quick runtime lookup of InstanceKlass* -> RunTimeSharedClassInfo* ++ RunTimeSharedClassInfo::set_for(info._klass, record); ++ } ++ return true; // keep on iterating ++ } ++}; ++ ++void SystemDictionaryShared::write_dictionary(RunTimeSharedDictionary* dictionary, ++ bool is_builtin) { ++ CompactHashtableStats stats; ++ dictionary->reset(); ++ CompactHashtableWriter writer(_dumptime_table->count_of(is_builtin), &stats); ++ CopySharedClassInfoToArchive copy(&writer, is_builtin); ++ assert_lock_strong(DumpTimeTable_lock); ++ _dumptime_table->iterate(©); ++ writer.dump(dictionary, is_builtin ? "builtin dictionary" : "unregistered dictionary"); ++} ++ ++void SystemDictionaryShared::write_to_archive() { ++ write_dictionary(&_dynamic_builtin_dictionary, true); ++ write_dictionary(&_dynamic_unregistered_dictionary, false); ++} ++ ++void SystemDictionaryShared::serialize_dictionary_headers(SerializeClosure* soc) { ++ _dynamic_builtin_dictionary.serialize_header(soc); ++ _dynamic_unregistered_dictionary.serialize_header(soc); ++} ++ ++void SystemDictionaryShared::set_shared_class_misc_info(InstanceKlass* k, ClassFileStream* cfs) { ++ Arguments::assert_is_dumping_archive(); ++ assert(!is_builtin(k), "must be unregistered class"); ++ DumpTimeSharedClassInfo* info = find_or_allocate_info_for(k); ++ if (info != NULL) { ++ info->_clsfile_size = cfs->length(); ++ info->_clsfile_crc32 = ClassLoader::crc32(0, (const char*)cfs->buffer(), cfs->length()); ++ } ++} ++ ++// This function is called for loading only UNREGISTERED classes ++InstanceKlass* SystemDictionaryShared::lookup_from_stream(Symbol* class_name, ++ Handle class_loader, ++ Handle protection_domain, ++ const ClassFileStream* cfs, ++ TRAPS) { ++ if (!UseSharedSpaces) { ++ return NULL; ++ } ++ if (class_name == NULL) { // don't do this for hidden classes ++ return NULL; ++ } ++ if (SystemDictionary::is_builtin_loader(class_loader)) { ++ // Do nothing for the BUILTIN loaders. ++ return NULL; ++ } ++ ++ const RunTimeSharedClassInfo* record = find_record(&_dynamic_unregistered_dictionary, class_name); ++ if (record == NULL) { ++ return NULL; ++ } ++ ++ int clsfile_size = cfs->length(); ++ int clsfile_crc32 = ClassLoader::crc32(0, (const char*)cfs->buffer(), cfs->length()); ++ ++ if (!record->matches(clsfile_size, clsfile_crc32)) { ++ return NULL; ++ } ++ ++ return acquire_class_for_current_thread(record->_klass, class_loader, ++ protection_domain, cfs, ++ THREAD); ++} ++ ++const RunTimeSharedClassInfo* ++SystemDictionaryShared::find_record(RunTimeSharedDictionary* dynamic_dict, Symbol* name) { ++ if (!UseSharedSpaces || !name->is_shared()) { ++ // The names of all shared classes must also be a shared Symbol. ++ return NULL; ++ } ++ ++ unsigned int hash = SystemDictionaryShared::hash_for_shared_dictionary_quick(name); ++ const RunTimeSharedClassInfo* record = NULL; ++ // AppCDS only support builtin classloader, customer class loader is just in dynamic archive. ++ if (DynamicArchive::is_mapped()) { ++ record = dynamic_dict->lookup(name, hash, 0); ++ } ++ ++ return record; ++} ++ ++InstanceKlass* SystemDictionaryShared::acquire_class_for_current_thread( ++ InstanceKlass *ik, ++ Handle class_loader, ++ Handle protection_domain, ++ const ClassFileStream *cfs, ++ TRAPS) { ++ ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(class_loader()); ++ ++ { ++ MutexLocker mu(SharedDictionary_lock, THREAD); ++ if (ik->class_loader_data() != NULL) { ++ // ik is already loaded (by this loader or by a different loader) ++ // or ik is being loaded by a different thread (by this loader or by a different loader) ++ return NULL; ++ } ++ ++ // No other thread has acquired this yet, so give it to *this thread* ++ ik->set_class_loader_data(loader_data); ++ } ++ ++ // No longer holding SharedDictionary_lock ++ // No need to lock, as can be held only by a single thread. ++ loader_data->add_class(ik); ++ ++ // Load and check super/interfaces, restore unsharable info ++ instanceKlassHandle shared_klass = SystemDictionary::load_shared_class(ik, class_loader, protection_domain, THREAD); ++ if (shared_klass() == NULL || HAS_PENDING_EXCEPTION) { ++ // TODO: clean up so it can be used again ++ return NULL; ++ } ++ ++ return shared_klass(); ++} ++ ++InstanceKlass* SystemDictionaryShared::find_dynamic_builtin_class(Symbol* name) { ++ const RunTimeSharedClassInfo* record = find_record(&_dynamic_builtin_dictionary, name); ++ if (record != NULL) { ++ assert(!record->_klass->is_anonymous(), "hidden class cannot be looked up by name"); ++ assert(check_klass_alignment(record->_klass), "Address not aligned"); ++ return record->_klass; ++ } else { ++ return NULL; ++ } ++} +diff --git a/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp b/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp +index 1bd61b02..36423bee 100644 +--- a/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp ++++ b/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp +@@ -22,7 +22,6 @@ + * + */ + +- + #ifndef SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP + #define SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP + +@@ -30,13 +29,91 @@ + #include "classfile/systemDictionary.hpp" + #include "verifier.hpp" + ++/*=============================================================================== ++ ++ Handling of the classes in the AppCDS archive ++ ++ To ensure safety and to simplify the implementation, archived classes are ++ "segregated" into 2 types. The following rules describe how they ++ are stored and looked up. ++ ++[1] Category of archived classes ++ ++ There are 2 disjoint groups of classes stored in the AppCDS archive: ++ ++ BUILTIN: These classes may be defined ONLY by the BOOT/PLATFORM/APP ++ loaders. ++ ++ UNREGISTERED: These classes may be defined ONLY by a ClassLoader ++ instance that's not listed above (using fingerprint matching) ++ ++[2] How classes from different categories are specified in the classlist: ++ ++ Starting from JDK9, each class in the classlist may be specified with ++ these keywords: "id", "super", "interfaces", "loader" and "source". ++ ++ ++ BUILTIN Only the "id" keyword may be (optionally) specified. All other ++ keywords are forbidden. ++ ++ The named class is looked up from the jimage and from ++ Xbootclasspath/a and CLASSPATH. ++ ++ UNREGISTERED: The "id", "super", and "source" keywords must all be ++ specified. ++ ++ The "interfaces" keyword must be specified if the class implements ++ one or more local interfaces. The "interfaces" keyword must not be ++ specified if the class does not implement local interfaces. ++ ++ The named class is looked up from the location specified in the ++ "source" keyword. ++ ++ Example classlist: ++ ++ # BUILTIN ++ java/lang/Object id: 0 ++ java/lang/Cloneable id: 1 ++ java/lang/String ++ ++ # UNREGISTERED ++ Bar id: 3 super: 0 interfaces: 1 source: /foo.jar ++ ++ ++[3] Identifying the category of archived classes ++ ++ BUILTIN: (C->shared_classpath_index() >= 0) ++ UNREGISTERED: (C->shared_classpath_index() == UNREGISTERED_INDEX (-9999)) ++ ++[4] Lookup of archived classes at run time: ++ ++ (a) BUILTIN loaders: ++ ++ search _builtin_dictionary ++ ++ (b) UNREGISTERED loaders: ++ ++ search _unregistered_dictionary for an entry that matches the ++ (name, clsfile_len, clsfile_crc32). ++ ++===============================================================================*/ ++#define UNREGISTERED_INDEX -9999 ++ ++class DumpTimeSharedClassInfo; ++class RunTimeSharedClassInfo; ++class RunTimeSharedDictionary; ++ + class SystemDictionaryShared: public SystemDictionary { ++private: ++ static bool _dump_in_progress; ++ DEBUG_ONLY(static bool _no_class_loading_should_happen;) ++ + public: + static void initialize(TRAPS) {} + static instanceKlassHandle find_or_load_shared_class(Symbol* class_name, + Handle class_loader, + TRAPS) { +- if (UseAppCDS) { ++ if (UseSharedSpaces) { + instanceKlassHandle ik = load_shared_class(class_name, class_loader, CHECK_NULL); + if (!ik.is_null()) { + instanceKlassHandle nh = instanceKlassHandle(); // null Handle +@@ -48,7 +125,7 @@ public: + } + static void roots_oops_do(OopClosure* blk) {} + static void oops_do(OopClosure* f) {} +- ++ + static bool is_sharing_possible(ClassLoaderData* loader_data) { + oop class_loader = loader_data->class_loader(); + return (class_loader == NULL || +@@ -60,8 +137,43 @@ public: + static size_t dictionary_entry_size() { + return sizeof(DictionaryEntry); + } ++ + static void init_shared_dictionary_entry(Klass* k, DictionaryEntry* entry) {} + ++ static void init_dumptime_info(InstanceKlass* k) NOT_CDS_RETURN; ++ static void remove_dumptime_info(InstanceKlass* k) NOT_CDS_RETURN; ++ ++ static void start_dumping(); ++ ++ static DumpTimeSharedClassInfo* find_or_allocate_info_for(InstanceKlass* k); ++ ++ static DumpTimeSharedClassInfo* find_or_allocate_info_for_locked(InstanceKlass* k); ++ ++ static bool empty_dumptime_table(); ++ ++ static void check_excluded_classes(); ++ ++ static bool check_for_exclusion(InstanceKlass* k, DumpTimeSharedClassInfo* info); ++ ++ static bool has_been_redefined(InstanceKlass* k); ++ ++ static bool check_for_exclusion_impl(InstanceKlass* k); ++ ++ static bool warn_excluded(InstanceKlass* k, const char* reason); ++ ++ static bool is_jfr_event_class(InstanceKlass *k); ++ ++ static bool has_class_failed_verification(InstanceKlass* ik); ++ ++ static bool is_builtin(InstanceKlass* k) { ++ return (k->shared_classpath_index() != UNREGISTERED_INDEX); ++ } ++ ++ static void dumptime_classes_do(class MetaspaceClosure* it); ++ ++ static void replace_klass_in_constantPool(); ++ ++ static bool is_excluded_class(InstanceKlass* k); + // The (non-application) CDS implementation supports only classes in the boot + // class loader, which ensures that the verification dependencies are the same + // during archive creation time and runtime. Thus we can do the dependency checks +@@ -69,6 +181,7 @@ public: + static void add_verification_dependency(Klass* k, Symbol* accessor_clsname, + Symbol* target_clsname) {} + static void finalize_verification_dependencies() {} ++ static void set_class_has_failed_verification(InstanceKlass* ik); + static bool check_verification_dependencies(Klass* k, Handle class_loader, + Handle protection_domain, + char** message_buffer, TRAPS) { +@@ -81,6 +194,49 @@ public: + } + return true; + } ++ static size_t estimate_size_for_archive(); ++ static void write_to_archive(); ++ static void write_dictionary(RunTimeSharedDictionary* dictionary, bool is_builtin); ++ static void serialize_dictionary_headers(class SerializeClosure* soc); ++ static unsigned int hash_for_shared_dictionary(address ptr); ++ static void set_shared_class_misc_info(InstanceKlass* k, ClassFileStream* cfs); ++ static InstanceKlass* lookup_from_stream(Symbol* class_name, ++ Handle class_loader, ++ Handle protection_domain, ++ const ClassFileStream* cfs, ++ TRAPS); ++ ++ DEBUG_ONLY(static bool no_class_loading_should_happen() {return _no_class_loading_should_happen;}) ++ ++#ifdef ASSERT ++ class NoClassLoadingMark: public StackObj { ++ public: ++ NoClassLoadingMark() { ++ assert(!_no_class_loading_should_happen, "must not be nested"); ++ _no_class_loading_should_happen = true; ++ } ++ ~NoClassLoadingMark() { ++ _no_class_loading_should_happen = false; ++ } ++ }; ++#endif ++ ++ template ++ static unsigned int hash_for_shared_dictionary_quick(T* ptr) { ++ assert(((MetaspaceObj*)ptr)->is_shared(), "must be"); ++ assert(ptr > (T*)SharedBaseAddress, "must be"); ++ uintx offset = uintx(ptr) - uintx(SharedBaseAddress); ++ return primitive_hash(offset); ++ } ++ ++ static const RunTimeSharedClassInfo* find_record(RunTimeSharedDictionary* dynamic_dict, Symbol* name); ++ static InstanceKlass* acquire_class_for_current_thread(InstanceKlass *ik, ++ Handle class_loader, ++ Handle protection_domain, ++ const ClassFileStream *cfs, ++ TRAPS); ++ ++ static InstanceKlass* find_dynamic_builtin_class(Symbol* name); + }; + + #endif // SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP +diff --git a/hotspot/src/share/vm/memory/allocation.hpp b/hotspot/src/share/vm/memory/allocation.hpp +index aa8f02d09..4d324b442 100644 +--- a/hotspot/src/share/vm/memory/allocation.hpp ++++ b/hotspot/src/share/vm/memory/allocation.hpp +@@ -302,6 +302,11 @@ class MetaspaceObj { + Type type, Thread* thread) throw(); + // can't use TRAPS from this header file. + void operator delete(void* p) { ShouldNotCallThis(); } ++ ++ // Declare a *static* method with the same signature in any subclass of MetaspaceObj ++ // that should be read-only by default. See symbol.hpp for an example. This function ++ // is used by the templates in metaspaceClosure.hpp ++ static bool is_read_only_by_default() { return false; } + }; + + // Base class for classes that constitute name spaces. +@@ -728,6 +733,12 @@ class ArrayAllocator VALUE_OBJ_CLASS_SPEC { + bool _use_malloc; + size_t _size; + bool _free_in_destructor; ++ ++ static bool should_use_malloc(size_t size) { ++ return size < ArrayAllocatorMallocLimit; ++ } ++ ++ static char* allocate_inner(size_t& size, bool& use_malloc); + public: + ArrayAllocator(bool free_in_destructor = true) : + _addr(NULL), _use_malloc(false), _size(0), _free_in_destructor(free_in_destructor) { } +@@ -739,6 +750,7 @@ class ArrayAllocator VALUE_OBJ_CLASS_SPEC { + } + + E* allocate(size_t length); ++ E* reallocate(size_t new_length); + void free(); + }; + +diff --git a/hotspot/src/share/vm/memory/allocation.inline.hpp b/hotspot/src/share/vm/memory/allocation.inline.hpp +index 9f2e1655a..2e794a8b6 100644 +--- a/hotspot/src/share/vm/memory/allocation.inline.hpp ++++ b/hotspot/src/share/vm/memory/allocation.inline.hpp +@@ -151,35 +151,58 @@ template void CHeapObj::operator delete [](void* p){ + } + + template +-E* ArrayAllocator::allocate(size_t length) { +- assert(_addr == NULL, "Already in use"); ++char* ArrayAllocator::allocate_inner(size_t &size, bool &use_malloc) { ++ char* addr = NULL; + +- _size = sizeof(E) * length; +- _use_malloc = _size < ArrayAllocatorMallocLimit; +- +- if (_use_malloc) { +- _addr = AllocateHeap(_size, F); +- if (_addr == NULL && _size >= (size_t)os::vm_allocation_granularity()) { ++ if (use_malloc) { ++ addr = AllocateHeap(size, F); ++ if (addr == NULL && size >= (size_t)os::vm_allocation_granularity()) { + // malloc failed let's try with mmap instead +- _use_malloc = false; ++ use_malloc = false; + } else { +- return (E*)_addr; ++ return addr; + } + } + + int alignment = os::vm_allocation_granularity(); +- _size = align_size_up(_size, alignment); ++ size = align_size_up(size, alignment); + +- _addr = os::reserve_memory(_size, NULL, alignment, F); +- if (_addr == NULL) { +- vm_exit_out_of_memory(_size, OOM_MMAP_ERROR, "Allocator (reserve)"); ++ addr = os::reserve_memory(size, NULL, alignment, F); ++ if (addr == NULL) { ++ vm_exit_out_of_memory(size, OOM_MMAP_ERROR, "Allocator (reserve)"); + } + +- os::commit_memory_or_exit(_addr, _size, !ExecMem, "Allocator (commit)"); ++ os::commit_memory_or_exit(addr, size, !ExecMem, "Allocator (commit)"); ++ return addr; ++} ++ ++template ++E* ArrayAllocator::allocate(size_t length) { ++ assert(_addr == NULL, "Already in use"); + ++ _size = sizeof(E) * length; ++ ++ _use_malloc = should_use_malloc(_size); ++ _addr = allocate_inner(_size, _use_malloc); + return (E*)_addr; + } + ++template ++E* ArrayAllocator::reallocate(size_t new_length) { ++ size_t new_size = sizeof(E) * new_length; ++ bool use_malloc = should_use_malloc(new_size); ++ char* new_addr = allocate_inner(new_size, use_malloc); ++ ++ memcpy(new_addr, _addr, MIN2(new_size, _size)); ++ ++ free(); ++ _size = new_size; ++ _use_malloc = use_malloc; ++ _addr = new_addr; ++ return (E*)new_addr; ++} ++ ++ + template + void ArrayAllocator::free() { + if (_addr != NULL) { +diff --git a/hotspot/src/share/vm/memory/filemap.cpp b/hotspot/src/share/vm/memory/filemap.cpp +index 99b1f58d0..3f4106476 100644 +--- a/hotspot/src/share/vm/memory/filemap.cpp ++++ b/hotspot/src/share/vm/memory/filemap.cpp +@@ -24,6 +24,8 @@ + + #include "jvm.h" + #include "precompiled.hpp" ++#include "cds/archiveBuilder.hpp" ++#include "cds/dynamicArchive.hpp" + #include "classfile/classLoader.hpp" + #include "classfile/sharedClassUtil.hpp" + #include "classfile/symbolTable.hpp" +@@ -140,19 +142,33 @@ template static void get_header_version(char (&header_version) [N]) { + } + } + +-FileMapInfo::FileMapInfo() { +- assert(_current_info == NULL, "must be singleton"); // not thread safe +- _current_info = this; ++FileMapInfo::FileMapInfo(bool is_static) { + memset(this, 0, sizeof(FileMapInfo)); ++ _is_static = is_static; ++ ++ if (is_static) { ++ assert(_current_info == NULL, "must be singleton"); // not thread safe ++ _current_info = this; ++ _header = SharedClassUtil::allocate_file_map_header(); ++ } else { ++ assert(_dynamic_archive_info == NULL, "must be singleton"); // not thread safe ++ _dynamic_archive_info = this; ++ _header = SharedClassUtil::allocate_dynamic_archive_header(); ++ } ++ ++ _header->_version = _invalid_version; + _file_offset = 0; + _file_open = false; +- _header = SharedClassUtil::allocate_file_map_header(); +- _header->_version = _invalid_version; + } + + FileMapInfo::~FileMapInfo() { +- assert(_current_info == this, "must be singleton"); // not thread safe +- _current_info = NULL; ++ if (_is_static) { ++ assert(_current_info == this, "must be singleton"); // not thread safe ++ _current_info = NULL; ++ } else { ++ assert(_dynamic_archive_info == this, "must be singleton"); // not thread safe ++ _dynamic_archive_info = NULL; ++ } + } + + void FileMapInfo::populate_header(size_t alignment) { +@@ -163,14 +179,66 @@ size_t FileMapInfo::FileMapHeader::data_size() { + return SharedClassUtil::file_map_header_size() - sizeof(FileMapInfo::FileMapHeaderBase); + } + ++size_t FileMapInfo::DynamicArchiveHeader::data_size() { ++ return sizeof(FileMapInfo::DynamicArchiveHeader) - sizeof(FileMapInfo::FileMapHeaderBase); ++} ++ ++bool FileMapInfo::DynamicArchiveHeader::validate() { ++ if (_magic != CDS_DYNAMIC_ARCHIVE_MAGIC) { ++ FileMapInfo::fail_continue("The shared archive file has a bad magic number."); ++ return false; ++ } ++ if (VerifySharedSpaces && compute_crc() != _crc) { ++ fail_continue("Header checksum verification failed."); ++ return false; ++ } ++ if (_version != current_version()) { ++ FileMapInfo::fail_continue("The shared archive file is the wrong version."); ++ return false; ++ } ++ char header_version[JVM_IDENT_MAX]; ++ get_header_version(header_version); ++ if (strncmp(_jvm_ident, header_version, JVM_IDENT_MAX-1) != 0) { ++ if (TraceClassPaths) { ++ tty->print_cr("Expected: %s", header_version); ++ tty->print_cr("Actual: %s", _jvm_ident); ++ } ++ FileMapInfo::fail_continue("The shared archive file was created by a different" ++ " version or build of HotSpot"); ++ return false; ++ } ++ if (_obj_alignment != ObjectAlignmentInBytes) { ++ FileMapInfo::fail_continue("The shared archive file's ObjectAlignmentInBytes of %d" ++ " does not equal the current ObjectAlignmentInBytes of %d.", ++ _obj_alignment, ObjectAlignmentInBytes); ++ return false; ++ } ++ ++ // TODO: much more validate check ++ ++ return true; ++} ++ + void FileMapInfo::FileMapHeader::populate(FileMapInfo* mapinfo, size_t alignment) { +- _magic = 0xf00baba2; +- _version = _current_version; ++ if (DynamicDumpSharedSpaces) { ++ _magic = CDS_DYNAMIC_ARCHIVE_MAGIC; ++ } else { ++ _magic = CDS_ARCHIVE_MAGIC; ++ } ++ _version = current_version(); + _alignment = alignment; + _obj_alignment = ObjectAlignmentInBytes; +- _classpath_entry_table_size = mapinfo->_classpath_entry_table_size; +- _classpath_entry_table = mapinfo->_classpath_entry_table; +- _classpath_entry_size = mapinfo->_classpath_entry_size; ++ /* TODO ++ _compressed_oops = UseCompressedOops; ++ _compressed_class_ptrs = UseCompressedClassPointers; ++ _max_heap_size = MaxHeapSize; ++ _narrow_klass_shift = CompressedKlassPointers::shift(); ++ */ ++ if (!DynamicDumpSharedSpaces) { ++ _classpath_entry_table_size = mapinfo->_classpath_entry_table_size; ++ _classpath_entry_table = mapinfo->_classpath_entry_table; ++ _classpath_entry_size = mapinfo->_classpath_entry_size; ++ } + + // The following fields are for sanity checks for whether this archive + // will function correctly with this JVM and the bootclasspath it's +@@ -303,62 +371,174 @@ bool FileMapInfo::validate_classpath_entry_table() { + return true; + } + ++bool FileMapInfo::get_base_archive_name_from_header(const char* archive_name, ++ int* size, char** base_archive_name) { ++ int fd = os::open(archive_name, O_RDONLY | O_BINARY, 0); ++ if (fd < 0) { ++ *size = 0; ++ return false; ++ } + +-// Read the FileMapInfo information from the file. +- +-bool FileMapInfo::init_from_file(int fd) { +- size_t sz = _header->data_size(); +- char* addr = _header->data(); ++ // read the header as a dynamic archive header ++ DynamicArchiveHeader* dynamic_header = SharedClassUtil::allocate_dynamic_archive_header(); ++ size_t sz = dynamic_header->data_size(); ++ char* addr = dynamic_header->data(); + size_t n = os::read(fd, addr, (unsigned int)sz); + if (n != sz) { + fail_continue("Unable to read the file header."); ++ delete dynamic_header; ++ os::close(fd); + return false; + } +- if (_header->_version != current_version()) { +- fail_continue("The shared archive file has the wrong version."); ++ if (dynamic_header->magic() != CDS_DYNAMIC_ARCHIVE_MAGIC) { ++ // Not a dynamic header, no need to proceed further. ++ *size = 0; ++ delete dynamic_header; ++ os::close(fd); + return false; + } + +- size_t info_size = _header->_paths_misc_info_size; +- _paths_misc_info = NEW_C_HEAP_ARRAY_RETURN_NULL(char, info_size, mtClass); +- if (_paths_misc_info == NULL) { +- fail_continue("Unable to read the file header."); ++ // read the base archive name ++ size_t name_size = dynamic_header->base_archive_name_size(); ++ if (name_size == 0) { ++ delete dynamic_header; ++ os::close(fd); + return false; + } +- n = os::read(fd, _paths_misc_info, (unsigned int)info_size); +- if (n != info_size) { +- fail_continue("Unable to read the shared path info header."); +- FREE_C_HEAP_ARRAY(char, _paths_misc_info, mtClass); +- _paths_misc_info = NULL; ++ *base_archive_name = NEW_C_HEAP_ARRAY(char, name_size, mtInternal); ++ n = os::read(fd, *base_archive_name, (unsigned int)name_size); ++ if (n != name_size) { ++ fail_continue("Unable to read the base archive name from the header."); ++ FREE_C_HEAP_ARRAY(char, *base_archive_name, mtInternal); ++ *base_archive_name = NULL; ++ delete dynamic_header; ++ os::close(fd); + return false; + } + +- size_t len = lseek(fd, 0, SEEK_END); +- struct FileMapInfo::FileMapHeader::space_info* si = +- &_header->_space[MetaspaceShared::mc]; +- if (si->_file_offset >= len || len - si->_file_offset < si->_used) { +- fail_continue("The shared archive file has been truncated."); ++ delete dynamic_header; ++ os::close(fd); ++ return true; ++} ++ ++bool FileMapInfo::check_archive(const char* archive_name, bool is_static) { ++ int fd = os::open(archive_name, O_RDONLY | O_BINARY, 0); ++ if (fd < 0) { ++ // do not vm_exit_during_initialization here because Arguments::init_shared_archive_paths() ++ // requires a shared archive name. The open_for_read() function will log a message regarding ++ // failure in opening a shared archive. + return false; + } + +- _file_offset += (long)n; ++ FileMapHeader* header = NULL; ++ if (is_static) { ++ header = SharedClassUtil::allocate_file_map_header(); ++ } else { ++ header = SharedClassUtil::allocate_dynamic_archive_header(); ++ } ++ ++ size_t sz = header->data_size(); ++ size_t n = os::read(fd, header->data(), (unsigned int)sz); ++ if (n != sz) { ++ delete header; ++ os::close(fd); ++ vm_exit_during_initialization("Unable to read header from shared archive", archive_name); ++ return false; ++ } ++ if (is_static) { ++ FileMapHeader* static_header = (FileMapHeader*)header; ++ if (static_header->magic() != CDS_ARCHIVE_MAGIC) { ++ delete header; ++ os::close(fd); ++ vm_exit_during_initialization("Not a base shared archive", archive_name); ++ return false; ++ } ++ } else { ++ DynamicArchiveHeader* dynamic_header = (DynamicArchiveHeader*)header; ++ if (dynamic_header->magic() != CDS_DYNAMIC_ARCHIVE_MAGIC) { ++ delete header; ++ os::close(fd); ++ vm_exit_during_initialization("Not a top shared archive", archive_name); ++ return false; ++ } ++ } ++ delete header; ++ os::close(fd); ++ return true; ++} ++ ++// Read the FileMapInfo information from the file. ++ ++bool FileMapInfo::init_from_file(int fd) { ++ size_t sz = header()->data_size(); ++ char* addr = header()->data(); ++ size_t n = os::read(fd, addr, (unsigned int)sz); ++ if (n != sz) { ++ fail_continue("Unable to read the file header."); ++ return false; ++ } ++ ++ _file_offset += n; ++ ++ if (is_static()) { ++ size_t info_size = _header->_paths_misc_info_size; ++ _paths_misc_info = NEW_C_HEAP_ARRAY_RETURN_NULL(char, info_size, mtClass); ++ if (_paths_misc_info == NULL) { ++ fail_continue("Unable to read the file header."); ++ return false; ++ } ++ n = os::read(fd, _paths_misc_info, (unsigned int)info_size); ++ if (n != info_size) { ++ fail_continue("Unable to read the shared path info header."); ++ FREE_C_HEAP_ARRAY(char, _paths_misc_info, mtClass); ++ _paths_misc_info = NULL; ++ return false; ++ } ++ ++ // just checking the last region is sufficient since the archive is written ++ // in sequential order ++ size_t len = lseek(fd, 0, SEEK_END); ++ struct FileMapInfo::FileMapHeader::space_info* si = ++ &_header->_space[MetaspaceShared::mc]; ++ if (si->_file_offset >= len || len - si->_file_offset < si->_used) { ++ fail_continue("The shared archive file has been truncated."); ++ return false; ++ } ++ ++ _file_offset += n; ++ } else { ++ _file_offset += dynamic_header()->base_archive_name_size(); // accounts for the size of _base_archive_name ++ } ++ + return true; + } + + + // Read the FileMapInfo information from the file. + bool FileMapInfo::open_for_read() { +- _full_path = make_log_name(Arguments::GetSharedArchivePath(), NULL); +- int fd = open(_full_path, O_RDONLY | O_BINARY, 0); ++ if (_file_open) { ++ return true; ++ } ++ if (is_static()) { ++ _full_path = Arguments::GetSharedArchivePath(); ++ } else { ++ _full_path = Arguments::GetSharedDynamicArchivePath(); ++ } ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("trying to map %s", _full_path); ++ } ++ int fd = os::open(_full_path, O_RDONLY | O_BINARY, 0); + if (fd < 0) { + if (errno == ENOENT) { +- // Not locating the shared archive is ok. +- fail_continue("Specified shared archive not found. archive file path:%s", _full_path); ++ fail_continue("Specified shared archive not found (%s).", _full_path); + } else { +- fail_continue("Failed to open shared archive file (%s).", +- strerror(errno)); ++ fail_continue("Failed to open shared archive file (%s).", strerror(errno)); + } + return false; ++ } else { ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Opened archive %s.", _full_path); ++ } + } + + _fd = fd; +@@ -368,7 +548,7 @@ bool FileMapInfo::open_for_read() { + + // Write the FileMapInfo information to the file. + void FileMapInfo::open_for_write() { +- if (UseAppCDS && AppCDSLockFile != NULL) { ++ if ((DynamicDumpSharedSpaces || UseAppCDS) && AppCDSLockFile != NULL) { + char* pos = strrchr(const_cast(AppCDSLockFile), '/'); + #ifdef __linux__ + if (pos != NULL && pos != AppCDSLockFile) { // No directory path specified +@@ -391,14 +571,18 @@ void FileMapInfo::open_for_write() { + int lock_fd = open(_appcds_file_lock_path, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR); + if (lock_fd < 0) { + tty->print_cr("Failed to create jsa file !\n Please check: \n 1. The directory exists.\n " +- "2. You have the permission.\n 3. Make sure no other process using the same lock file.\n"); ++ "2. You have the permission.\n 3. Make sure no other process using the same lock file.\n"); + fail_stop("Failed to create appcds lock file, the lock path is: %s.", _appcds_file_lock_path); + } + tty->print_cr("You are using file lock %s in concurrent mode", AppCDSLockFile); + } + #endif + } +- _full_path = make_log_name(Arguments::GetSharedArchivePath(), NULL); ++ if (is_static()) { ++ _full_path = make_log_name(Arguments::GetSharedArchivePath(), NULL); ++ } else { ++ _full_path = make_log_name(Arguments::GetSharedDynamicArchivePath(), NULL); ++ } + if (PrintSharedSpaces) { + tty->print_cr("Dumping shared data to file: "); + tty->print_cr(" %s", _full_path); +@@ -436,6 +620,18 @@ void FileMapInfo::write_header() { + align_file_position(); + } + ++void FileMapInfo::write_dynamic_header() { ++ align_file_position(); ++ size_t sz = _header->data_size(); ++ char* addr = _header->data(); ++ write_bytes(addr, (int)sz); // skip the C++ vtable ++ ++ char* base_archive_name = (char*)Arguments::GetSharedArchivePath(); ++ if (base_archive_name != NULL) { ++ write_bytes(base_archive_name, dynamic_header()->base_archive_name_size()); ++ } ++ align_file_position(); ++} + + // Dump shared spaces to file. + +@@ -464,7 +660,15 @@ void FileMapInfo::write_region(int region, char* base, size_t size, + } else { + si->_file_offset = _file_offset; + } +- si->_base = base; ++ if (is_static()) { ++ si->_base = base; ++ } else { ++ if (region == MetaspaceShared::d_bm) { ++ si->_base = NULL; // always NULL for bm region ++ } else { ++ si->_base = ArchiveBuilder::current()->to_requested(base); ++ } ++ } + si->_used = size; + si->_capacity = capacity; + si->_read_only = read_only; +@@ -473,7 +677,16 @@ void FileMapInfo::write_region(int region, char* base, size_t size, + write_bytes_aligned(base, (int)size); + } + ++char* FileMapInfo::write_bitmap_region(const BitMap* ptrmap) { ++ size_t size_in_bits = ptrmap->size(); ++ size_t size_in_bytes = ptrmap->size_in_words() * BytesPerWord; ++ char* buffer = NEW_C_HEAP_ARRAY(char, size_in_bytes, mtClassShared); ++ ptrmap->write_to((BitMap::bm_word_t*)buffer, size_in_bytes); ++ dynamic_header()->set_ptrmap_size_in_bits(size_in_bits); + ++ write_region(MetaspaceShared::d_bm, (char*)buffer, size_in_bytes, size_in_bytes, /*read_only=*/true, /*allow_exec=*/false); ++ return buffer; ++} + // Dump bytes to file -- at the current file position. + + void FileMapInfo::write_bytes(const void* buffer, int nbytes) { +@@ -542,7 +755,7 @@ void FileMapInfo::close() { + // JVM/TI RedefineClasses() support: + // Remap the shared readonly space to shared readwrite, private. + bool FileMapInfo::remap_shared_readonly_as_readwrite() { +- struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[0]; ++ struct FileMapInfo::FileMapHeader::space_info* si = is_static() ? &_header->_space[0] : &_header->_space[1]; + if (!si->_read_only) { + // the space is already readwrite so we are done + return true; +@@ -570,10 +783,14 @@ bool FileMapInfo::remap_shared_readonly_as_readwrite() { + + // Map the whole region at once, assumed to be allocated contiguously. + ReservedSpace FileMapInfo::reserve_shared_memory() { +- struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[0]; +- char* requested_addr = si->_base; ++ char* requested_addr = region_base(0); ++ size_t size = 0; + +- size_t size = FileMapInfo::shared_spaces_size(); ++ if (is_static()) { ++ size = FileMapInfo::shared_spaces_size(); ++ } else { ++ size = align_up((uintptr_t)region_end(1) - (uintptr_t)region_base(0), (size_t)os::vm_allocation_granularity()); ++ } + + // Reserve the space first, then map otherwise map will go right over some + // other reserved memory (like the code cache). +@@ -648,6 +865,7 @@ void FileMapInfo::assert_mark(bool check) { + + + FileMapInfo* FileMapInfo::_current_info = NULL; ++FileMapInfo* FileMapInfo::_dynamic_archive_info = NULL; + SharedClassPathEntry* FileMapInfo::_classpath_entry_table = NULL; + int FileMapInfo::_classpath_entry_table_size = 0; + size_t FileMapInfo::_classpath_entry_size = 0x1234baad; +@@ -674,19 +892,26 @@ bool FileMapInfo::initialize() { + if (!open_for_read()) { + return false; + } +- +- init_from_file(_fd); ++ if (!init_from_file(_fd)) { ++ return false; ++ } + if (!validate_header()) { + return false; + } + +- SharedReadOnlySize = _header->_space[0]._capacity; +- SharedReadWriteSize = _header->_space[1]._capacity; +- SharedMiscDataSize = _header->_space[2]._capacity; +- SharedMiscCodeSize = _header->_space[3]._capacity; ++ if (is_static()) { ++ SharedReadOnlySize = _header->_space[0]._capacity; ++ SharedReadWriteSize = _header->_space[1]._capacity; ++ SharedMiscDataSize = _header->_space[2]._capacity; ++ SharedMiscCodeSize = _header->_space[3]._capacity; ++ } + return true; + } + ++void FileMapInfo::DynamicArchiveHeader::set_as_offset(char* p, size_t *offset) { ++ *offset = ArchiveBuilder::current()->any_to_offset((address)p); ++} ++ + int FileMapInfo::FileMapHeader::compute_crc() { + char* header = data(); + // start computing from the field after _crc +@@ -701,7 +926,7 @@ int FileMapInfo::compute_header_crc() { + } + + bool FileMapInfo::FileMapHeader::validate() { +- if (_magic != (int)0xf00baba2) { ++ if (_magic != CDS_ARCHIVE_MAGIC) { + FileMapInfo::fail_continue("The shared archive file has a bad magic number."); + return false; + } +@@ -738,6 +963,10 @@ bool FileMapInfo::FileMapHeader::validate() { + bool FileMapInfo::validate_header() { + bool status = _header->validate(); + ++ if (status && !is_static()) { ++ return DynamicArchive::validate(this); ++ } ++ + if (status) { + if (!ClassLoader::check_shared_paths_misc_info(_paths_misc_info, _header->_paths_misc_info_size)) { + if (!PrintSharedArchiveAndExit) { +@@ -761,7 +990,13 @@ bool FileMapInfo::validate_header() { + // Return: + // True if the p is within the mapped shared space, otherwise, false. + bool FileMapInfo::is_in_shared_space(const void* p) { +- for (int i = 0; i < MetaspaceShared::n_regions; i++) { ++ int count = 0; ++ if (is_static()) { ++ count = MetaspaceShared::n_regions; ++ } else { ++ count = MetaspaceShared::d_n_regions; ++ } ++ for (int i = 0; i < count; i++) { + if (p >= _header->_space[i]._base && + p < _header->_space[i]._base + _header->_space[i]._used) { + return true; +@@ -772,6 +1007,11 @@ bool FileMapInfo::is_in_shared_space(const void* p) { + } + + void FileMapInfo::print_shared_spaces() { ++ // TODO: support dynamic archive ++ if (!is_static()) { ++ return; ++ } ++ + gclog_or_tty->print_cr("Shared Spaces:"); + for (int i = 0; i < MetaspaceShared::n_regions; i++) { + struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[i]; +diff --git a/hotspot/src/share/vm/memory/filemap.hpp b/hotspot/src/share/vm/memory/filemap.hpp +index 0eee1c7ea..eab9ebcfc 100644 +--- a/hotspot/src/share/vm/memory/filemap.hpp ++++ b/hotspot/src/share/vm/memory/filemap.hpp +@@ -27,6 +27,8 @@ + + #include "memory/metaspaceShared.hpp" + #include "memory/metaspace.hpp" ++#include "runtime/os.hpp" ++#include "utilities/align.hpp" + + // Layout of the file: + // header: dump of archive instance plus versioning info, datestamp, etc. +@@ -37,8 +39,12 @@ + // misc data (block offset table, string table, symbols, dictionary, etc.) + // tag(666) + ++#define CDS_ARCHIVE_MAGIC 0xf00baba2 ++#define CDS_DYNAMIC_ARCHIVE_MAGIC 0xf00baba8 ++ + static const int JVM_IDENT_MAX = 256; + ++class BitMap; + class Metaspace; + + class SharedClassPathEntry VALUE_OBJ_CLASS_SPEC { +@@ -56,11 +62,13 @@ private: + friend class ManifestStream; + enum { + _invalid_version = -1, +- _current_version = 2 ++ _current_version = 3, + }; + +- bool _file_open; +- int _fd; ++ bool _is_static; ++ bool _file_open; ++ bool _is_mapped; ++ int _fd; + size_t _file_offset; + + private: +@@ -77,20 +85,21 @@ public: + struct FileMapHeaderBase : public CHeapObj { + virtual bool validate() = 0; + virtual void populate(FileMapInfo* info, size_t alignment) = 0; +- }; +- struct FileMapHeader : FileMapHeaderBase { + // Use data() and data_size() to memcopy to/from the FileMapHeader. We need to + // avoid read/writing the C++ vtable pointer. +- static size_t data_size(); ++ virtual size_t data_size() = 0; ++ }; ++ struct FileMapHeader : FileMapHeaderBase { ++ size_t data_size(); + char* data() { + return ((char*)this) + sizeof(FileMapHeaderBase); + } + +- int _magic; // identify file type. +- int _crc; // header crc checksum. +- int _version; // (from enum, above.) +- size_t _alignment; // how shared archive should be aligned +- int _obj_alignment; // value of ObjectAlignmentInBytes ++ unsigned int _magic; // identify file type. ++ int _crc; // header crc checksum. ++ int _version; // (from enum, above.) ++ size_t _alignment; // how shared archive should be aligned ++ int _obj_alignment; // value of ObjectAlignmentInBytes + + struct space_info { + int _crc; // crc checksum of the current space +@@ -137,7 +146,48 @@ public: + + virtual bool validate(); + virtual void populate(FileMapInfo* info, size_t alignment); ++ int crc() { return _crc; } ++ int space_crc(int i) { return _space[i]._crc; } + int compute_crc(); ++ unsigned int magic() const { return _magic; } ++ const char* jvm_ident() const { return _jvm_ident; } ++ }; ++ ++ // Fixme ++ struct DynamicArchiveHeader : FileMapHeader { ++ private: ++ int _base_header_crc; ++ int _base_region_crc[MetaspaceShared::n_regions]; ++ char* _requested_base_address; // Archive relocation is not necessary if we map with this base address. ++ size_t _ptrmap_size_in_bits; // Size of pointer relocation bitmap ++ size_t _base_archive_name_size; ++ size_t _serialized_data_offset; // Data accessed using {ReadClosure,WriteClosure}::serialize() ++ ++ public: ++ size_t data_size(); ++ int base_header_crc() const { return _base_header_crc; } ++ int base_region_crc(int i) const { ++ return _base_region_crc[i]; ++ } ++ ++ void set_base_header_crc(int c) { _base_header_crc = c; } ++ void set_base_region_crc(int i, int c) { ++ _base_region_crc[i] = c; ++ } ++ ++ void set_requested_base(char* b) { ++ _requested_base_address = b; ++ } ++ size_t ptrmap_size_in_bits() const { return _ptrmap_size_in_bits; } ++ void set_ptrmap_size_in_bits(size_t s) { _ptrmap_size_in_bits = s; } ++ void set_base_archive_name_size(size_t s) { _base_archive_name_size = s; } ++ size_t base_archive_name_size() { return _base_archive_name_size; } ++ void set_as_offset(char* p, size_t *offset); ++ char* from_mapped_offset(size_t offset) const { return _requested_base_address + offset; } ++ void set_serialized_data(char* p) { set_as_offset(p, &_serialized_data_offset); } ++ char* serialized_data() const { return from_mapped_offset(_serialized_data_offset); } ++ ++ virtual bool validate(); + }; + + FileMapHeader * _header; +@@ -147,32 +197,52 @@ public: + char* _paths_misc_info; + + static FileMapInfo* _current_info; ++ static FileMapInfo* _dynamic_archive_info; + ++ static bool get_base_archive_name_from_header(const char* archive_name, ++ int* size, char** base_archive_name); ++ static bool check_archive(const char* archive_name, bool is_static); + bool init_from_file(int fd); + void align_file_position(); + bool validate_header_impl(); + + public: +- FileMapInfo(); ++ FileMapInfo(bool is_static = true); + ~FileMapInfo(); + + static int current_version() { return _current_version; } + int compute_header_crc(); + void set_header_crc(int crc) { _header->_crc = crc; } ++ int space_crc(int i) { return _header->_space[i]._crc; } + void populate_header(size_t alignment); + bool validate_header(); + void invalidate(); ++ int crc() { return _header->_crc; } + int version() { return _header->_version; } + size_t alignment() { return _header->_alignment; } + size_t space_capacity(int i) { return _header->_space[i]._capacity; } ++ size_t used(int i) { return _header->_space[i]._used; } ++ size_t used_aligned(int i) { return align_up(used(i), (size_t)os::vm_allocation_granularity()); } + char* region_base(int i) { return _header->_space[i]._base; } ++ char* region_end(int i) { return region_base(i) + used_aligned(i); } + struct FileMapHeader* header() { return _header; } ++ struct DynamicArchiveHeader* dynamic_header() { ++ // assert(!is_static(), "must be"); ++ return (struct DynamicArchiveHeader*)header(); ++ } ++ ++ void set_header_base_archive_name_size(size_t size) { dynamic_header()->set_base_archive_name_size(size); } + + static FileMapInfo* current_info() { + CDS_ONLY(return _current_info;) + NOT_CDS(return NULL;) + } + ++ static FileMapInfo* dynamic_info() { ++ CDS_ONLY(return _dynamic_archive_info;) ++ NOT_CDS(return NULL;) ++ } ++ + static void assert_mark(bool check); + + // File manipulation. +@@ -180,18 +250,24 @@ public: + bool open_for_read(); + void open_for_write(); + void write_header(); ++ void write_dynamic_header(); + void write_space(int i, Metaspace* space, bool read_only); + void write_region(int region, char* base, size_t size, + size_t capacity, bool read_only, bool allow_exec); ++ char* write_bitmap_region(const BitMap* ptrmap); + void write_bytes(const void* buffer, int count); + void write_bytes_aligned(const void* buffer, int count); + char* map_region(int i); + void unmap_region(int i); + bool verify_region_checksum(int i); + void close(); +- bool is_open() { return _file_open; } ++ bool is_open() { return _file_open; } ++ bool is_static() const { return _is_static; } ++ bool is_mapped() const { return _is_mapped; } ++ void set_is_mapped(bool v) { _is_mapped = v; } + ReservedSpace reserve_shared_memory(); +- ++ void set_requested_base(char* b) { dynamic_header()->set_requested_base(b); } ++ char* serialized_data() { return dynamic_header()->serialized_data(); } + // JVM/TI RedefineClasses() support: + // Remap the shared readonly space to shared readwrite, private. + bool remap_shared_readonly_as_readwrite(); +diff --git a/hotspot/src/share/vm/memory/iterator.hpp b/hotspot/src/share/vm/memory/iterator.hpp +index 62204eea7..dc01186a2 100644 +--- a/hotspot/src/share/vm/memory/iterator.hpp ++++ b/hotspot/src/share/vm/memory/iterator.hpp +@@ -378,6 +378,13 @@ public: + // for verification that sections of the serialized data are of the + // correct length. + virtual void do_tag(int tag) = 0; ++ ++ // Read/write the 32-bit unsigned integer pointed to by p. ++ virtual void do_u4(u4* p) { } ++ ++ bool writing() { ++ return !reading(); ++ } + }; + + class SymbolClosure : public StackObj { +diff --git a/hotspot/src/share/vm/memory/metaspace.cpp b/hotspot/src/share/vm/memory/metaspace.cpp +index 2912f41b6..7e95b5c0b 100644 +--- a/hotspot/src/share/vm/memory/metaspace.cpp ++++ b/hotspot/src/share/vm/memory/metaspace.cpp +@@ -37,6 +37,7 @@ + #include "memory/metaspaceTracer.hpp" + #include "memory/resourceArea.hpp" + #include "memory/universe.hpp" ++#include "runtime/arguments.hpp" + #include "runtime/atomic.inline.hpp" + #include "runtime/globals.hpp" + #include "runtime/init.hpp" +@@ -426,8 +427,16 @@ VirtualSpaceNode::VirtualSpaceNode(size_t bytes) : _top(NULL), _next(NULL), _rs( + assert(shared_base == 0 || _rs.base() == shared_base, "should match"); + } else { + // Get a mmap region anywhere if the SharedBaseAddress fails. ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Could not allocate static space at request address: " INTPTR_FORMAT, p2i(shared_base)); ++ } + _rs = ReservedSpace(bytes, Metaspace::reserve_alignment(), large_pages); + } ++ // ...failing that, give up. ++ if (!_rs.is_reserved()) { ++ vm_exit_during_initialization( ++ err_msg("Could not allocate static shared space: " SIZE_FORMAT " bytes", bytes)); ++ } + MetaspaceShared::set_shared_rs(&_rs); + } else + #endif +@@ -3322,21 +3331,80 @@ void Metaspace::global_initialize() { + // the addresses don't conflict) + address cds_address = NULL; + if (UseSharedSpaces) { +- FileMapInfo* mapinfo = new FileMapInfo(); ++ FileMapInfo* static_mapinfo = new FileMapInfo(); ++ FileMapInfo* dynamic_mapinfo = new FileMapInfo(false); + + // Open the shared archive file, read and validate the header. If + // initialization fails, shared spaces [UseSharedSpaces] are + // disabled and the file is closed. +- // Map in spaces now also +- if (mapinfo->initialize() && MetaspaceShared::map_shared_spaces(mapinfo)) { ++ // ++ // This will reserve two address spaces suitable to house Klass structures, one ++ // for the cds archives (static archive and optionally dynamic archive) and ++ // optionally one move for ccs. ++ // ++ // Since both spaces must fall within the compressed class pointer encoding ++ // range, they are allocated close to each other. ++ // ++ // Space for archives will be reserved first, followed by a potential gap, ++ // followed by the space for ccs: ++ // ++ // +-- Base address End ++ // | | ++ // v v ++ // +------------+ +-------------+ +--------------------+ ++ // | static arc | [align] | [dyn. arch] | [align] | compr. class space | ++ // +------------+ +-------------+ +--------------------+ ++ // ++ // (The gap may result from different alignment requirements between metaspace ++ // and CDS) ++ // ++ // If UseCompressedClassPointers is disabled, only one address space will be ++ // reserved: ++ // ++ // +-- Base address End ++ // | | ++ // v v ++ // +------------+ +-------------+ ++ // | static arc | [align] | [dyn. arch] | ++ // +------------+ +-------------+ ++ // ++ // If UseCompressedClassPointers=1, the range encompassing both spaces will be ++ // suitable to en/decode narrow Klass pointers: the base will be valid for ++ // encoding, the range [Base, End) not surpass KlassEncodingMetaspaceMax. ++ if (static_mapinfo->initialize() && MetaspaceShared::map_shared_spaces(static_mapinfo)) { + cds_total = FileMapInfo::shared_spaces_size(); +- cds_address = (address)mapinfo->region_base(0); ++ cds_address = (address)static_mapinfo->region_base(0); ++ MetaspaceShared::set_shared_metaspace_static_bottom(cds_address); ++ // Update SharedBaseAddress to the same value as the dump phase. ++ SharedBaseAddress = (size_t)cds_address; ++ if (!DynamicDumpSharedSpaces && ++ (Arguments::GetSharedDynamicArchivePath() != NULL) && ++ dynamic_mapinfo->initialize() && ++ MetaspaceShared::map_shared_spaces(dynamic_mapinfo)) { ++ cds_total += align_up(dynamic_mapinfo->region_end(1) - dynamic_mapinfo->region_base(0), ++ (size_t)os::vm_allocation_granularity()); ++ } else { ++ assert(!dynamic_mapinfo->is_open(), ++ "dynamic archive file not closed or shared spaces not disabled."); ++ } + } else { +- assert(!mapinfo->is_open() && !UseSharedSpaces, +- "archive file not closed or shared spaces not disabled."); ++ assert(!static_mapinfo->is_open() && !UseSharedSpaces, ++ "static archive file not closed or shared spaces not disabled."); ++ } ++ ++ if (static_mapinfo != NULL && !static_mapinfo->is_mapped()) { ++ delete static_mapinfo; ++ } ++ if (dynamic_mapinfo != NULL && !dynamic_mapinfo->is_mapped()) { ++ delete dynamic_mapinfo; + } + } ++ ++ if (DynamicDumpSharedSpaces && !UseSharedSpaces) { ++ vm_exit_during_initialization("DynamicDumpSharedSpaces is unsupported when base CDS archive is not loaded", NULL); ++ } + #endif // INCLUDE_CDS ++ + #ifdef _LP64 + // If UseCompressedClassPointers is set then allocate the metaspace area + // above the heap and above the CDS area (if it exists). +diff --git a/hotspot/src/share/vm/memory/metaspace.hpp b/hotspot/src/share/vm/memory/metaspace.hpp +index 3920004a8..2b06cb620 100644 +--- a/hotspot/src/share/vm/memory/metaspace.hpp ++++ b/hotspot/src/share/vm/memory/metaspace.hpp +@@ -82,6 +82,7 @@ class VirtualSpaceList; + // quantum of metadata. + + class Metaspace : public CHeapObj { ++ friend class ArchiveBuilder; + friend class VMStructs; + friend class SpaceManager; + friend class VM_CollectForMetadataAllocation; +diff --git a/hotspot/src/share/vm/memory/metaspaceClosure.cpp b/hotspot/src/share/vm/memory/metaspaceClosure.cpp +new file mode 100644 +index 000000000..00ec8fced +--- /dev/null ++++ b/hotspot/src/share/vm/memory/metaspaceClosure.cpp +@@ -0,0 +1,87 @@ ++#include "precompiled.hpp" ++#include "memory/metaspaceClosure.hpp" ++ ++// Update the reference to point to new_loc. ++void MetaspaceClosure::Ref::update(address new_loc) const { ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Ref: [" PTR_FORMAT "] -> " PTR_FORMAT " => " PTR_FORMAT, ++ p2i(mpp()), p2i(obj()), p2i(new_loc)); ++ } ++ uintx p = (uintx)new_loc; ++ p |= flag_bits(); // Make sure the flag bits are copied to the new pointer. ++ *(address*)mpp() = (address)p; ++} ++ ++void MetaspaceClosure::push_impl(MetaspaceClosure::Ref* ref) { ++ if (_nest_level < MAX_NEST_LEVEL) { ++ do_push(ref); ++ if (!ref->keep_after_pushing()) { ++ delete ref; ++ } ++ } else { ++ do_pending_ref(ref); ++ ref->set_next(_pending_refs); ++ _pending_refs = ref; ++ } ++} ++ ++void MetaspaceClosure::do_push(MetaspaceClosure::Ref* ref) { ++ if (ref->not_null()) { ++ bool read_only; ++ Writability w = ref->writability(); ++ switch (w) { ++ case _writable: ++ read_only = false; ++ break; ++ case _not_writable: ++ read_only = true; ++ break; ++ default: ++ assert(w == _default, "must be"); ++ read_only = ref->is_read_only_by_default(); ++ } ++ if (_nest_level == 0) { ++ assert(_enclosing_ref == NULL, "must be"); ++ } ++ _nest_level ++; ++ if (do_ref(ref, read_only)) { // true means we want to iterate the embedded pointer in ++ Ref* saved = _enclosing_ref; ++ _enclosing_ref = ref; ++ ref->metaspace_pointers_do(this); ++ _enclosing_ref = saved; ++ } ++ _nest_level --; ++ } ++} ++ ++void MetaspaceClosure::finish() { ++ assert(_nest_level == 0, "must be"); ++ while (_pending_refs != NULL) { ++ Ref* ref = _pending_refs; ++ _pending_refs = _pending_refs->next(); ++ do_push(ref); ++ if (!ref->keep_after_pushing()) { ++ delete ref; ++ } ++ } ++} ++ ++MetaspaceClosure::~MetaspaceClosure() { ++ assert(_pending_refs == NULL, ++ "you must explicitly call MetaspaceClosure::finish() to process all refs!"); ++} ++ ++bool UniqueMetaspaceClosure::do_ref(MetaspaceClosure::Ref* ref, bool read_only) { ++ bool created; ++ _has_been_visited.add_if_absent(ref->obj(), read_only, &created); ++ if (!created) { ++ return false; // Already visited: no need to iterate embedded pointers. ++ } else { ++ if (_has_been_visited.maybe_grow(MAX_TABLE_SIZE)) { ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Expanded _has_been_visited table to %d", _has_been_visited.table_size()); ++ } ++ } ++ return do_unique_ref(ref, read_only); ++ } ++} +diff --git a/hotspot/src/share/vm/memory/metaspaceClosure.hpp b/hotspot/src/share/vm/memory/metaspaceClosure.hpp +new file mode 100644 +index 000000000..f67d8d6fd +--- /dev/null ++++ b/hotspot/src/share/vm/memory/metaspaceClosure.hpp +@@ -0,0 +1,381 @@ ++ ++ ++#ifndef SHARE_VM_MEMORY_METASPACECLOSURE_HPP ++#define SHARE_VM_MEMORY_METASPACECLOSURE_HPP ++ ++#include "memory/allocation.hpp" ++#include "utilities/array.hpp" ++#include "utilities/globalDefinitions.hpp" ++#include "utilities/hashtable.hpp" ++ ++// The metadata hierarchy is separate from the oop hierarchy ++ class MetaspaceObj; // no C++ vtable ++//class Array; // no C++ vtable ++ class Annotations; // no C++ vtable ++ class ConstantPoolCache; // no C++ vtable ++ class ConstMethod; // no C++ vtable ++ class MethodCounters; // no C++ vtable ++ class Symbol; // no C++ vtable ++ class Metadata; // has C++ vtable (so do all subclasses) ++ class ConstantPool; ++ class MethodData; ++ class Method; ++ class Klass; ++ class InstanceKlass; ++ class InstanceMirrorKlass; ++ class InstanceClassLoaderKlass; ++ class InstanceRefKlass; ++ class ArrayKlass; ++ class ObjArrayKlass; ++ class TypeArrayKlass; ++ ++// class MetaspaceClosure -- ++// ++// This class is used for iterating the objects in the HotSpot Metaspaces. It ++// provides an API to walk all the reachable objects starting from a set of ++// root references (such as all Klass'es in the SystemDictionary). ++// ++// Currently it is used for compacting the CDS archive by eliminate temporary ++// objects allocated during archive creation time. See ArchiveBuilder for an example. ++// ++// To support MetaspaceClosure, each subclass of MetaspaceObj must provide ++// a method of the type void metaspace_pointers_do(MetaspaceClosure*). This method ++// should call MetaspaceClosure::push() on every pointer fields of this ++// class that points to a MetaspaceObj. See Annotations::metaspace_pointers_do() ++// for an example. ++ ++ ++class MetaspaceClosure : public StackObj { ++public: ++ enum Writability { ++ _writable, ++ _not_writable, ++ _default ++ }; ++ ++ enum SpecialRef { ++ _method_entry_ref ++ }; ++ ++ // class MetaspaceClosure::Ref -- ++ // ++ // MetaspaceClosure can be viewed as a very simple type of copying garbage ++ // collector. For it to function properly, it requires each subclass of ++ // MetaspaceObj to provide two methods: ++ // ++ // size_t size(); -- to determine how much data to copy ++ // void metaspace_pointers_do(MetaspaceClosure*); -- to locate all the embedded pointers ++ // ++ // Calling these methods would be trivial if these two were virtual methods. ++ // However, to save space, MetaspaceObj has NO vtable. The vtable is introduced ++ // only in the Metadata class. ++ // ++ // To work around the lack of a vtable, we use the Ref class with templates ++ // (see MSORef, OtherArrayRef, MSOArrayRef, and MSOPointerArrayRef) ++ // so that we can statically discover the type of a object. The use of Ref ++ // depends on the fact that: ++ // ++ // [1] We don't use polymorphic pointers for MetaspaceObj's that are not subclasses ++ // of Metadata. I.e., we don't do this: ++ // class Klass { ++ // MetaspaceObj *_obj; ++ // Array* foo() { return (Array*)_obj; } ++ // Symbol* bar() { return (Symbol*) _obj; } ++ // ++ // [2] All Array dimensions are statically declared. ++ class Ref : public CHeapObj { ++ Writability _writability; ++ bool _keep_after_pushing; ++ Ref* _next; ++ void* _user_data; ++ ++ protected: ++ virtual void** mpp() const = 0; ++ Ref(Writability w) : _writability(w), _keep_after_pushing(false), _next(NULL), _user_data(NULL) {} ++ public: ++ virtual bool not_null() const = 0; ++ virtual int size() const = 0; ++ virtual void metaspace_pointers_do(MetaspaceClosure *it) const = 0; ++ virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const = 0; ++ virtual MetaspaceObj::Type msotype() const = 0; ++ virtual bool is_read_only_by_default() const = 0; ++ virtual ~Ref() {} ++ ++ address obj() const { ++ // In some rare cases (see CPSlot in constantPool.hpp) we store some flags in the lowest ++ // 2 bits of a MetaspaceObj pointer. Unmask these when manipulating the pointer. ++ uintx p = (uintx)*mpp(); ++ return (address)(p & (~FLAG_MASK)); ++ } ++ ++ address* addr() const { ++ return (address*)mpp(); ++ } ++ ++ void update(address new_loc) const; ++ ++ Writability writability() const { return _writability; }; ++ void set_keep_after_pushing() { _keep_after_pushing = true; } ++ bool keep_after_pushing() { return _keep_after_pushing; } ++ void set_user_data(void* data) { _user_data = data; } ++ void* user_data() { return _user_data; } ++ void set_next(Ref* n) { _next = n; } ++ Ref* next() const { return _next; } ++ ++ private: ++ static const uintx FLAG_MASK = 0x03; ++ ++ int flag_bits() const { ++ uintx p = (uintx)*mpp(); ++ return (int)(p & FLAG_MASK); ++ } ++ }; ++ ++private: ++ // MSORef -- iterate an instance of MetaspaceObj ++ template class MSORef : public Ref { ++ T** _mpp; ++ T* dereference() const { ++ return *_mpp; ++ } ++ protected: ++ virtual void** mpp() const { ++ return (void**)_mpp; ++ } ++ ++ public: ++ MSORef(T** mpp, Writability w) : Ref(w), _mpp(mpp) {} ++ ++ virtual bool is_read_only_by_default() const { return T::is_read_only_by_default(); } ++ virtual bool not_null() const { return dereference() != NULL; } ++ virtual int size() const { return dereference()->size(); } ++ virtual MetaspaceObj::Type msotype() const { return dereference()->type(); } ++ ++ virtual void metaspace_pointers_do(MetaspaceClosure *it) const { ++ dereference()->metaspace_pointers_do(it); ++ } ++ virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const { ++ ((T*)new_loc)->metaspace_pointers_do(it); ++ } ++ }; ++ ++ // abstract base class for MSOArrayRef, MSOPointerArrayRef and OtherArrayRef ++ template class ArrayRef : public Ref { ++ Array** _mpp; ++ protected: ++ Array* dereference() const { ++ return *_mpp; ++ } ++ virtual void** mpp() const { ++ return (void**)_mpp; ++ } ++ ++ ArrayRef(Array** mpp, Writability w) : Ref(w), _mpp(mpp) {} ++ ++ // all Arrays are read-only by default ++ virtual bool is_read_only_by_default() const { return true; } ++ virtual bool not_null() const { return dereference() != NULL; } ++ virtual int size() const { return dereference()->size(); } ++ virtual MetaspaceObj::Type msotype() const { return MetaspaceObj::array_type(sizeof(T)); } ++ }; ++ ++ // OtherArrayRef -- iterate an instance of Array, where T is NOT a subtype of MetaspaceObj. ++ // T can be a primitive type, such as int, or a structure. However, we do not scan ++ // the fields inside T, so you should not embed any pointers inside T. ++ template class OtherArrayRef : public ArrayRef { ++ public: ++ OtherArrayRef(Array** mpp, Writability w) : ArrayRef(mpp, w) {} ++ ++ virtual void metaspace_pointers_do(MetaspaceClosure *it) const { ++ Array* array = ArrayRef::dereference(); ++ if (TraceDynamicCDS) ++ dynamic_cds_log->print_cr("Iter(OtherArray): %p [%d]", array, array->length()); ++ } ++ virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const { ++ Array* array = (Array*)new_loc; ++ if (TraceDynamicCDS) ++ dynamic_cds_log->print_cr("Iter(OtherArray): %p [%d]", array, array->length()); ++ } ++ }; ++ ++ // MSOArrayRef -- iterate an instance of Array, where T is a subtype of MetaspaceObj. ++ // We recursively call T::metaspace_pointers_do() for each element in this array. ++ template class MSOArrayRef : public ArrayRef { ++ public: ++ MSOArrayRef(Array** mpp, Writability w) : ArrayRef(mpp, w) {} ++ ++ virtual void metaspace_pointers_do(MetaspaceClosure *it) const { ++ metaspace_pointers_do_at_impl(it, ArrayRef::dereference()); ++ } ++ virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const { ++ metaspace_pointers_do_at_impl(it, (Array*)new_loc); ++ } ++ private: ++ void metaspace_pointers_do_at_impl(MetaspaceClosure *it, Array* array) const { ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Iter(MSOArray): %p [%d]", array, array->length()); ++ } ++ for (int i = 0; i < array->length(); i++) { ++ T* elm = array->adr_at(i); ++ elm->metaspace_pointers_do(it); ++ } ++ } ++ }; ++ ++ // MSOPointerArrayRef -- iterate an instance of Array, where T is a subtype of MetaspaceObj. ++ // We recursively call MetaspaceClosure::push() for each pointer in this array. ++ template class MSOPointerArrayRef : public ArrayRef { ++ public: ++ MSOPointerArrayRef(Array** mpp, Writability w) : ArrayRef(mpp, w) {} ++ ++ virtual void metaspace_pointers_do(MetaspaceClosure *it) const { ++ metaspace_pointers_do_at_impl(it, ArrayRef::dereference()); ++ } ++ virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const { ++ metaspace_pointers_do_at_impl(it, (Array*)new_loc); ++ } ++ private: ++ void metaspace_pointers_do_at_impl(MetaspaceClosure *it, Array* array) const { ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Iter(MSOPointerArray): %p [%d]", array, array->length()); ++ } ++ for (int i = 0; i < array->length(); i++) { ++ T** mpp = array->adr_at(i); ++ it->push(mpp); ++ } ++ } ++ }; ++ ++ // Normally, chains of references like a->b->c->d are iterated recursively. However, ++ // if recursion is too deep, we save the Refs in _pending_refs, and push them later in ++ // MetaspaceClosure::finish(). This avoids overflowing the C stack. ++ static const int MAX_NEST_LEVEL = 5; ++ Ref* _pending_refs; ++ int _nest_level; ++ Ref* _enclosing_ref; ++ ++ void push_impl(Ref* ref); ++ void do_push(Ref* ref); ++ ++public: ++ MetaspaceClosure(): _pending_refs(NULL), _nest_level(0), _enclosing_ref(NULL) {} ++ ~MetaspaceClosure(); ++ ++ void finish(); ++ ++ // enclosing_ref() is used to compute the offset of a field in a C++ class. For example ++ // class Foo { intx scala; Bar* ptr; } ++ // Foo *f = 0x100; ++ // when the f->ptr field is iterated with do_ref() on 64-bit platforms, we will have ++ // do_ref(Ref* r) { ++ // r->addr() == 0x108; // == &f->ptr; ++ // enclosing_ref()->obj() == 0x100; // == foo ++ // So we know that we are iterating upon a field at offset 8 of the object at 0x100. ++ // ++ // Note that if we have stack overflow, do_pending_ref(r) will be called first and ++ // do_ref(r) will be called later, for the same r. In this case, enclosing_ref() is valid only ++ // when do_pending_ref(r) is called, and will return NULL when do_ref(r) is called. ++ Ref* enclosing_ref() const { ++ return _enclosing_ref; ++ } ++ ++ // This is called when a reference is placed in _pending_refs. Override this ++ // function if you're using enclosing_ref(). See notes above. ++ virtual void do_pending_ref(Ref* ref) {} ++ ++ // returns true if we want to keep iterating the pointers embedded inside ++ virtual bool do_ref(Ref* ref, bool read_only) = 0; ++ ++private: ++ template ++ void push_with_ref(T** mpp, Writability w) { ++ push_impl(new REF_TYPE(mpp, w)); ++ } ++ ++public: ++ // When MetaspaceClosure::push(...) is called, pick the correct Ref subtype to handle it: ++ // ++ // MetaspaceClosure* it = ...; ++ // Klass* o = ...; it->push(&o); => MSORef ++ // Array* a1 = ...; it->push(&a1); => OtherArrayRef ++ // Array* a2 = ...; it->push(&a2); => MSOArrayRef ++ // Array* a3 = ...; it->push(&a3); => MSOPointerArrayRef ++ // Array*>* a4 = ...; it->push(&a4); => MSOPointerArrayRef ++ // Array* a5 = ...; it->push(&a5); => MSOPointerArrayRef ++ // ++ // Note that the following will fail to compile (to prevent you from adding new fields ++ // into the MetaspaceObj subtypes that cannot be properly copied by CDS): ++ // ++ // Hashtable* h = ...; it->push(&h); => Hashtable is not a subclass of MetaspaceObj ++ // Array* a6 = ...; it->push(&a6); => Hashtable is not a subclass of MetaspaceObj ++ // Array* a7 = ...; it->push(&a7); => int is not a subclass of MetaspaceObj ++ ++ template ++ void push(T** mpp, Writability w = _default) { ++ push_with_ref >(mpp, w); ++ } ++ ++ void push(Array** mpp, Writability w = _default) { ++ push_with_ref >(mpp, w); ++ } ++ ++ void push(Array** mpp, Writability w = _default) { ++ push_with_ref >(mpp, w); ++ } ++ ++ void push(Array** mpp, Writability w = _default) { ++ push_with_ref >(mpp, w); ++ } ++ ++ void push(Array** mpp, Writability w = _default) { ++ push_with_ref >(mpp, w); ++ } ++ ++ void push(Array** mpp, Writability w = _default) { ++ push_with_ref >(mpp, w); ++ } ++ ++ template ++ void push(Array** mpp, Writability w = _default) { ++ push_with_ref >(mpp, w); ++ } ++ ++ template ++ void push(Array** mpp, Writability w = _default) { ++ push_with_ref >(mpp, w); ++ } ++ ++#if 0 ++ // Enable this block if you're changing the push(...) methods, to test for types that should be ++ // disallowed. Each of the following "push" calls should result in a compile-time error. ++ void test_disallowed_types(MetaspaceClosure* it) { ++ Hashtable* h = NULL; ++ it->push(&h); ++ ++ Array*>* a6 = NULL; ++ it->push(&a6); ++ ++ Array* a7 = NULL; ++ it->push(&a7); ++ } ++#endif ++}; ++ ++// This is a special MetaspaceClosure that visits each unique MetaspaceObj once. ++class UniqueMetaspaceClosure : public MetaspaceClosure { ++ static const int INITIAL_TABLE_SIZE = 15889; ++ static const int MAX_TABLE_SIZE = 1000000; ++ ++ // Do not override. Returns true if we are discovering ref->obj() for the first time. ++ virtual bool do_ref(Ref* ref, bool read_only); ++ ++public: ++ // Gets called the first time we discover an object. ++ virtual bool do_unique_ref(Ref* ref, bool read_only) = 0; ++ UniqueMetaspaceClosure() : _has_been_visited(INITIAL_TABLE_SIZE) {} ++ ++private: ++ KVHashtable _has_been_visited; ++}; ++ ++#endif // SHARE_MEMORY_METASPACECLOSURE_HPP +diff --git a/hotspot/src/share/vm/memory/metaspaceShared.cpp b/hotspot/src/share/vm/memory/metaspaceShared.cpp +index 9857b7577..00fb9fe91 100644 +--- a/hotspot/src/share/vm/memory/metaspaceShared.cpp ++++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp +@@ -38,6 +38,7 @@ + #include "memory/metaspaceShared.hpp" + #include "oops/objArrayOop.hpp" + #include "oops/oop.inline.hpp" ++#include "runtime/arguments.hpp" + #include "runtime/signature.hpp" + #include "runtime/vm_operations.hpp" + #include "runtime/vmThread.hpp" +@@ -47,14 +48,17 @@ + PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + + int MetaspaceShared::_max_alignment = 0; +- + ReservedSpace* MetaspaceShared::_shared_rs = NULL; ++char* MetaspaceShared::_requested_base_address; + + bool MetaspaceShared::_link_classes_made_progress; + bool MetaspaceShared::_check_classes_made_progress; + bool MetaspaceShared::_has_error_classes; + bool MetaspaceShared::_archive_loading_failed = false; + bool MetaspaceShared::_remapped_readwrite = false; ++void* MetaspaceShared::_shared_metaspace_static_bottom = NULL; ++void* MetaspaceShared::_shared_metaspace_dynamic_base = NULL; ++void* MetaspaceShared::_shared_metaspace_dynamic_top = NULL; + // Read/write a data stream for restoring/preserving metadata pointers and + // miscellaneous data from/to the shared archive file. + +@@ -843,7 +847,7 @@ int MetaspaceShared::preload_and_dump(const char * class_list_path, + + // Returns true if the class's status has changed + bool MetaspaceShared::try_link_class(InstanceKlass* ik, TRAPS) { +- assert(DumpSharedSpaces, "should only be called during dumping"); ++// assert(DumpSharedSpaces, "should only be called during dumping"); + if (ik->init_state() < InstanceKlass::linked) { + bool saved = BytecodeVerificationLocal; + if (!SharedClassUtil::is_shared_boot_class(ik)) { +@@ -862,6 +866,7 @@ bool MetaspaceShared::try_link_class(InstanceKlass* ik, TRAPS) { + tty->print_cr("Preload Warning: Verification failed for %s", + ik->external_name()); + CLEAR_PENDING_EXCEPTION; ++ SystemDictionaryShared::set_class_has_failed_verification(ik); + ik->set_in_error_state(); + _has_error_classes = true; + } +@@ -902,6 +907,11 @@ public: + FileMapInfo::assert_mark(tag == old_tag); + } + ++ void do_u4(u4* p) { ++ intptr_t obj = nextPtr(); ++ *p = (u4)(uintx(obj)); ++ } ++ + void do_region(u_char* start, size_t size) { + assert((intptr_t)start % sizeof(intptr_t) == 0, "bad alignment"); + assert(size % sizeof(intptr_t) == 0, "bad size"); +@@ -918,7 +928,10 @@ public: + + // Return true if given address is in the mapped shared space. + bool MetaspaceShared::is_in_shared_space(const void* p) { +- return UseSharedSpaces && FileMapInfo::current_info()->is_in_shared_space(p); ++ return UseSharedSpaces && ((FileMapInfo::current_info() != NULL && ++ FileMapInfo::current_info()->is_mapped() && ++ FileMapInfo::current_info()->is_in_shared_space(p)) || ++ is_shared_dynamic(p)); + } + + void MetaspaceShared::print_shared_spaces() { +@@ -927,19 +940,34 @@ void MetaspaceShared::print_shared_spaces() { + } + } + +- + // Map shared spaces at requested addresses and return if succeeded. + // Need to keep the bounds of the ro and rw space for the Metaspace::contains + // call, or is_in_shared_space. + bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) { + size_t image_alignment = mapinfo->alignment(); + ++ mapinfo->set_is_mapped(false); ++ + #ifndef _WINDOWS + // Map in the shared memory and then map the regions on top of it. + // On Windows, don't map the memory here because it will cause the + // mappings of the regions to fail. + ReservedSpace shared_rs = mapinfo->reserve_shared_memory(); +- if (!shared_rs.is_reserved()) return false; ++ if (!shared_rs.is_reserved()) { ++ FileMapInfo::fail_continue("Unable to reserve shared memory"); ++ FLAG_SET_DEFAULT(UseSharedSpaces, false); ++ return false; ++ } ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Reserved archive_space_rs [" INTPTR_FORMAT " - " INTPTR_FORMAT "] (" SIZE_FORMAT ") bytes", ++ p2i(shared_rs.base()), p2i(shared_rs.base() + shared_rs.size()), shared_rs.size()); ++ } ++ if (mapinfo->is_static()) { ++ _requested_base_address = shared_rs.base(); ++ } else { ++ _shared_metaspace_dynamic_base = shared_rs.base(); ++ _shared_metaspace_dynamic_top = shared_rs.base() + shared_rs.size(); ++ } + #endif + + assert(!DumpSharedSpaces, "Should not be called with DumpSharedSpaces"); +@@ -950,40 +978,79 @@ bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) { + char* _mc_base = NULL; + + // Map each shared region +- if ((_ro_base = mapinfo->map_region(ro)) != NULL && +- mapinfo->verify_region_checksum(ro) && +- (_rw_base = mapinfo->map_region(rw)) != NULL && +- mapinfo->verify_region_checksum(rw) && +- (_md_base = mapinfo->map_region(md)) != NULL && +- mapinfo->verify_region_checksum(md) && +- (_mc_base = mapinfo->map_region(mc)) != NULL && +- mapinfo->verify_region_checksum(mc) && +- (image_alignment == (size_t)max_alignment()) && +- mapinfo->validate_classpath_entry_table()) { +- // Success (no need to do anything) +- return true; ++ if (mapinfo->is_static()) { ++ if ((_ro_base = mapinfo->map_region(ro)) != NULL && ++ mapinfo->verify_region_checksum(ro) && ++ (_rw_base = mapinfo->map_region(rw)) != NULL && ++ mapinfo->verify_region_checksum(rw) && ++ (_md_base = mapinfo->map_region(md)) != NULL && ++ mapinfo->verify_region_checksum(md) && ++ (_mc_base = mapinfo->map_region(mc)) != NULL && ++ mapinfo->verify_region_checksum(mc) && ++ (image_alignment == (size_t)max_alignment()) && ++ mapinfo->validate_classpath_entry_table()) { ++ mapinfo->set_is_mapped(true); ++ return true; ++ } + } else { +- // If there was a failure in mapping any of the spaces, unmap the ones +- // that succeeded +- if (_ro_base != NULL) mapinfo->unmap_region(ro); +- if (_rw_base != NULL) mapinfo->unmap_region(rw); +- if (_md_base != NULL) mapinfo->unmap_region(md); +- if (_mc_base != NULL) mapinfo->unmap_region(mc); ++ if ((_rw_base = mapinfo->map_region(d_rw)) != NULL && ++ mapinfo->verify_region_checksum(d_rw) && ++ (_ro_base = mapinfo->map_region(d_ro)) != NULL && ++ mapinfo->verify_region_checksum(d_ro) && ++ (image_alignment == (size_t)max_alignment())) { ++ mapinfo->set_is_mapped(true); ++ return true; ++ } ++ } ++ ++ // If there was a failure in mapping any of the spaces, unmap the ones ++ // that succeeded ++ if (_ro_base != NULL) mapinfo->unmap_region(ro); ++ if (_rw_base != NULL) mapinfo->unmap_region(rw); ++ if (_md_base != NULL) mapinfo->unmap_region(md); ++ if (_mc_base != NULL) mapinfo->unmap_region(mc); + #ifndef _WINDOWS +- // Release the entire mapped region +- shared_rs.release(); ++ // Release the entire mapped region ++ shared_rs.release(); + #endif +- // If -Xshare:on is specified, print out the error message and exit VM, +- // otherwise, set UseSharedSpaces to false and continue. +- if (RequireSharedSpaces || PrintSharedArchiveAndExit) { +- vm_exit_during_initialization("Unable to use shared archive.", "Failed map_region for using -Xshare:on."); +- } else { +- FLAG_SET_DEFAULT(UseSharedSpaces, false); +- } +- return false; ++ // If -Xshare:on is specified, print out the error message and exit VM, ++ // otherwise, set UseSharedSpaces to false and continue. ++ if (RequireSharedSpaces || PrintSharedArchiveAndExit) { ++ vm_exit_during_initialization("Unable to use shared archive.", "Failed map_region for using -Xshare:on."); ++ } else { ++ FLAG_SET_DEFAULT(UseSharedSpaces, false); + } ++ return false; + } + ++void** MetaspaceShared::_vtbl_list = NULL; ++ ++intptr_t* MetaspaceShared::get_archived_vtable(MetaspaceObj::Type msotype, address obj) { ++ Arguments::assert_is_dumping_archive(); ++ switch (msotype) { ++ case MetaspaceObj::SymbolType: ++ case MetaspaceObj::TypeArrayU1Type: ++ case MetaspaceObj::TypeArrayU2Type: ++ case MetaspaceObj::TypeArrayU4Type: ++ case MetaspaceObj::TypeArrayU8Type: ++ case MetaspaceObj::TypeArrayOtherType: ++ case MetaspaceObj::ConstMethodType: ++ case MetaspaceObj::ConstantPoolCacheType: ++ case MetaspaceObj::AnnotationType: ++ case MetaspaceObj::MethodCountersType: ++ // These have no vtables. ++ break; ++ case MetaspaceObj::MethodDataType: ++ // We don't archive MethodData <-- should have been removed in removed_unsharable_info ++ ShouldNotReachHere(); ++ break; ++ default: ++ int vtable_offset = MetaspaceShared::vtbl_list_size * sizeof(void*) + sizeof(intptr_t); ++ char* vtable_start = (char*)_vtbl_list + vtable_offset; ++ return (intptr_t*)find_matching_vtbl_ptr(_vtbl_list, (void*)vtable_start, obj); ++ } ++ return NULL; ++} + // Read the miscellaneous data from the shared file, and + // serialize it out to its various destinations. + +@@ -996,6 +1063,7 @@ void MetaspaceShared::initialize_shared_spaces() { + // for Klass objects. They get filled in later. + + void** vtbl_list = (void**)buffer; ++ _vtbl_list = vtbl_list; + buffer += MetaspaceShared::vtbl_list_size * sizeof(void*); + Universe::init_self_patching_vtbl_list(vtbl_list, vtbl_list_size); + +@@ -1079,6 +1147,15 @@ void MetaspaceShared::initialize_shared_spaces() { + // Close the mapinfo file + mapinfo->close(); + ++ FileMapInfo *dynamic_mapinfo = FileMapInfo::dynamic_info(); ++ if (dynamic_mapinfo != NULL) { ++ intptr_t* buffer = (intptr_t*)dynamic_mapinfo->serialized_data(); ++ ReadClosure rc(&buffer); ++ SymbolTable::serialize_shared_table_header(&rc); ++ SystemDictionaryShared::serialize_dictionary_headers(&rc); ++ dynamic_mapinfo->close(); ++ } ++ + if (PrintSharedArchiveAndExit) { + if (PrintSharedDictionary) { + tty->print_cr("\nShared classes:\n"); +@@ -1104,6 +1181,11 @@ bool MetaspaceShared::remap_shared_readonly_as_readwrite() { + if (!mapinfo->remap_shared_readonly_as_readwrite()) { + return false; + } ++ ++ mapinfo = FileMapInfo::dynamic_info(); ++ if (mapinfo != NULL && !mapinfo->remap_shared_readonly_as_readwrite()) { ++ return false; ++ } + _remapped_readwrite = true; + } + return true; +diff --git a/hotspot/src/share/vm/memory/metaspaceShared.hpp b/hotspot/src/share/vm/memory/metaspaceShared.hpp +index d58ebecb2..a9dadfbb9 100644 +--- a/hotspot/src/share/vm/memory/metaspaceShared.hpp ++++ b/hotspot/src/share/vm/memory/metaspaceShared.hpp +@@ -28,6 +28,7 @@ + #include "memory/memRegion.hpp" + #include "runtime/virtualspace.hpp" + #include "utilities/exceptions.hpp" ++#include "utilities/growableArray.hpp" + #include "utilities/macros.hpp" + + #define LargeSharedArchiveSize (300*M) +@@ -44,6 +45,7 @@ + (uintx)(type ## SharedArchiveSize * region ## RegionPercentage) : Shared ## region ## Size + + class FileMapInfo; ++class SerializeClosure; + + // Class Data Sharing Support + class MetaspaceShared : AllStatic { +@@ -56,6 +58,11 @@ class MetaspaceShared : AllStatic { + static bool _has_error_classes; + static bool _archive_loading_failed; + static bool _remapped_readwrite; ++ static void* _shared_metaspace_static_bottom; ++ static void** _vtbl_list; // Remember the vtable start address for dynamic dump metadata ++ static char* _requested_base_address; ++ static void* _shared_metaspace_dynamic_base; ++ static void* _shared_metaspace_dynamic_top; + public: + enum { + vtbl_list_size = 17, // number of entries in the shared space vtable list. +@@ -71,11 +78,20 @@ class MetaspaceShared : AllStatic { + }; + + enum { +- ro = 0, // read-only shared space in the heap +- rw = 1, // read-write shared space in the heap +- md = 2, // miscellaneous data for initializing tables, etc. +- mc = 3, // miscellaneous code - vtable replacement. +- n_regions = 4 ++ // core archive spaces ++ ro = 0, // read-only shared space in the heap ++ rw = 1, // read-write shared space in the heap ++ md = 2, // miscellaneous data for initializing tables, etc. (static only) ++ mc = 3, // miscellaneous code - vtable replacement. (static only) ++ n_regions = 4 // total number of static regions ++ }; ++ ++ enum { ++ // core dynamic archive spaces ++ d_rw = 0, // read-write shared space in the heap ++ d_ro = 1, // read-only shared space in the heap ++ d_bm = 2, // relocation bitmaps (freed after file mapping is finished) ++ d_n_regions = 2 // d_rw and d_ro + }; + + // Accessor functions to save shared space created for metadata, which has +@@ -108,6 +124,28 @@ class MetaspaceShared : AllStatic { + _archive_loading_failed = true; + } + static bool map_shared_spaces(FileMapInfo* mapinfo) NOT_CDS_RETURN_(false); ++ ++ static bool is_shared_dynamic(const void* p) { ++ return p < _shared_metaspace_dynamic_top && p >= _shared_metaspace_dynamic_base; ++ } ++ ++ // This is the base address as specified by -XX:SharedBaseAddress during -Xshare:dump. ++ // Both the base/top archives are written using this as their base address. ++ // ++ // During static dump: _requested_base_address == SharedBaseAddress. ++ // ++ // During dynamic dump: _requested_base_address is not always the same as SharedBaseAddress: ++ // - SharedBaseAddress is used for *reading the base archive*. I.e., CompactHashtable uses ++ // it to convert offsets to pointers to Symbols in the base archive. ++ // The base archive may be mapped to an OS-selected address due to ASLR. E.g., ++ // you may have SharedBaseAddress == 0x00ff123400000000. ++ // - _requested_base_address is used for *writing the output archive*. It's usually ++ // 0x800000000 (unless it was set by -XX:SharedBaseAddress during -Xshare:dump). ++ static char* requested_base_address() { ++ return _requested_base_address; ++ } ++ ++ static intptr_t* get_archived_vtable(MetaspaceObj::Type msotype, address obj); + static void initialize_shared_spaces() NOT_CDS_RETURN; + + // Return true if given address is in the mapped shared space. +@@ -138,5 +176,8 @@ class MetaspaceShared : AllStatic { + + static int count_class(const char* classlist_file); + static void estimate_regions_size() NOT_CDS_RETURN; ++ ++ static void set_shared_metaspace_static_bottom(void* bottom) { _shared_metaspace_static_bottom = bottom; } ++ static void* shared_metaspace_static_bottom() { return _shared_metaspace_static_bottom; } + }; + #endif // SHARE_VM_MEMORY_METASPACE_SHARED_HPP +diff --git a/hotspot/src/share/vm/oops/annotations.cpp b/hotspot/src/share/vm/oops/annotations.cpp +index 776b8606b..6b3080f17 100644 +--- a/hotspot/src/share/vm/oops/annotations.cpp ++++ b/hotspot/src/share/vm/oops/annotations.cpp +@@ -27,6 +27,7 @@ + #include "memory/heapInspection.hpp" + #include "memory/metadataFactory.hpp" + #include "memory/oopFactory.hpp" ++#include "memory/metaspaceClosure.hpp" + #include "oops/annotations.hpp" + #include "oops/instanceKlass.hpp" + #include "utilities/ostream.hpp" +@@ -36,6 +37,17 @@ Annotations* Annotations::allocate(ClassLoaderData* loader_data, TRAPS) { + return new (loader_data, size(), true, MetaspaceObj::AnnotationType, THREAD) Annotations(); + } + ++void Annotations::metaspace_pointers_do(MetaspaceClosure* it) { ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Iter(Annotations): %p", this); ++ } ++ ++ it->push(&_class_annotations); ++ it->push(&_fields_annotations); ++ it->push(&_class_type_annotations); ++ it->push(&_fields_type_annotations); // FIXME: need a test case where _fields_type_annotations != NULL ++} ++ + // helper + void Annotations::free_contents(ClassLoaderData* loader_data, Array* p) { + if (p != NULL) { +diff --git a/hotspot/src/share/vm/oops/annotations.hpp b/hotspot/src/share/vm/oops/annotations.hpp +index ad405a8db..d1f7bc71b 100644 +--- a/hotspot/src/share/vm/oops/annotations.hpp ++++ b/hotspot/src/share/vm/oops/annotations.hpp +@@ -35,6 +35,7 @@ + class ClassLoaderData; + class outputStream; + class KlassSizeStats; ++class MetaspaceClosure; + + typedef Array AnnotationArray; + +@@ -54,6 +55,8 @@ class Annotations: public MetaspaceObj { + Array* _fields_type_annotations; + + public: ++ void metaspace_pointers_do(MetaspaceClosure* it); ++ + // Allocate instance of this class + static Annotations* allocate(ClassLoaderData* loader_data, TRAPS); + +@@ -61,8 +64,14 @@ class Annotations: public MetaspaceObj { + void deallocate_contents(ClassLoaderData* loader_data); + DEBUG_ONLY(bool on_stack() { return false; }) // for template + ++ // Annotations should be stored in the read-only region of CDS archive. ++ static bool is_read_only_by_default() { return true; } ++ ++ MetaspaceObj::Type type() const { return AnnotationType; } ++ + // Sizing (in words) + static int size() { return sizeof(Annotations) / wordSize; } ++ + #if INCLUDE_SERVICES + void collect_statistics(KlassSizeStats *sz) const; + #endif +diff --git a/hotspot/src/share/vm/oops/arrayKlass.cpp b/hotspot/src/share/vm/oops/arrayKlass.cpp +index 129bce63d..9009d6972 100644 +--- a/hotspot/src/share/vm/oops/arrayKlass.cpp ++++ b/hotspot/src/share/vm/oops/arrayKlass.cpp +@@ -30,6 +30,7 @@ + #include "jvmtifiles/jvmti.h" + #include "memory/gcLocker.hpp" + #include "memory/universe.inline.hpp" ++#include "memory/metaspaceClosure.hpp" + #include "oops/arrayKlass.hpp" + #include "oops/arrayOop.hpp" + #include "oops/instanceKlass.hpp" +@@ -64,6 +65,19 @@ oop ArrayKlass::multi_allocate(int rank, jint* sizes, TRAPS) { + return NULL; + } + ++void ArrayKlass::metaspace_pointers_do(MetaspaceClosure* it) { ++ Klass::metaspace_pointers_do(it); ++ ++ if (TraceDynamicCDS) { ++ ResourceMark rm; ++ dynamic_cds_log->print_cr("Iter(InstanceKlass): %p (%s)", this, external_name()); ++ } ++ ++ // need to cast away volatile ++ it->push((Klass**)&_higher_dimension); ++ it->push((Klass**)&_lower_dimension); ++} ++ + // find field according to JVM spec 5.4.3.2, returns the klass in which the field is defined + Klass* ArrayKlass::find_field(Symbol* name, Symbol* sig, fieldDescriptor* fd) const { + // There are no fields in an array klass but look to the super class (Object) +@@ -203,6 +217,14 @@ void ArrayKlass::remove_unshareable_info() { + _higher_dimension = NULL; + } + ++void ArrayKlass::remove_java_mirror() { ++ Klass::remove_java_mirror(); ++ if (_higher_dimension != NULL) { ++ ArrayKlass *ak = ArrayKlass::cast(higher_dimension()); ++ ak->remove_java_mirror(); ++ } ++} ++ + void ArrayKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS) { + assert(loader_data == ClassLoaderData::the_null_class_loader_data(), "array classes belong to null loader"); + Klass::restore_unshareable_info(loader_data, protection_domain, CHECK); +diff --git a/hotspot/src/share/vm/oops/arrayKlass.hpp b/hotspot/src/share/vm/oops/arrayKlass.hpp +index d28ece376..9b6fd9e0b 100644 +--- a/hotspot/src/share/vm/oops/arrayKlass.hpp ++++ b/hotspot/src/share/vm/oops/arrayKlass.hpp +@@ -100,7 +100,7 @@ class ArrayKlass: public Klass { + + GrowableArray* compute_secondary_supers(int num_extra_slots); + bool compute_is_subtype_of(Klass* k); +- ++ virtual void metaspace_pointers_do(MetaspaceClosure* it); + // Sizing + static int header_size() { return sizeof(ArrayKlass)/HeapWordSize; } + static int static_size(int header_size); +@@ -141,6 +141,7 @@ class ArrayKlass: public Klass { + + // CDS support - remove and restore oops from metadata. Oops are not shared. + virtual void remove_unshareable_info(); ++ virtual void remove_java_mirror(); + virtual void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS); + + // Printing +diff --git a/hotspot/src/share/vm/oops/constMethod.cpp b/hotspot/src/share/vm/oops/constMethod.cpp +index a496149df..fc7d74512 100644 +--- a/hotspot/src/share/vm/oops/constMethod.cpp ++++ b/hotspot/src/share/vm/oops/constMethod.cpp +@@ -26,6 +26,7 @@ + #include "interpreter/interpreter.hpp" + #include "memory/gcLocker.hpp" + #include "memory/heapInspection.hpp" ++#include "memory/metaspaceClosure.hpp" + #include "memory/metadataFactory.hpp" + #include "oops/constMethod.hpp" + #include "oops/method.hpp" +@@ -148,6 +149,31 @@ Method* ConstMethod::method() const { + return _constants->pool_holder()->method_with_idnum(_method_idnum); + } + ++void ConstMethod::metaspace_pointers_do(MetaspaceClosure* it) { ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Iter(ConstMethod): %p", this); ++ } ++ ++ if (!method()->method_holder()->is_rewritten()) { ++ it->push(&_constants, MetaspaceClosure::_writable); ++ } else { ++ it->push(&_constants); ++ } ++ it->push(&_stackmap_data); ++ if (has_method_annotations()) { ++ it->push(method_annotations_addr()); ++ } ++ if (has_parameter_annotations()) { ++ it->push(parameter_annotations_addr()); ++ } ++ if (has_type_annotations()) { ++ it->push(type_annotations_addr()); ++ } ++ if (has_default_annotations()) { ++ it->push(default_annotations_addr()); ++ } ++} ++ + // linenumber table - note that length is unknown until decompression, + // see class CompressedLineNumberReadStream. + +diff --git a/hotspot/src/share/vm/oops/constMethod.hpp b/hotspot/src/share/vm/oops/constMethod.hpp +index 0caa3a26f..20cff631e 100644 +--- a/hotspot/src/share/vm/oops/constMethod.hpp ++++ b/hotspot/src/share/vm/oops/constMethod.hpp +@@ -129,7 +129,7 @@ class MethodParametersElement VALUE_OBJ_CLASS_SPEC { + }; + + class KlassSizeStats; +- ++class MetaspaceClosure; + // Class to collect the sizes of ConstMethod inline tables + #define INLINE_TABLES_DO(do_element) \ + do_element(localvariable_table_length) \ +@@ -344,6 +344,12 @@ public: + // Size needed + static int size(int code_size, InlineTableSizes* sizes); + ++ // ConstMethods should be stored in the read-only region of CDS archive. ++ static bool is_read_only_by_default() { return true; } ++ ++ void metaspace_pointers_do(MetaspaceClosure* it); ++ MetaspaceObj::Type type() const { return ConstMethodType; } ++ + int size() const { return _constMethod_size;} + void set_constMethod_size(int size) { _constMethod_size = size; } + #if INCLUDE_SERVICES +diff --git a/hotspot/src/share/vm/oops/constantPool.cpp b/hotspot/src/share/vm/oops/constantPool.cpp +index b6158e4e9..f8078bffa 100644 +--- a/hotspot/src/share/vm/oops/constantPool.cpp ++++ b/hotspot/src/share/vm/oops/constantPool.cpp +@@ -23,16 +23,19 @@ + */ + + #include "precompiled.hpp" ++#include "cds/archiveUtils.hpp" + #include "classfile/classLoaderData.hpp" + #include "classfile/javaClasses.hpp" + #include "classfile/metadataOnStackMark.hpp" + #include "classfile/symbolTable.hpp" + #include "classfile/systemDictionary.hpp" ++#include "classfile/systemDictionaryShared.hpp" + #include "classfile/vmSymbols.hpp" + #include "interpreter/linkResolver.hpp" + #include "memory/heapInspection.hpp" + #include "memory/metadataFactory.hpp" + #include "memory/oopFactory.hpp" ++#include "memory/metaspaceClosure.hpp" + #include "oops/constantPool.hpp" + #include "oops/instanceKlass.hpp" + #include "oops/objArrayKlass.hpp" +@@ -153,6 +156,52 @@ void ConstantPool::initialize_resolved_references(ClassLoaderData* loader_data, + } + } + ++void ConstantPool::metaspace_pointers_do(MetaspaceClosure* it) { ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Iter(ConstantPool): %p", this); ++ } ++ ++ it->push(&_tags, MetaspaceClosure::_writable); ++ it->push(&_cache); ++ it->push(&_pool_holder); ++ it->push(&_operands); ++ it->push(&_reference_map, MetaspaceClosure::_writable); ++ ++ for (int i = 0; i < length(); i++) { ++ // About resolved klasses, we should be careful because of data structure difference ++ // between jdk8 and jdk17. ++ constantTag ctag = tag_at(i); ++ if (ctag.is_string() || ctag.is_utf8() || ctag.is_replaced_symbol()) { ++ it->push(symbol_at_addr(i)); ++ } else if (ctag.is_klass()) { ++ it->push((Klass**)obj_at_addr_raw(i)); ++ } ++ } ++} ++ ++// We replace data in base() by normal symbol in two conditions: ++// 1. resolved klass ++// The value is klass ptr, in remove_unshareable_info we need replace klass ptr by klass‘s ++// name. The klass may be excluded, hence klass ptr is NULL and lost klass'name ++// at the end. Replace excluded klasses by names. ++// 2. unresolved klass ++// The value is symbol ptr | 1, the data is unparseable pushed in MetaspaceClosure, we need ++// replace the data by a normal symbol ptr at first, and store value symbol ptr | 1 at last. ++void ConstantPool::symbol_replace_excluded_klass() { ++ for (int i = 0; i < length(); i++) { ++ constantTag ctag = tag_at(i); ++ if (ctag.is_klass()) { ++ Klass* klass = resolved_klass_at(i); ++ if (SystemDictionaryShared::is_excluded_class((InstanceKlass*)klass)) { ++ replaced_symbol_at_put(i, klass->name()); ++ } ++ } else if (ctag.is_unresolved_klass()) { ++ CPSlot entry = slot_at(i); ++ replaced_symbol_at_put(i, entry.get_symbol()); ++ } ++ } ++} ++ + // CDS support. Create a new resolved_references array. + void ConstantPool::restore_unshareable_info(TRAPS) { + +@@ -180,18 +229,30 @@ void ConstantPool::restore_unshareable_info(TRAPS) { + } + + void ConstantPool::remove_unshareable_info() { +- if (UseAppCDS) { +- if (cache() != NULL) { +- cache()->reset(); ++ if (cache() != NULL) { ++ cache()->remove_unshareable_info(); ++ } ++ ++ // Shared ConstantPools are in the RO region, so the _flags cannot be modified. ++ // The _on_stack flag is used to prevent ConstantPools from deallocation during ++ // class redefinition. Since shared ConstantPools cannot be deallocated anyway, ++ // we always set _on_stack to true to avoid having to change _flags during runtime. ++ _flags |= _on_stack; ++ int num_klasses = 0; ++ for (int index = 1; index < length(); index++) { // Index 0 is unused ++ if (tag_at(index).is_unresolved_klass_in_error()) { ++ tag_at_put(index, JVM_CONSTANT_UnresolvedClass); ++ } else if (tag_at(index).is_method_handle_in_error()) { ++ tag_at_put(index, JVM_CONSTANT_MethodHandle); ++ } else if (tag_at(index).is_method_type_in_error()) { ++ tag_at_put(index, JVM_CONSTANT_MethodType); + } +- for (int i = 0; i < _length; i++) { +- if (tag_at(i).is_klass()) { +- Klass* resolvedKlass = resolved_klass_at(i); +- ResourceMark rm; +- char* name = resolvedKlass->name()->as_C_string(); +- int len = strlen(name); +- unresolved_klass_at_put(i, resolvedKlass->name()); +- } ++ ++ if (tag_at(index).is_klass()) { ++ Klass* resolved_Klass = resolved_klass_at(index); ++ unresolved_klass_at_put(index, resolved_Klass->name()); ++ } else if (tag_at(index).is_replaced_symbol()) { ++ unresolved_klass_at_put(index, *symbol_at_addr(index)); + } + } + // Resolved references are not in the shared archive. +@@ -519,8 +580,14 @@ Klass* ConstantPool::klass_ref_at(int which, TRAPS) { + + + Symbol* ConstantPool::klass_name_at(int which) const { +- assert(tag_at(which).is_unresolved_klass() || tag_at(which).is_klass(), +- "Corrupted constant pool"); ++ // Dynamic CDS dump need call here in verify, release version no need do it. ++#ifndef PRODUCT ++ assert(tag_at(which).is_unresolved_klass() || tag_at(which).is_klass() || ++ tag_at(which).is_replaced_symbol(), "Corrupted constant pool"); ++ if (tag_at(which).is_replaced_symbol()) { ++ return *symbol_at_addr(which); ++ } ++#endif + // A resolved constantPool entry will contain a Klass*, otherwise a Symbol*. + // It is not safe to rely on the tag bit's here, since we don't have a lock, and the entry and + // tag is not updated atomicly. +diff --git a/hotspot/src/share/vm/oops/constantPool.hpp b/hotspot/src/share/vm/oops/constantPool.hpp +index ec111df04..b5b4db38b 100644 +--- a/hotspot/src/share/vm/oops/constantPool.hpp ++++ b/hotspot/src/share/vm/oops/constantPool.hpp +@@ -231,6 +231,9 @@ class ConstantPool : public Metadata { + return cache()->entry_at(cp_cache_index); + } + ++ virtual void metaspace_pointers_do(MetaspaceClosure* it); ++ void symbol_replace_excluded_klass(); ++ virtual MetaspaceObj::Type type() const { return ConstantPoolType; } + // Assembly code support + static int tags_offset_in_bytes() { return offset_of(ConstantPool, _tags); } + static int cache_offset_in_bytes() { return offset_of(ConstantPool, _cache); } +@@ -315,6 +318,11 @@ class ConstantPool : public Metadata { + *symbol_at_addr(which) = s; + } + ++ void replaced_symbol_at_put(int which, Symbol*s) { ++ tag_at_put(which, JVM_CONSTANT_ReplacedSymbol); ++ *symbol_at_addr(which) = s; ++ } ++ + void string_at_put(int which, int obj_index, oop str) { + resolved_references()->obj_at_put(obj_index, str); + } +@@ -747,6 +755,10 @@ class ConstantPool : public Metadata { + void collect_statistics(KlassSizeStats *sz) const; + #endif + ++ // ConstantPools should be stored in the read-only region of CDS archive. ++ // But the vtable will be patched in JDK8, so it must be writable. ++ static bool is_read_only_by_default() { return false; } ++ + friend class ClassFileParser; + friend class SystemDictionary; + +diff --git a/hotspot/src/share/vm/oops/cpCache.cpp b/hotspot/src/share/vm/oops/cpCache.cpp +index ebcf3d6a9..51f5397b8 100644 +--- a/hotspot/src/share/vm/oops/cpCache.cpp ++++ b/hotspot/src/share/vm/oops/cpCache.cpp +@@ -24,14 +24,17 @@ + + #include "precompiled.hpp" + #include "gc_implementation/shared/markSweep.inline.hpp" ++#include "interpreter/bytecodeStream.hpp" + #include "interpreter/interpreter.hpp" + #include "interpreter/rewriter.hpp" + #include "memory/universe.inline.hpp" ++#include "memory/metaspaceClosure.hpp" + #include "oops/cpCache.hpp" + #include "oops/objArrayOop.hpp" + #include "oops/oop.inline.hpp" + #include "prims/jvmtiRedefineClassesTrace.hpp" + #include "prims/methodHandles.hpp" ++#include "runtime/arguments.hpp" + #include "runtime/handles.inline.hpp" + #include "runtime/orderAccess.inline.hpp" + #include "utilities/macros.hpp" +@@ -602,6 +605,72 @@ void ConstantPoolCache::initialize(const intArray& inverse_index_map, + } + } + ++void ConstantPoolCache::metaspace_pointers_do(MetaspaceClosure* it) { ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Iter(ConstantPoolCache): %p", this); ++ } ++ it->push(&_constant_pool); ++ // it->push(&_reference_map); ++} ++ ++void ConstantPoolCache::remove_unshareable_info() { ++ walk_entries_for_initialization(/*check_only = */ false); ++} ++ ++void ConstantPoolCache::walk_entries_for_initialization(bool check_only) { ++ Arguments::assert_is_dumping_archive(); ++ // When dumping the archive, we want to clean up the ConstantPoolCache ++ // to remove any effect of linking due to the execution of Java code -- ++ // each ConstantPoolCacheEntry will have the same contents as if ++ // ConstantPoolCache::initialize has just returned: ++ // ++ // - We keep the ConstantPoolCache::constant_pool_index() bits for all entries. ++ // - We keep the "f2" field for entries used by invokedynamic and invokehandle ++ // - All other bits in the entries are cleared to zero. ++ ResourceMark rm; ++ ++ InstanceKlass* ik = constant_pool()->pool_holder(); ++ bool* f2_used = NEW_RESOURCE_ARRAY(bool, length()); ++ memset(f2_used, 0, sizeof(bool) * length()); ++ ++ Thread* current = Thread::current(); ++ ++ // Find all the slots that we need to preserve f2 ++ for (int i = 0; i < ik->methods()->length(); i++) { ++ Method* m = ik->methods()->at(i); ++ RawBytecodeStream bcs(methodHandle(current, m)); ++ while (!bcs.is_last_bytecode()) { ++ Bytecodes::Code opcode = bcs.raw_next(); ++ switch (opcode) { ++ case Bytecodes::_invokedynamic: { ++ int index = Bytes::get_native_u4(bcs.bcp() + 1); ++ int cp_cache_index = constant_pool()->invokedynamic_cp_cache_index(index); ++ f2_used[cp_cache_index] = 1; ++ } ++ break; ++ case Bytecodes::_invokehandle: { ++ int cp_cache_index = Bytes::get_native_u2(bcs.bcp() + 1); ++ f2_used[cp_cache_index] = 1; ++ } ++ break; ++ default: ++ break; ++ } ++ } ++ } ++ ++ if (check_only) { ++ DEBUG_ONLY( ++ for (int i=0; iverify_just_initialized(f2_used[i]); ++ }) ++ } else { ++ for (int i=0; ireinitialize(f2_used[i]); ++ } ++ } ++} ++ + #if INCLUDE_JVMTI + // RedefineClasses() API support: + // If any entry of this ConstantPoolCache points to any of +diff --git a/hotspot/src/share/vm/oops/cpCache.hpp b/hotspot/src/share/vm/oops/cpCache.hpp +index 48f9bbd27..cb2fa43d6 100644 +--- a/hotspot/src/share/vm/oops/cpCache.hpp ++++ b/hotspot/src/share/vm/oops/cpCache.hpp +@@ -124,6 +124,7 @@ class PSPromotionManager; + // source code. The _indices field with the bytecode must be written last. + + class CallInfo; ++class MetaspaceClosure; + + class ConstantPoolCacheEntry VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; +@@ -397,6 +398,24 @@ class ConstantPoolCacheEntry VALUE_OBJ_CLASS_SPEC { + // When shifting flags as a 32-bit int, make sure we don't need an extra mask for tos_state: + assert((((u4)-1 >> tos_state_shift) & ~tos_state_mask) == 0, "no need for tos_state mask"); + } ++ ++ void reinitialize(bool f2_used) { ++ _indices &= cp_index_mask; ++ _f1 = NULL; ++ _flags = 0; ++ if (!f2_used) { ++ _f2 = 0; ++ } ++ } ++ ++ void verify_just_initialized(bool f2_used) { ++ assert((_indices & (~cp_index_mask)) == 0, "sanity"); ++ assert(_f1 == NULL, "sanity"); ++ assert(_flags == 0, "sanity"); ++ if (!f2_used) { ++ assert(_f2 == 0, "sanity"); ++ } ++} + }; + + +@@ -468,6 +487,10 @@ class ConstantPoolCache: public MetaspaceObj { + return base() + i; + } + ++ void metaspace_pointers_do(MetaspaceClosure* it); ++ void remove_unshareable_info(); ++ void walk_entries_for_initialization(bool check_only); ++ MetaspaceObj::Type type() const { return ConstantPoolCacheType; } + // Code generation + static ByteSize base_offset() { return in_ByteSize(sizeof(ConstantPoolCache)); } + static ByteSize entry_offset(int raw_index) { +@@ -488,7 +511,7 @@ class ConstantPoolCache: public MetaspaceObj { + #endif // INCLUDE_JVMTI + + void reset(); +- ++ + // Deallocate - no fields to deallocate + DEBUG_ONLY(bool on_stack() { return false; }) + void deallocate_contents(ClassLoaderData* data) {} +diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp +index 367c9a09d..0d1b1a8d0 100644 +--- a/hotspot/src/share/vm/oops/instanceKlass.cpp ++++ b/hotspot/src/share/vm/oops/instanceKlass.cpp +@@ -39,6 +39,7 @@ + #include "memory/iterator.inline.hpp" + #include "memory/metadataFactory.hpp" + #include "memory/oopFactory.hpp" ++#include "memory/metaspaceClosure.hpp" + #include "oops/fieldStreams.hpp" + #include "oops/instanceClassLoaderKlass.hpp" + #include "oops/instanceKlass.hpp" +@@ -53,6 +54,7 @@ + #include "prims/jvmtiRedefineClasses.hpp" + #include "prims/jvmtiThreadState.hpp" + #include "prims/methodComparator.hpp" ++#include "runtime/arguments.hpp" + #include "runtime/fieldDescriptor.hpp" + #include "runtime/handles.inline.hpp" + #include "runtime/javaCalls.hpp" +@@ -463,12 +465,73 @@ void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) { + MetadataFactory::free_metadata(loader_data, annotations()); + } + set_annotations(NULL); ++ ++ if (Arguments::is_dumping_archive()) { ++ SystemDictionaryShared::remove_dumptime_info(this); ++ } + } + + bool InstanceKlass::should_be_initialized() const { + return !is_initialized(); + } + ++void InstanceKlass::metaspace_pointers_do(MetaspaceClosure* it) { ++ Klass::metaspace_pointers_do(it); ++ ++ if (TraceDynamicCDS) { ++ ResourceMark rm; ++ dynamic_cds_log->print_cr("Iter(InstanceKlass): %p (%s)", this, external_name()); ++ } ++ ++ it->push(&_annotations); ++ it->push((Klass**)&_array_klasses); ++ if (!is_rewritten()) { ++ it->push(&_constants, MetaspaceClosure::_writable); ++ } else { ++ it->push(&_constants); ++ } ++ it->push(&_inner_classes); ++#if INCLUDE_JVMTI ++ it->push(&_previous_versions); ++#endif ++ it->push(&_array_name); ++ it->push(&_methods); ++ it->push(&_default_methods); ++ it->push(&_local_interfaces); ++ it->push(&_transitive_interfaces); ++ it->push(&_method_ordering); ++ if (!is_rewritten()) { ++ it->push(&_default_vtable_indices, MetaspaceClosure::_writable); ++ } else { ++ it->push(&_default_vtable_indices); ++ } ++ ++ // _fields might be written into by Rewriter::scan_method() -> fd.set_has_initialized_final_update() ++ it->push(&_fields, MetaspaceClosure::_writable); ++ ++ if (itable_length() > 0) { ++ itableOffsetEntry* ioe = (itableOffsetEntry*)start_of_itable(); ++ int method_table_offset_in_words = ioe->offset()/wordSize; ++ int nof_interfaces = (method_table_offset_in_words - itable_offset_in_words()) ++ / itableOffsetEntry::size(); ++ ++ for (int i = 0; i < nof_interfaces; i ++, ioe ++) { ++ if (ioe->interface_klass() != NULL) { ++ it->push(ioe->interface_klass_addr()); ++ itableMethodEntry* ime = ioe->first_method_entry(this); ++ int n = klassItable::method_count_for_interface(ioe->interface_klass()); ++ for (int index = 0; index < n; index ++) { ++ it->push(ime[index].method_addr()); ++ } ++ } ++ } ++ } ++ ++ // it->push(&_nest_members); ++ // it->push(&_permitted_subclasses); ++ // it->push(&_record_components); ++} ++ + klassVtable* InstanceKlass::vtable() const { + return new klassVtable(this, start_of_vtable(), vtable_length() / vtableEntry::size()); + } +@@ -765,6 +828,28 @@ bool InstanceKlass::link_class_impl( + } + + ++// Check if a class or any of its supertypes has a version older than 50. ++// CDS will not perform verification of old classes during dump time because ++// without changing the old verifier, the verification constraint cannot be ++// retrieved during dump time. ++// Verification of archived old classes will be performed during run time. ++bool InstanceKlass::can_be_verified_at_dumptime() const { ++ if (major_version() < 50 /*JAVA_6_VERSION*/) { ++ return false; ++ } ++ if (java_super() != NULL && !java_super()->can_be_verified_at_dumptime()) { ++ return false; ++ } ++ Array* interfaces = local_interfaces(); ++ int len = interfaces->length(); ++ for (int i = 0; i < len; i++) { ++ if (!((InstanceKlass*)interfaces->at(i))->can_be_verified_at_dumptime()) { ++ return false; ++ } ++ } ++ return true; ++} ++ + // Rewrite the byte codes of all of the methods of a class. + // The rewriter must be called exactly once. Rewriting must happen after + // verification but before the first method of the class is executed. +@@ -1459,7 +1544,32 @@ static int linear_search(Array* methods, Symbol* name, Symbol* signatur + } + #endif + ++bool InstanceKlass::_disable_method_binary_search = false; ++ ++NOINLINE int linear_search(const Array* methods, const Symbol* name) { ++ int len = methods->length(); ++ int l = 0; ++ int h = len - 1; ++ while (l <= h) { ++ Method* m = methods->at(l); ++ if (m->name() == name) { ++ return l; ++ } ++ l++; ++ } ++ return -1; ++} ++ + static int binary_search(Array* methods, Symbol* name) { ++ if (InstanceKlass::_disable_method_binary_search) { ++ assert(DynamicDumpSharedSpaces, "must be"); ++ // At the final stage of dynamic dumping, the methods array may not be sorted ++ // by ascending addresses of their names, so we can't use binary search anymore. ++ // However, methods with the same name are still laid out consecutively inside the ++ // methods array, so let's look for the first one that matches. ++ return linear_search(methods, name); ++ } ++ + int len = methods->length(); + // methods are sorted, so do binary search + int l = 0; +@@ -2455,24 +2565,37 @@ void InstanceKlass::remove_unshareable_info() { + m->remove_unshareable_info(); + } + +- if (UseAppCDS) { ++ if (UseAppCDS || DynamicDumpSharedSpaces) { + if (_oop_map_cache != NULL) { + delete _oop_map_cache; + _oop_map_cache = NULL; + } +- ++ + JNIid::deallocate(jni_ids()); + set_jni_ids(NULL); +- ++ + jmethodID* jmeths = methods_jmethod_ids_acquire(); + if (jmeths != (jmethodID*)NULL) { + release_set_methods_jmethod_ids(NULL); + FreeHeap(jmeths); + } + } +- + // do array classes also. + array_klasses_do(remove_unshareable_in_class); ++ // These are not allocated from metaspace. They are safe to set to NULL. ++ _member_names = NULL; ++ _dependencies = NULL; ++ _osr_nmethods_head = NULL; ++ _init_thread = NULL; ++} ++ ++void InstanceKlass::remove_java_mirror() { ++ Klass::remove_java_mirror(); ++ ++ // do array classes also. ++ if (array_klasses() != NULL) { ++ array_klasses()->remove_java_mirror(); ++ } + } + + static void restore_unshareable_in_class(Klass* k, TRAPS) { +diff --git a/hotspot/src/share/vm/oops/instanceKlass.hpp b/hotspot/src/share/vm/oops/instanceKlass.hpp +index 39d2c580c..43919e83d 100644 +--- a/hotspot/src/share/vm/oops/instanceKlass.hpp ++++ b/hotspot/src/share/vm/oops/instanceKlass.hpp +@@ -323,6 +323,7 @@ class InstanceKlass: public Klass { + friend class SystemDictionary; + + public: ++ static bool _disable_method_binary_search; + bool has_nonstatic_fields() const { + return (_misc_flags & _misc_has_nonstatic_fields) != 0; + } +@@ -488,6 +489,7 @@ class InstanceKlass: public Klass { + void link_class(TRAPS); + bool link_class_or_fail(TRAPS); // returns false on failure + void unlink_class(); ++ bool can_be_verified_at_dumptime() const; + void rewrite_class(TRAPS); + void link_methods(TRAPS); + Method* class_initializer(); +@@ -525,6 +527,10 @@ class InstanceKlass: public Klass { + Method* find_method(Symbol* name, Symbol* signature) const; + static Method* find_method(Array* methods, Symbol* name, Symbol* signature); + ++ static void disable_method_binary_search() { ++ _disable_method_binary_search = true; ++ } ++ + // find a local method, but skip static methods + Method* find_instance_method(Symbol* name, Symbol* signature, + PrivateLookupMode private_mode); +@@ -1001,7 +1007,8 @@ class InstanceKlass: public Klass { + bool can_be_fastpath_allocated() const { + return !layout_helper_needs_slow_path(layout_helper()); + } +- ++ ++ virtual void metaspace_pointers_do(MetaspaceClosure* iter); + // Java vtable/itable + klassVtable* vtable() const; // return new klassVtable wrapper + inline Method* method_at_vtable(int index); +@@ -1075,7 +1082,7 @@ class InstanceKlass: public Klass { + + public: + void set_in_error_state() { +- assert(DumpSharedSpaces, "only call this when dumping archive"); ++ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "only call this when dumping archive"); + _init_state = initialization_error; + } + bool check_sharing_error_state(); +@@ -1150,6 +1157,7 @@ private: + public: + // CDS support - remove and restore oops from metadata. Oops are not shared. + virtual void remove_unshareable_info(); ++ virtual void remove_java_mirror(); + virtual void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS); + + // jvm support +diff --git a/hotspot/src/share/vm/oops/klass.cpp b/hotspot/src/share/vm/oops/klass.cpp +index 5269060a4..34d9d9895 100644 +--- a/hotspot/src/share/vm/oops/klass.cpp ++++ b/hotspot/src/share/vm/oops/klass.cpp +@@ -26,16 +26,19 @@ + #include "classfile/javaClasses.hpp" + #include "classfile/dictionary.hpp" + #include "classfile/systemDictionary.hpp" ++#include "classfile/systemDictionaryShared.hpp" + #include "classfile/vmSymbols.hpp" + #include "gc_implementation/shared/markSweep.inline.hpp" + #include "gc_interface/collectedHeap.inline.hpp" + #include "memory/heapInspection.hpp" + #include "memory/metadataFactory.hpp" ++#include "memory/metaspaceClosure.hpp" + #include "memory/oopFactory.hpp" + #include "memory/resourceArea.hpp" + #include "oops/instanceKlass.hpp" + #include "oops/klass.inline.hpp" + #include "oops/oop.inline2.hpp" ++#include "runtime/arguments.hpp" + #include "runtime/atomic.inline.hpp" + #include "runtime/orderAccess.inline.hpp" + #include "utilities/stack.hpp" +@@ -69,6 +72,10 @@ ClassLoaderData *Klass::_fake_loader_data_Ext = reinterpret_castincrement_refcount(); ++ ++ if (Arguments::is_dumping_archive() && oop_is_instance()) { ++ SystemDictionaryShared::init_dumptime_info(InstanceKlass::cast(this)); ++ } + } + + bool Klass::is_subclass_of(const Klass* k) const { +@@ -369,6 +376,36 @@ GrowableArray* Klass::compute_secondary_supers(int num_extra_slots) { + return NULL; + } + ++void Klass::metaspace_pointers_do(MetaspaceClosure* it) { ++ if (TraceDynamicCDS) { ++ ResourceMark rm; ++ dynamic_cds_log->print_cr("Iter(Klass): %p (%s)", this, external_name()); ++ } ++ ++ it->push(&_name); ++ it->push(&_secondary_super_cache); ++ it->push(&_secondary_supers); ++ for (int i = 0; i < _primary_super_limit; i++) { ++ it->push(&_primary_supers[i]); ++ } ++ it->push(&_super); ++ it->push((Klass**)&_subklass); ++ it->push((Klass**)&_next_sibling); ++ it->push(&_next_link); ++ ++ vtableEntry* vt = start_of_vtable(); ++ for (int i = 0; i < vtable_length(); i++) { ++ it->push(vt[i].method_addr()); ++ } ++} ++ ++inline vtableEntry* Klass::start_of_vtable() const { ++ return (vtableEntry*) ((address)this + in_bytes(vtable_start_offset())); ++} ++ ++inline ByteSize Klass::vtable_start_offset() { ++ return in_ByteSize(InstanceKlass::header_size() * wordSize); ++} + + Klass* Klass::subklass() const { + return _subklass == NULL ? NULL : _subklass; +@@ -530,7 +567,7 @@ void Klass::oops_do(OopClosure* cl) { + } + + void Klass::remove_unshareable_info() { +- assert (DumpSharedSpaces, "only called for DumpSharedSpaces"); ++ assert (DumpSharedSpaces || DynamicDumpSharedSpaces, "only called for DumpSharedSpaces or DynamicDumpSharedSpaces"); + + JFR_ONLY(REMOVE_ID(this);) + set_subklass(NULL); +@@ -539,40 +576,46 @@ void Klass::remove_unshareable_info() { + set_java_mirror(NULL); + set_next_link(NULL); + +- if (!UseAppCDS) { +- // CDS logic ++ if (class_loader_data() == NULL) { ++ // Null out class loader data for classes loaded by bootstrap (null) loader ++ set_class_loader_data(NULL); ++ } else if (SystemDictionary::is_ext_class_loader(class_loader())) { ++ // Mark class loaded by system class loader ++ set_class_loader_data(_fake_loader_data_Ext); ++ } else if (SystemDictionary::is_app_class_loader(class_loader())) { ++ set_class_loader_data(_fake_loader_data_App); ++ } else { ++ // Class loader data for classes loaded by customer loader + set_class_loader_data(NULL); +- } else if (class_loader_data() != NULL) { +- // AppCDS logic +- if (class_loader() == NULL) { +- // Null out class loader data for classes loaded by bootstrap (null) loader +- set_class_loader_data(NULL); +- } else if(SystemDictionary::is_ext_class_loader(class_loader())) { +- // Mark class loaded by system class loader +- set_class_loader_data(_fake_loader_data_Ext); +- } else { +- set_class_loader_data(_fake_loader_data_App); +- } + } + } + ++void Klass::remove_java_mirror() { ++ Arguments::assert_is_dumping_archive(); ++ if (TraceDynamicCDS) { ++ ResourceMark rm; ++ dynamic_cds_log->print_cr("remove java_mirror: %s", external_name()); ++ } ++ // Just null out the mirror. The class_loader_data() no longer exists. ++ _java_mirror = NULL; ++} ++ + void Klass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS) { + JFR_ONLY(RESTORE_ID(this);) ++ if (TraceDynamicCDS) { ++ ResourceMark rm(THREAD); ++ dynamic_cds_log->print_cr("restore: %s", external_name()); ++ } ++ + // If an exception happened during CDS restore, some of these fields may already be + // set. We leave the class on the CLD list, even if incomplete so that we don't + // modify the CLD list outside a safepoint. + if (class_loader_data() == NULL || has_fake_loader_data()) { +- // CDS should not set fake loader data +- assert(!has_fake_loader_data() || (has_fake_loader_data() && UseAppCDS), +- "setting fake loader data possible only with AppCDS enabled"); +- // Restore class_loader_data + set_class_loader_data(loader_data); +- + // Add to class loader list first before creating the mirror + // (same order as class file parsing) + loader_data->add_class(this); + } +- + // Recreate the class mirror. + // Only recreate it if not present. A previous attempt to restore may have + // gotten an OOM later but keep the mirror if it was created. +diff --git a/hotspot/src/share/vm/oops/klass.hpp b/hotspot/src/share/vm/oops/klass.hpp +index f70587eab..4e45a7756 100644 +--- a/hotspot/src/share/vm/oops/klass.hpp ++++ b/hotspot/src/share/vm/oops/klass.hpp +@@ -94,6 +94,8 @@ class ParCompactionManager; + class KlassSizeStats; + class fieldDescriptor; + class MarkSweep; ++class MetaspaceClosure; ++class vtableEntry; + + class Klass : public Metadata { + friend class VMStructs; +@@ -209,7 +211,7 @@ protected: + bool has_fake_loader_data_App() { return class_loader_data() == _fake_loader_data_App; } + bool has_fake_loader_data_Ext() { return class_loader_data() == _fake_loader_data_Ext; } + bool has_fake_loader_data() { return (has_fake_loader_data_App() || has_fake_loader_data_Ext()); } +- ++ + bool is_klass() const volatile { return true; } + + // super +@@ -316,6 +318,7 @@ protected: + _shared_class_path_index = index; + }; + ++ virtual void metaspace_pointers_do(MetaspaceClosure* it); + + protected: // internal accessors + Klass* subklass_oop() const { return _subklass; } +@@ -323,7 +326,10 @@ protected: + void set_subklass(Klass* s); + void set_next_sibling(Klass* s); + ++ vtableEntry* start_of_vtable() const; ++ + public: ++ static ByteSize vtable_start_offset(); + + // Compiler support + static ByteSize super_offset() { return in_ByteSize(offset_of(Klass, _super)); } +@@ -505,6 +511,7 @@ protected: + public: + // CDS support - remove and restore oops from metadata. Oops are not shared. + virtual void remove_unshareable_info(); ++ virtual void remove_java_mirror(); + virtual void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS); + + protected: +@@ -725,6 +732,7 @@ protected: + + virtual const char* internal_name() const = 0; + ++ virtual MetaspaceObj::Type type() const { return ClassType; } + // Verification + virtual void verify_on(outputStream* st); + void verify() { verify_on(tty); } +diff --git a/hotspot/src/share/vm/oops/klassVtable.hpp b/hotspot/src/share/vm/oops/klassVtable.hpp +index 244f3c0cc..9379bcca0 100644 +--- a/hotspot/src/share/vm/oops/klassVtable.hpp ++++ b/hotspot/src/share/vm/oops/klassVtable.hpp +@@ -176,6 +176,7 @@ class vtableEntry VALUE_OBJ_CLASS_SPEC { + } + static int method_offset_in_bytes() { return offset_of(vtableEntry, _method); } + Method* method() const { return _method; } ++ Method** method_addr() { return &_method; } + + private: + Method* _method; +@@ -216,6 +217,7 @@ class itableOffsetEntry VALUE_OBJ_CLASS_SPEC { + int _offset; + public: + Klass* interface_klass() const { return _interface; } ++ InstanceKlass**interface_klass_addr() { return(InstanceKlass**) &_interface; } + int offset() const { return _offset; } + + static itableMethodEntry* method_entry(Klass* k, int offset) { return (itableMethodEntry*)(((address)k) + offset); } +@@ -238,6 +240,7 @@ class itableMethodEntry VALUE_OBJ_CLASS_SPEC { + + public: + Method* method() const { return _method; } ++ Method**method_addr() { return &_method; } + + void clear() { _method = NULL; } + +diff --git a/hotspot/src/share/vm/oops/metadata.hpp b/hotspot/src/share/vm/oops/metadata.hpp +index dc52c452e..372faa953 100644 +--- a/hotspot/src/share/vm/oops/metadata.hpp ++++ b/hotspot/src/share/vm/oops/metadata.hpp +@@ -28,6 +28,7 @@ + #include "utilities/exceptions.hpp" + #include "utilities/globalDefinitions.hpp" + #include "utilities/ostream.hpp" ++class MetaspaceClosure; + + // This is the base class for an internal Class related metadata + class Metadata : public MetaspaceObj { +@@ -47,8 +48,9 @@ class Metadata : public MetaspaceObj { + virtual bool is_method() const volatile { return false; } + virtual bool is_methodData() const volatile { return false; } + virtual bool is_constantPool() const volatile { return false; } +- ++ virtual MetaspaceObj::Type type() const = 0; + virtual const char* internal_name() const = 0; ++ virtual void metaspace_pointers_do(MetaspaceClosure* iter) {} + + void print() const { print_on(tty); } + void print_value() const { print_value_on(tty); } +diff --git a/hotspot/src/share/vm/oops/method.cpp b/hotspot/src/share/vm/oops/method.cpp +index 64cdae9c7..305348bd0 100644 +--- a/hotspot/src/share/vm/oops/method.cpp ++++ b/hotspot/src/share/vm/oops/method.cpp +@@ -37,6 +37,7 @@ + #include "memory/heapInspection.hpp" + #include "memory/metadataFactory.hpp" + #include "memory/metaspaceShared.hpp" ++#include "memory/metaspaceClosure.hpp" + #include "memory/oopFactory.hpp" + #include "oops/constMethod.hpp" + #include "oops/methodData.hpp" +@@ -834,6 +835,20 @@ void Method::set_not_osr_compilable(int comp_level, bool report, const char* rea + assert(!CompilationPolicy::can_be_osr_compiled(this, comp_level), "sanity check"); + } + ++void Method::metaspace_pointers_do(MetaspaceClosure* it) { ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Iter(Method): %p", this); ++ } ++ ++ if (!method_holder()->is_rewritten()) { ++ it->push(&_constMethod, MetaspaceClosure::_writable); ++ } else { ++ it->push(&_constMethod); ++ } ++ it->push(&_method_data); ++ it->push(&_method_counters); ++} ++ + // Revert to using the interpreter and clear out the nmethod + void Method::clear_code(bool acquire_lock /* = true */) { + MutexLockerEx pl(acquire_lock ? Patching_lock : NULL, Mutex::_no_safepoint_check_flag); +@@ -1421,12 +1436,15 @@ static int method_comparator(Method* a, Method* b) { + + // This is only done during class loading, so it is OK to assume method_idnum matches the methods() array + // default_methods also uses this without the ordering for fast find_method +-void Method::sort_methods(Array* methods, bool idempotent, bool set_idnums) { ++void Method::sort_methods(Array* methods, bool idempotent, bool set_idnums, method_comparator_func func) { + int length = methods->length(); + if (length > 1) { ++ if (func == NULL) { ++ func = method_comparator; ++ } + { + No_Safepoint_Verifier nsv; +- QuickSort::sort(methods->data(), length, method_comparator, idempotent); ++ QuickSort::sort(methods->data(), length, func, idempotent); + } + // Reset method ordering + if (set_idnums) { +diff --git a/hotspot/src/share/vm/oops/method.hpp b/hotspot/src/share/vm/oops/method.hpp +index 1f507ac0f..ec93f2fb4 100644 +--- a/hotspot/src/share/vm/oops/method.hpp ++++ b/hotspot/src/share/vm/oops/method.hpp +@@ -99,6 +99,7 @@ class MethodCounters; + class ConstMethod; + class InlineTableSizes; + class KlassSizeStats; ++class MetaspaceClosure; + + class Method : public Metadata { + friend class VMStructs; +@@ -857,6 +858,9 @@ class Method : public Metadata { + void print_made_not_compilable(int comp_level, bool is_osr, bool report, const char* reason); + + public: ++ void metaspace_pointers_do(MetaspaceClosure* it); ++ virtual MetaspaceObj::Type type() const { return MethodType; } ++ + MethodCounters* get_method_counters(TRAPS) { + if (_method_counters == NULL) { + build_method_counters(this, CHECK_AND_CLEAR_NULL); +@@ -897,8 +901,9 @@ class Method : public Metadata { + void print_name(outputStream* st = tty) PRODUCT_RETURN; // prints as "virtual void foo(int)" + #endif + ++ typedef int (*method_comparator_func)(Method* a, Method* b); + // Helper routine used for method sorting +- static void sort_methods(Array* methods, bool idempotent = false, bool set_idnums = true); ++ static void sort_methods(Array* methods, bool idempotent = false, bool set_idnums = true, method_comparator_func func = NULL); + + // Deallocation function for redefine classes or if an error occurs + void deallocate_contents(ClassLoaderData* loader_data); +diff --git a/hotspot/src/share/vm/oops/methodCounters.hpp b/hotspot/src/share/vm/oops/methodCounters.hpp +index b98644574..6a3f7a738 100644 +--- a/hotspot/src/share/vm/oops/methodCounters.hpp ++++ b/hotspot/src/share/vm/oops/methodCounters.hpp +@@ -129,5 +129,12 @@ class MethodCounters: public MetaspaceObj { + return offset_of(MethodCounters, _interpreter_invocation_count); + } + ++ MetaspaceObj::Type type() const { return MethodCountersType; } ++ ++ void metaspace_pointers_do(MetaspaceClosure* it) { ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Iter(MethodCounters): %p", this); ++ } ++ } + }; + #endif //SHARE_VM_OOPS_METHODCOUNTERS_HPP +diff --git a/hotspot/src/share/vm/oops/methodData.cpp b/hotspot/src/share/vm/oops/methodData.cpp +index eb48188a6..bde6ca123 100644 +--- a/hotspot/src/share/vm/oops/methodData.cpp ++++ b/hotspot/src/share/vm/oops/methodData.cpp +@@ -29,6 +29,7 @@ + #include "interpreter/bytecodeStream.hpp" + #include "interpreter/linkResolver.hpp" + #include "memory/heapInspection.hpp" ++#include "memory/metaspaceClosure.hpp" + #include "oops/methodData.hpp" + #include "prims/jvmtiRedefineClasses.hpp" + #include "runtime/compilationPolicy.hpp" +@@ -1683,3 +1684,11 @@ void MethodData::clean_weak_method_links() { + clean_extra_data(&cl); + verify_extra_data_clean(&cl); + } ++ ++ ++void MethodData::metaspace_pointers_do(MetaspaceClosure* iter) { ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Iter(MethodData): %p", this); ++ } ++ iter->push(&_method); ++} +diff --git a/hotspot/src/share/vm/oops/methodData.hpp b/hotspot/src/share/vm/oops/methodData.hpp +index 3cd7cd6f1..eb121268f 100644 +--- a/hotspot/src/share/vm/oops/methodData.hpp ++++ b/hotspot/src/share/vm/oops/methodData.hpp +@@ -67,7 +67,7 @@ class KlassSizeStats; + + // forward decl + class ProfileData; +- ++class MetaspaceClosure; + // DataLayout + // + // Overlay for generic profiling data. +@@ -2486,6 +2486,9 @@ public: + void clean_method_data(BoolObjectClosure* is_alive); + + void clean_weak_method_links(); ++ ++ virtual void metaspace_pointers_do(MetaspaceClosure* iter); ++ virtual MetaspaceObj::Type type() const { return MethodDataType; } + }; + + #endif // SHARE_VM_OOPS_METHODDATAOOP_HPP +diff --git a/hotspot/src/share/vm/oops/objArrayKlass.cpp b/hotspot/src/share/vm/oops/objArrayKlass.cpp +index 19abfbd5a..60d173e9e 100644 +--- a/hotspot/src/share/vm/oops/objArrayKlass.cpp ++++ b/hotspot/src/share/vm/oops/objArrayKlass.cpp +@@ -33,6 +33,7 @@ + #include "memory/metadataFactory.hpp" + #include "memory/resourceArea.hpp" + #include "memory/universe.inline.hpp" ++#include "memory/metaspaceClosure.hpp" + #include "oops/instanceKlass.hpp" + #include "oops/klass.inline.hpp" + #include "oops/objArrayKlass.hpp" +@@ -569,6 +570,12 @@ int ObjArrayKlass::oop_adjust_pointers(oop obj) { + return size; + } + ++void ObjArrayKlass::metaspace_pointers_do(MetaspaceClosure* it) { ++ ArrayKlass::metaspace_pointers_do(it); ++ it->push(&_element_klass); ++ it->push(&_bottom_klass); ++} ++ + #if INCLUDE_ALL_GCS + void ObjArrayKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { + assert(obj->is_objArray(), "obj must be obj array"); +diff --git a/hotspot/src/share/vm/oops/objArrayKlass.hpp b/hotspot/src/share/vm/oops/objArrayKlass.hpp +index ab3cbc61c..c17adba70 100644 +--- a/hotspot/src/share/vm/oops/objArrayKlass.hpp ++++ b/hotspot/src/share/vm/oops/objArrayKlass.hpp +@@ -109,7 +109,8 @@ class ObjArrayKlass : public ArrayKlass { + template inline void objarray_follow_contents(oop obj, int index, MarkSweep* mark); + + int oop_adjust_pointers(oop obj); +- ++ ++ virtual void metaspace_pointers_do(MetaspaceClosure* iter); + // Parallel Scavenge and Parallel Old + PARALLEL_GC_DECLS + #if INCLUDE_ALL_GCS +diff --git a/hotspot/src/share/vm/oops/symbol.hpp b/hotspot/src/share/vm/oops/symbol.hpp +index aaa55c589..4b1b5cb5d 100644 +--- a/hotspot/src/share/vm/oops/symbol.hpp ++++ b/hotspot/src/share/vm/oops/symbol.hpp +@@ -25,9 +25,9 @@ + #ifndef SHARE_VM_OOPS_SYMBOL_HPP + #define SHARE_VM_OOPS_SYMBOL_HPP + +-#include "utilities/utf8.hpp" + #include "memory/allocation.hpp" + #include "runtime/atomic.hpp" ++#include "utilities/utf8.hpp" + + // A Symbol is a canonicalized string. + // All Symbols reside in global SymbolTable and are reference counted. +@@ -101,6 +101,7 @@ + // Since sometimes this is allocated from Metadata, pick a base allocation + // type without virtual functions. + class ClassLoaderData; ++class MetaspaceClosure; + + // We separate the fields in SymbolBase from Symbol::_body so that + // Symbol::size(int) can correctly calculate the space needed. +@@ -113,7 +114,7 @@ class SymbolBase : public MetaspaceObj { + int _identity_hash; + }; + +-class Symbol : private SymbolBase { ++class Symbol : public SymbolBase { + friend class VMStructs; + friend class SymbolTable; + friend class MoveSymbols; +@@ -160,6 +161,9 @@ class Symbol : private SymbolBase { + int refcount() const { return _refcount; } + void increment_refcount(); + void decrement_refcount(); ++ bool is_permanent() const { ++ return (refcount() == -1); ++ } + + int byte_at(int index) const { + assert(index >=0 && index < _length, "symbol index overflow"); +@@ -180,6 +184,17 @@ class Symbol : private SymbolBase { + return starts_with(prefix, (int) strlen(prefix)); + } + ++ void set_permanent() { ++ _refcount = -1; ++ } ++ ++ void metaspace_pointers_do(MetaspaceClosure* it) { ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Iter(Symbol): %p", this); ++ } ++ } ++ ++ MetaspaceObj::Type type() const { return SymbolType; } + // Tests if the symbol starts with the given prefix. + int index_of_at(int i, const char* str, int len) const; + int index_of_at(int i, const char* str) const { +@@ -208,6 +223,9 @@ class Symbol : private SymbolBase { + + jchar* as_unicode(int& length) const; + ++ // Symbols should be stored in the read-only region of CDS archive. ++ static bool is_read_only_by_default() { return true; } ++ + // Treating this symbol as a class name, returns the Java name for the class. + // String is allocated in resource area if buffer is not provided. + // See Klass::external_name() +diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp +index 6f5e75107..1f603021a 100644 +--- a/hotspot/src/share/vm/runtime/arguments.cpp ++++ b/hotspot/src/share/vm/runtime/arguments.cpp +@@ -29,6 +29,7 @@ + #include "compiler/compilerOracle.hpp" + #include "memory/allocation.inline.hpp" + #include "memory/cardTableRS.hpp" ++#include "memory/filemap.hpp" + #include "memory/genCollectedHeap.hpp" + #include "memory/referenceProcessor.hpp" + #include "memory/universe.inline.hpp" +@@ -126,6 +127,7 @@ bool Arguments::_BackgroundCompilation = BackgroundCompilation; + bool Arguments::_ClipInlining = ClipInlining; + + char* Arguments::SharedArchivePath = NULL; ++char* Arguments::SharedDynamicArchivePath = NULL; + + AgentLibraryList Arguments::_libraryList; + AgentLibraryList Arguments::_agentList; +@@ -179,6 +181,117 @@ static void logOption(const char* opt) { + } + } + ++#if INCLUDE_CDS ++// Sharing support ++// Construct the path to the archive ++int Arguments::num_archives(const char* archive_path) { ++ if (archive_path == NULL) { ++ return 0; ++ } ++ int npaths = 1; ++ char* p = (char*)archive_path; ++ while (*p != '\0') { ++ if (*p == os::path_separator()[0]) { ++ npaths++; ++ } ++ p++; ++ } ++ return npaths; ++} ++ ++void Arguments::extract_shared_archive_paths(const char* archive_path, ++ char** base_archive_path, ++ char** top_archive_path) { ++ char* begin_ptr = (char*)archive_path; ++ char* end_ptr = strchr((char*)archive_path, os::path_separator()[0]); ++ if (end_ptr == NULL || end_ptr == begin_ptr) { ++ vm_exit_during_initialization("Base archive was not specified", archive_path); ++ } ++ size_t len = end_ptr - begin_ptr; ++ char* cur_path = NEW_C_HEAP_ARRAY(char, len + 1, mtInternal); ++ strncpy(cur_path, begin_ptr, len); ++ cur_path[len] = '\0'; ++ FileMapInfo::check_archive((const char*)cur_path, true /*is_static*/); ++ *base_archive_path = cur_path; ++ ++ begin_ptr = ++end_ptr; ++ if (*begin_ptr == '\0') { ++ vm_exit_during_initialization("Top archive was not specified", archive_path); ++ } ++ end_ptr = strchr(begin_ptr, '\0'); ++ assert(end_ptr != NULL, "sanity"); ++ len = end_ptr - begin_ptr; ++ cur_path = NEW_C_HEAP_ARRAY(char, len + 1, mtInternal); ++ strncpy(cur_path, begin_ptr, len + 1); ++ ++ FileMapInfo::check_archive((const char*)cur_path, false /*is_static*/); ++ *top_archive_path = cur_path; ++} ++ ++bool Arguments::init_shared_archive_paths() { ++ if (ArchiveClassesAtExit != NULL) { ++ if (DumpSharedSpaces) { ++ vm_exit_during_initialization("-XX:ArchiveClassesAtExit cannot be used with -Xshare:dump"); ++ } ++ SharedDynamicArchivePath = os::strdup_check_oom(ArchiveClassesAtExit, mtClassShared); ++ } else { ++ if (SharedDynamicArchivePath != NULL) { ++ os::free(SharedDynamicArchivePath); ++ SharedDynamicArchivePath = NULL; ++ } ++ } ++ ++ if (SharedArchiveFile != NULL) { ++ int archives = num_archives(SharedArchiveFile); ++ if (is_dumping_archive()) { ++ if (archives > 1) { ++ vm_exit_during_initialization( ++ "Cannot have more than 1 archive file specified in -XX:SharedArchiveFile during CDS dumping"); ++ } ++ if (DynamicDumpSharedSpaces) { ++ if (strcmp(SharedArchiveFile, ArchiveClassesAtExit) == 0) { ++ vm_exit_during_initialization( ++ "Cannot have the same archive file specified for -XX:SharedArchiveFile and -XX:ArchiveClassesAtExit", ++ SharedArchiveFile); ++ } ++ } ++ } ++ ++ if (!is_dumping_archive()) { ++ if (archives > 2) { ++ vm_exit_during_initialization( ++ "Cannot have more than 2 archive files specified in the -XX:SharedArchiveFile option"); ++ } ++ if (archives == 1) { ++ char* temp_archive_path = os::strdup_check_oom(SharedArchiveFile, mtClassShared); ++ int name_size; ++ bool success = ++ FileMapInfo::get_base_archive_name_from_header(temp_archive_path, &name_size, &SharedArchivePath); ++ if (!success) { ++ SharedArchivePath = temp_archive_path; ++ } else { ++ SharedDynamicArchivePath = temp_archive_path; ++ } ++ } else { ++ extract_shared_archive_paths((const char*)SharedArchiveFile, ++ &SharedArchivePath, &SharedDynamicArchivePath); ++ } ++ ++ // We must use tty here instead of dynamic_cds_log for dynamic_cds_log is initialized after share path init. ++ if (InfoDynamicCDS && SharedArchivePath != NULL) { ++ tty->print_cr("SharedArchivePath: %s", SharedArchivePath); ++ } ++ if (InfoDynamicCDS && SharedDynamicArchivePath != NULL) { ++ tty->print_cr("SharedDynamicArchivePath: %s", SharedDynamicArchivePath); ++ } ++ } else { // CDS dumping ++ SharedArchivePath = os::strdup_check_oom(SharedArchiveFile, mtClassShared); ++ } ++ } ++ return (SharedArchivePath != NULL); ++} ++#endif // INCLUDE_CDS ++ + // Process java launcher properties. + void Arguments::process_sun_java_launcher_properties(JavaVMInitArgs* args) { + // See if sun.java.launcher or sun.java.launcher.pid is defined. +@@ -3724,6 +3837,30 @@ jint Arguments::finalize_vm_init_args(SysClassPath* scp_p, bool scp_assembly_req + set_mode_flags(_int); + } + ++#if INCLUDE_CDS ++ if (ArchiveClassesAtExit == NULL) { ++ FLAG_SET_DEFAULT(DynamicDumpSharedSpaces, false); ++ } else { ++ FLAG_SET_DEFAULT(DynamicDumpSharedSpaces, true); ++ // When Dynamic CDS dump is turned on, we will set ClassUnloading false, ++ // and there is no need to care if the class loader is alive. ++ FLAG_SET_DEFAULT(ClassUnloading, false); ++ } ++ ++ if (TraceDynamicCDS) { ++ FLAG_SET_DEFAULT(DebugDynamicCDS, true); ++ FLAG_SET_DEFAULT(InfoDynamicCDS, true); ++ } else if (DebugDynamicCDS) { ++ FLAG_SET_DEFAULT(InfoDynamicCDS, true); ++ } ++ ++#ifdef _LP64 ++ // We attempt to set SharedBaseAddress right above ++ // the java heap base on ObjectAlignmentInBytes. ++ FLAG_SET_DEFAULT(SharedBaseAddress, (ObjectAlignmentInBytes * 4 * G)); ++#endif // _LP64 ++#endif // INCLUDE_CDS ++ + // eventually fix up InitialTenuringThreshold if only MaxTenuringThreshold is set + if (FLAG_IS_DEFAULT(InitialTenuringThreshold) && (InitialTenuringThreshold > MaxTenuringThreshold)) { + FLAG_SET_ERGO(uintx, InitialTenuringThreshold, MaxTenuringThreshold); +@@ -3885,6 +4022,11 @@ void Arguments::set_shared_spaces_flags() { + } + #endif + } ++ ++#if INCLUDE_CDS ++ // Initialize shared archive paths which could include both base and dynamic archive paths ++ init_shared_archive_paths(); ++#endif // INCLUDE_CDS + } + + #if !INCLUDE_ALL_GCS +diff --git a/hotspot/src/share/vm/runtime/arguments.hpp b/hotspot/src/share/vm/runtime/arguments.hpp +index a1fcfc398..19f5cb60b 100644 +--- a/hotspot/src/share/vm/runtime/arguments.hpp ++++ b/hotspot/src/share/vm/runtime/arguments.hpp +@@ -443,7 +443,8 @@ class Arguments : AllStatic { + static bool CheckCompileOnly; + + static char* SharedArchivePath; +- static char* AppCDSLockPath; ++ ++ static char* SharedDynamicArchivePath; + + public: + // Parses the arguments, first phase +@@ -553,6 +554,22 @@ class Arguments : AllStatic { + + static const char* GetSharedArchivePath() { return SharedArchivePath; } + ++ static const char* GetSharedDynamicArchivePath() { return SharedDynamicArchivePath; } ++ ++ static bool init_shared_archive_paths(); ++ ++ static void extract_shared_archive_paths(const char* archive_path, ++ char** base_archive_path, ++ char** top_archive_path); ++ ++ static int num_archives(const char* archive_path); ++ ++ static bool is_dumping_archive() { return DumpSharedSpaces || DynamicDumpSharedSpaces; } ++ ++ static void assert_is_dumping_archive() { ++ assert(Arguments::is_dumping_archive(), "dump time only"); ++ } ++ + static bool CompileMethod(char* className, char* methodName) { + return + methodExists( +diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp +index 91e52f033..eb13ee0d7 100644 +--- a/hotspot/src/share/vm/runtime/globals.hpp ++++ b/hotspot/src/share/vm/runtime/globals.hpp +@@ -3910,6 +3910,24 @@ class CommandLineFlags { + NOT_LP64(LINUX_ONLY(2*G) NOT_LINUX(0)), \ + "Address to allocate shared memory region for class data") \ + \ ++ experimental(ccstr, ArchiveClassesAtExit, NULL, \ ++ "The path and name of the dynamic archive file") \ ++ \ ++ product(bool, InfoDynamicCDS, false, \ ++ "Log info level in DynamicCDS") \ ++ \ ++ product(bool, TraceDynamicCDS, false, \ ++ "Trace details in DynamicCDS") \ ++ \ ++ product(bool, DebugDynamicCDS, false, \ ++ "Debug details in DynamicCDS") \ ++ \ ++ product(bool, DynamicDumpSharedSpaces, false, \ ++ "Dynamic archive") \ ++ \ ++ product(uintx, SharedSymbolTableBucketSize, 4, \ ++ "Average number of symbols per bucket in shared table") \ ++ \ + diagnostic(bool, EnableInvokeDynamic, true, \ + "support JSR 292 (method handles, invokedynamic, " \ + "anonymous classes") \ +@@ -4017,6 +4035,9 @@ class CommandLineFlags { + "Dump the names all loaded classes, that could be stored into " \ + "the CDS archive, in the specified file") \ + \ ++ product(ccstr, DynamicCDSLog, NULL, \ ++ "Dynamic CDS log path") \ ++ \ + product(ccstr, SharedClassListFile, NULL, \ + "Override the default CDS class list") \ + \ +diff --git a/hotspot/src/share/vm/runtime/java.cpp b/hotspot/src/share/vm/runtime/java.cpp +index 0a263b017..4f290c826 100644 +--- a/hotspot/src/share/vm/runtime/java.cpp ++++ b/hotspot/src/share/vm/runtime/java.cpp +@@ -23,6 +23,7 @@ + */ + + #include "precompiled.hpp" ++#include "cds/dynamicArchive.hpp" + #include "classfile/classLoader.hpp" + #include "classfile/symbolTable.hpp" + #include "classfile/systemDictionary.hpp" +@@ -546,6 +547,13 @@ void before_exit(JavaThread * thread) { + // Note: we don't wait until it actually dies. + os::terminate_signal_thread(); + ++#if INCLUDE_CDS ++ if (DynamicDumpSharedSpaces) { ++ DynamicArchive::dump(); ++ ShouldNotReachHere(); ++ } ++#endif ++ + print_statistics(); + Universe::heap()->print_tracing_info(); + +diff --git a/hotspot/src/share/vm/runtime/mutexLocker.cpp b/hotspot/src/share/vm/runtime/mutexLocker.cpp +index a96ae50eb..a1c61f864 100644 +--- a/hotspot/src/share/vm/runtime/mutexLocker.cpp ++++ b/hotspot/src/share/vm/runtime/mutexLocker.cpp +@@ -39,6 +39,7 @@ + + Mutex* Patching_lock = NULL; + Monitor* SystemDictionary_lock = NULL; ++Mutex* SharedDictionary_lock = NULL; + Mutex* PackageTable_lock = NULL; + Mutex* CompiledIC_lock = NULL; + Mutex* InlineCacheBuffer_lock = NULL; +@@ -129,6 +130,7 @@ Monitor* RedefineClasses_lock = NULL; + + Mutex* FreeHumongousRegions_lock = NULL; + ++Mutex* DumpTimeTable_lock = NULL; + #ifdef INCLUDE_JFR + Mutex* JfrStacktrace_lock = NULL; + Monitor* JfrMsg_lock = NULL; +@@ -224,6 +226,7 @@ void mutex_init() { + def(JmethodIdCreation_lock , Mutex , leaf, true ); // used for creating jmethodIDs. + + def(SystemDictionary_lock , Monitor, leaf, true ); // lookups done by VM thread ++ def(SharedDictionary_lock , Mutex , leaf, true ); + def(PackageTable_lock , Mutex , leaf, false); + def(InlineCacheBuffer_lock , Mutex , leaf, true ); + def(VMStatistic_lock , Mutex , leaf, false); +@@ -289,7 +292,7 @@ void mutex_init() { + def(RedefineClasses_lock , Monitor, nonleaf+5, true); + + def(FreeHumongousRegions_lock , Mutex , nonleaf, false); +- ++ def(DumpTimeTable_lock , Mutex , leaf - 1, true); + #if INCLUDE_JFR + def(JfrMsg_lock , Monitor, leaf, true); + def(JfrBuffer_lock , Mutex, leaf, true); +diff --git a/hotspot/src/share/vm/runtime/mutexLocker.hpp b/hotspot/src/share/vm/runtime/mutexLocker.hpp +index 428c80181..f28058b0e 100644 +--- a/hotspot/src/share/vm/runtime/mutexLocker.hpp ++++ b/hotspot/src/share/vm/runtime/mutexLocker.hpp +@@ -47,6 +47,7 @@ + + extern Mutex* Patching_lock; // a lock used to guard code patching of compiled code + extern Monitor* SystemDictionary_lock; // a lock on the system dictonary ++extern Mutex* SharedDictionary_lock; // a lock on the CDS shared dictionary + extern Mutex* PackageTable_lock; // a lock on the class loader package table + extern Mutex* CompiledIC_lock; // a lock used to guard compiled IC patching and access + extern Mutex* InlineCacheBuffer_lock; // a lock used to guard the InlineCacheBuffer +@@ -145,6 +146,8 @@ extern Monitor* RedefineClasses_lock; // locks classes from parallel + + extern Mutex* FreeHumongousRegions_lock; // locks humongous regions from freeing in parallel + ++extern Mutex* DumpTimeTable_lock; // SystemDictionaryShared::find_or_allocate_info_for ++ + #if INCLUDE_JFR + extern Mutex* JfrStacktrace_lock; // used to guard access to the JFR stacktrace table + extern Monitor* JfrMsg_lock; // protects JFR messaging +diff --git a/hotspot/src/share/vm/runtime/os.cpp b/hotspot/src/share/vm/runtime/os.cpp +index ed41265cc..5c5d60220 100644 +--- a/hotspot/src/share/vm/runtime/os.cpp ++++ b/hotspot/src/share/vm/runtime/os.cpp +@@ -568,7 +568,7 @@ bool os::find_builtin_agent(AgentLibrary *agent_lib, const char *syms[], + + // --------------------- heap allocation utilities --------------------- + +-char *os::strdup(const char *str, MEMFLAGS flags) { ++char* os::strdup(const char *str, MEMFLAGS flags) { + size_t size = strlen(str); + char *dup_str = (char *)malloc(size + 1, flags); + if (dup_str == NULL) return NULL; +@@ -576,6 +576,13 @@ char *os::strdup(const char *str, MEMFLAGS flags) { + return dup_str; + } + ++char* os::strdup_check_oom(const char* str, MEMFLAGS flags) { ++ char* p = os::strdup(str, flags); ++ if (p == NULL) { ++ vm_exit_out_of_memory(strlen(str) + 1, OOM_MALLOC_ERROR, "os::strdup_check_oom"); ++ } ++ return p; ++} + + + #define paranoid 0 /* only set to 1 if you suspect checking code has bug */ +diff --git a/hotspot/src/share/vm/runtime/os.hpp b/hotspot/src/share/vm/runtime/os.hpp +index 296380f39..7ae49fd5b 100644 +--- a/hotspot/src/share/vm/runtime/os.hpp ++++ b/hotspot/src/share/vm/runtime/os.hpp +@@ -731,6 +731,8 @@ class os: AllStatic { + static void free (void *memblock, MEMFLAGS flags = mtNone); + static bool check_heap(bool force = false); // verify C heap integrity + static char* strdup(const char *, MEMFLAGS flags = mtInternal); // Like strdup ++ // Like strdup, but exit VM when strdup() returns NULL ++ static char* strdup_check_oom(const char*, MEMFLAGS flags = mtInternal); + + #ifndef PRODUCT + static julong num_mallocs; // # of calls to malloc/realloc +diff --git a/hotspot/src/share/vm/runtime/thread.cpp b/hotspot/src/share/vm/runtime/thread.cpp +index 94b9e69d2..807786d98 100644 +--- a/hotspot/src/share/vm/runtime/thread.cpp ++++ b/hotspot/src/share/vm/runtime/thread.cpp +@@ -23,6 +23,7 @@ + */ + + #include "precompiled.hpp" ++#include "cds/dynamicArchive.hpp" + #include "classfile/classLoader.hpp" + #include "classfile/javaClasses.hpp" + #include "classfile/systemDictionary.hpp" +@@ -3934,6 +3935,15 @@ void JavaThread::invoke_shutdown_hooks() { + this->clear_pending_exception(); + } + ++#if INCLUDE_CDS ++ // Link all classes for dynamic CDS dumping before vm exit. ++ // Same operation is being done in JVM_BeforeHalt for handling the ++ // case where the application calls System.exit(). ++ if (DynamicDumpSharedSpaces) { ++ DynamicArchive::prepare_for_dynamic_dumping_at_exit(); ++ } ++#endif ++ + EXCEPTION_MARK; + Klass* k = + SystemDictionary::resolve_or_null(vmSymbols::java_lang_Shutdown(), +diff --git a/hotspot/src/share/vm/services/diagnosticCommand.cpp b/hotspot/src/share/vm/services/diagnosticCommand.cpp +index ede8db156..358ec6e09 100644 +--- a/hotspot/src/share/vm/services/diagnosticCommand.cpp ++++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp +@@ -23,6 +23,7 @@ + */ + + #include "precompiled.hpp" ++#include "cds/dynamicArchive.hpp" + #include "classfile/classLoaderStats.hpp" + #include "gc_implementation/shared/vmGCOperations.hpp" + #include "runtime/javaCalls.hpp" +@@ -57,6 +58,7 @@ void DCmdRegistrant::register_dcmds(){ + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + #if INCLUDE_SERVICES // Heap dumping/inspection supported + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(DCmd_Source_Internal | DCmd_Source_AttachAPI, true, false)); ++ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(DCmd_Source_Internal | DCmd_Source_AttachAPI, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + #endif // INCLUDE_SERVICES +@@ -375,6 +377,17 @@ int HeapDumpDCmd::num_arguments() { + } + } + ++void DynamicCDSDumpDCmd::execute(DCmdSource source, TRAPS) { ++#if INCLUDE_CDS ++ if (DynamicDumpSharedSpaces) { ++ DynamicArchive::dump(); ++ ShouldNotReachHere(); ++ } else { ++ warning("Dynamic CDS is not enabled"); ++ } ++#endif ++} ++ + ClassHistogramDCmd::ClassHistogramDCmd(outputStream* output, bool heap) : + DCmdWithParser(output, heap), + _all("-all", "Inspect all objects, including unreachable objects", +diff --git a/hotspot/src/share/vm/services/diagnosticCommand.hpp b/hotspot/src/share/vm/services/diagnosticCommand.hpp +index b1fb57e53..e28011f25 100644 +--- a/hotspot/src/share/vm/services/diagnosticCommand.hpp ++++ b/hotspot/src/share/vm/services/diagnosticCommand.hpp +@@ -267,6 +267,29 @@ public: + }; + #endif // INCLUDE_SERVICES + ++class DynamicCDSDumpDCmd : public DCmdWithParser { ++public: ++ DynamicCDSDumpDCmd(outputStream* output, bool heap) : DCmdWithParser(output, heap) { } ++ static const char* name() { ++ return "GC.dynamic_cds_dump"; ++ } ++ static const char* description() { ++ return "Dynamic CDS dump"; ++ } ++ static const char* impact() { ++ return "Medium"; ++ } ++ static const JavaPermission permission() { ++ JavaPermission p = {"java.lang.management.ManagementPermission", ++ "monitor", NULL}; ++ return p; ++ } ++ static int num_arguments() { ++ return 0; ++ } ++ virtual void execute(DCmdSource source, TRAPS); ++}; ++ + // See also: inspectheap in attachListener.cpp + class ClassHistogramDCmd : public DCmdWithParser { + protected: +diff --git a/hotspot/src/share/vm/utilities/array.hpp b/hotspot/src/share/vm/utilities/array.hpp +index 920b87501..371876b56 100644 +--- a/hotspot/src/share/vm/utilities/array.hpp ++++ b/hotspot/src/share/vm/utilities/array.hpp +@@ -302,6 +302,7 @@ define_array(intArray , int ) define_stack(intStack , intArray ) + + template + class Array: public MetaspaceObj { ++ friend class ArchiveBuilder; + friend class MetadataFactory; + friend class VMStructs; + friend class MethodHandleCompiler; // special case +diff --git a/hotspot/src/share/vm/utilities/bitMap.cpp b/hotspot/src/share/vm/utilities/bitMap.cpp +index e64add155..12b4b4160 100644 +--- a/hotspot/src/share/vm/utilities/bitMap.cpp ++++ b/hotspot/src/share/vm/utilities/bitMap.cpp +@@ -67,16 +67,14 @@ void BitMap::resize(idx_t size_in_bits, bool in_resource_area) { + idx_t new_size_in_words = size_in_words(); + if (in_resource_area) { + _map = NEW_RESOURCE_ARRAY(bm_word_t, new_size_in_words); ++ Copy::disjoint_words((HeapWord*)old_map, (HeapWord*) _map, ++ MIN2(old_size_in_words, new_size_in_words)); + } else { +- if (old_map != NULL) { +- _map_allocator.free(); +- } +- _map = _map_allocator.allocate(new_size_in_words); ++ _map = _map_allocator.reallocate(new_size_in_words); + } +- Copy::disjoint_words((HeapWord*)old_map, (HeapWord*) _map, +- MIN2(old_size_in_words, new_size_in_words)); ++ + if (new_size_in_words > old_size_in_words) { +- clear_range_of_words(old_size_in_words, size_in_words()); ++ clear_range_of_words(old_size_in_words, new_size_in_words); + } + } + +@@ -454,6 +452,11 @@ bool BitMap::is_empty() const { + return rest == 0 || (*word & right_n_bits((int)rest)) == (bm_word_t) NoBits; + } + ++void BitMap::write_to(bm_word_t* buffer, size_t buffer_size_in_bytes) const { ++ assert(buffer_size_in_bytes == (size_in_words() * BytesPerWord), "must be"); ++ memcpy(buffer, _map, size_in_words() * BytesPerWord); ++} ++ + void BitMap::clear_large() { + clear_large_range_of_words(0, size_in_words()); + } +diff --git a/hotspot/src/share/vm/utilities/bitMap.hpp b/hotspot/src/share/vm/utilities/bitMap.hpp +index 51c58da8e..08452bd90 100644 +--- a/hotspot/src/share/vm/utilities/bitMap.hpp ++++ b/hotspot/src/share/vm/utilities/bitMap.hpp +@@ -269,6 +269,7 @@ class BitMap VALUE_OBJ_CLASS_SPEC { + bool is_full() const; + bool is_empty() const; + ++ void write_to(bm_word_t* buffer, size_t buffer_size_in_bytes) const; + void print_on_error(outputStream* st, const char* prefix) const; + + #ifndef PRODUCT +diff --git a/hotspot/src/share/vm/utilities/constantTag.hpp b/hotspot/src/share/vm/utilities/constantTag.hpp +index ae99d5706..07a873743 100644 +--- a/hotspot/src/share/vm/utilities/constantTag.hpp ++++ b/hotspot/src/share/vm/utilities/constantTag.hpp +@@ -43,7 +43,8 @@ enum { + JVM_CONSTANT_UnresolvedClassInError = 103, // Error tag due to resolution error + JVM_CONSTANT_MethodHandleInError = 104, // Error tag due to resolution error + JVM_CONSTANT_MethodTypeInError = 105, // Error tag due to resolution error +- JVM_CONSTANT_InternalMax = 105 // Last implementation tag ++ JVM_CONSTANT_ReplacedSymbol = 106, ++ JVM_CONSTANT_InternalMax = 106 // Last implementation tag + }; + + +@@ -62,7 +63,7 @@ class constantTag VALUE_OBJ_CLASS_SPEC { + bool is_double() const { return _tag == JVM_CONSTANT_Double; } + bool is_name_and_type() const { return _tag == JVM_CONSTANT_NameAndType; } + bool is_utf8() const { return _tag == JVM_CONSTANT_Utf8; } +- ++ bool is_replaced_symbol() const { return _tag == JVM_CONSTANT_ReplacedSymbol; } + bool is_invalid() const { return _tag == JVM_CONSTANT_Invalid; } + + bool is_unresolved_klass() const { +diff --git a/hotspot/src/share/vm/utilities/globalDefinitions.hpp b/hotspot/src/share/vm/utilities/globalDefinitions.hpp +index 81866b840..25f6f026c 100644 +--- a/hotspot/src/share/vm/utilities/globalDefinitions.hpp ++++ b/hotspot/src/share/vm/utilities/globalDefinitions.hpp +@@ -1511,6 +1511,16 @@ static inline void* dereference_vptr(const void* addr) { + return *(void**)addr; + } + ++ ++template unsigned primitive_hash(const K& k) { ++ unsigned hash = (unsigned)((uintptr_t)k); ++ return hash ^ (hash >> 3); // just in case we're dealing with aligned ptrs ++} ++ ++template bool primitive_equals(const K& k0, const K& k1) { ++ return k0 == k1; ++} ++ + #ifndef PRODUCT + + // For unit testing only +@@ -1519,7 +1529,6 @@ public: + static void test_globals(); + static void test_proper_unit(); + }; +- + #endif // PRODUCT + + #endif // SHARE_VM_UTILITIES_GLOBALDEFINITIONS_HPP +diff --git a/hotspot/src/share/vm/utilities/hashtable.cpp b/hotspot/src/share/vm/utilities/hashtable.cpp +index c026e6a0e..66df8f1f8 100644 +--- a/hotspot/src/share/vm/utilities/hashtable.cpp ++++ b/hotspot/src/share/vm/utilities/hashtable.cpp +@@ -34,7 +34,7 @@ + #include "utilities/hashtable.hpp" + #include "utilities/hashtable.inline.hpp" + #include "utilities/numberSeq.hpp" +- ++#include "utilities/align.hpp" + + // This hashtable is implemented as an open hash table with a fixed number of buckets. + +@@ -145,7 +145,7 @@ template void BasicHashtable::free_buckets() { + // Don't delete the buckets in the shared space. They aren't + // allocated by os::malloc + if (!UseSharedSpaces || +- !FileMapInfo::current_info()->is_in_shared_space(_buckets)) { ++ !MetaspaceShared::is_in_shared_space(_buckets)) { + FREE_C_HEAP_ARRAY(HashtableBucket, _buckets, F); + } + _buckets = NULL; +@@ -221,7 +221,7 @@ template void BasicHashtable::copy_table(char** top, char* end) + *top += entry_size(); + } + } +- *plen = (char*)(*top) - (char*)plen - sizeof(*plen); ++ *plen = ((char*)(*top) - (char*)plen) - sizeof(*plen); + + // Set the shared bit. + +@@ -317,7 +317,6 @@ template void RehashableHashtable::dump_table(output + st->print_cr("Maximum bucket size : %9d", (int)summary.maximum()); + } + +- + // Dump the hash table buckets. + + template void BasicHashtable::copy_buckets(char** top, char* end) { +@@ -335,6 +334,57 @@ template void BasicHashtable::copy_buckets(char** top, char* end + *top += len; + } + ++template bool BasicHashtable::resize(int new_size) { ++ ++ // Allocate new buckets ++ HashtableBucket* buckets_new = NEW_C_HEAP_ARRAY2_RETURN_NULL(HashtableBucket, new_size, F, CURRENT_PC); ++ if (buckets_new == NULL) { ++ return false; ++ } ++ ++ // Clear the new buckets ++ for (int i = 0; i < new_size; i++) { ++ buckets_new[i].clear(); ++ } ++ ++ int table_size_old = _table_size; ++ // hash_to_index() uses _table_size, so switch the sizes now ++ _table_size = new_size; ++ ++ // Move entries from the old table to a new table ++ for (int index_old = 0; index_old < table_size_old; index_old++) { ++ for (BasicHashtableEntry* p = _buckets[index_old].get_entry(); p != NULL; ) { ++ BasicHashtableEntry* next = p->next(); ++ int index_new = hash_to_index(p->hash()); ++ ++ p->set_next(buckets_new[index_new].get_entry()); ++ buckets_new[index_new].set_entry(p); ++ p = next; ++ } ++ } ++ ++ // The old backets now can be released ++ BasicHashtable::free_buckets(); ++ ++ // Switch to the new storage ++ _buckets = buckets_new; ++ ++ return true; ++} ++ ++template bool BasicHashtable::maybe_grow(int max_size, int load_factor) { ++ assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); ++ ++ if (table_size() >= max_size) { ++ return false; ++ } ++ if (number_of_entries() / table_size() > load_factor) { ++ resize(MIN2(table_size() * 2, max_size)); ++ return true; ++ } else { ++ return false; ++ } ++} + + #ifndef PRODUCT + +@@ -352,7 +402,6 @@ template void Hashtable::print() { + } + } + +- + template void BasicHashtable::verify() { + int count = 0; + for (int i = 0; i < table_size(); i++) { +@@ -406,3 +455,4 @@ template class BasicHashtable; + template class BasicHashtable; + template class BasicHashtable; + template class BasicHashtable; ++template class BasicHashtable; +diff --git a/hotspot/src/share/vm/utilities/hashtable.hpp b/hotspot/src/share/vm/utilities/hashtable.hpp +index 30e442d15..358b09c3d 100644 +--- a/hotspot/src/share/vm/utilities/hashtable.hpp ++++ b/hotspot/src/share/vm/utilities/hashtable.hpp +@@ -151,7 +151,7 @@ public: + void copy_table(char** top, char* end); + + // Bucket handling +- int hash_to_index(unsigned int full_hash) { ++ int hash_to_index(unsigned int full_hash) const { + int h = full_hash % _table_size; + assert(h >= 0 && h < _table_size, "Illegal hash value"); + return h; +@@ -184,7 +184,7 @@ protected: + int entry_size() const { return _entry_size; } + + // The following method is MT-safe and may be used with caution. +- BasicHashtableEntry* bucket(int i); ++ BasicHashtableEntry* bucket(int i) const; + + // The following method is not MT-safe and must be done under lock. + BasicHashtableEntry** bucket_addr(int i) { return _buckets[i].entry_addr(); } +@@ -234,7 +234,7 @@ protected: + // is mt-safe wrt. to other calls of this method. + void bulk_free_entries(BucketUnlinkContext* context); + public: +- int table_size() { return _table_size; } ++ int table_size() const { return _table_size; } + void set_entry(int index, BasicHashtableEntry* entry); + + void add_entry(int index, BasicHashtableEntry* entry); +@@ -243,6 +243,10 @@ public: + + int number_of_entries() { return _number_of_entries; } + ++ bool resize(int new_size); ++ ++ bool maybe_grow(int max_size, int load_factor = 0); ++ + void verify() PRODUCT_RETURN; + }; + +@@ -364,4 +368,92 @@ public: + } + }; + ++// A subclass of BasicHashtable that allows you to do a simple K -> V mapping ++// without using tons of boilerplate code. ++template< ++ typename K, typename V, MEMFLAGS F, ++ unsigned (*HASH) (K const&) = primitive_hash, ++ bool (*EQUALS)(K const&, K const&) = primitive_equals ++ > ++class KVHashtable : public BasicHashtable { ++ class KVHashtableEntry : public BasicHashtableEntry { ++ public: ++ K _key; ++ V _value; ++ KVHashtableEntry* next() { ++ return (KVHashtableEntry*)BasicHashtableEntry::next(); ++ } ++ }; ++ ++protected: ++ KVHashtableEntry* bucket(int i) const { ++ return (KVHashtableEntry*)BasicHashtable::bucket(i); ++ } ++ ++ KVHashtableEntry* new_entry(unsigned int hashValue, K key, V value) { ++ KVHashtableEntry* entry = (KVHashtableEntry*)BasicHashtable::new_entry(hashValue); ++ entry->_key = key; ++ entry->_value = value; ++ return entry; ++ } ++ ++public: ++ KVHashtable(int table_size) : BasicHashtable(table_size, sizeof(KVHashtableEntry)) {} ++ ++ V* add(K key, V value) { ++ unsigned int hash = HASH(key); ++ KVHashtableEntry* entry = new_entry(hash, key, value); ++ BasicHashtable::add_entry(BasicHashtable::hash_to_index(hash), entry); ++ return &(entry->_value); ++ } ++ ++ V* lookup(K key) const { ++ unsigned int hash = HASH(key); ++ int index = BasicHashtable::hash_to_index(hash); ++ for (KVHashtableEntry* e = bucket(index); e != NULL; e = e->next()) { ++ if (e->hash() == hash && EQUALS(e->_key, key)) { ++ return &(e->_value); ++ } ++ } ++ return NULL; ++ } ++ ++ // Look up the key. ++ // If an entry for the key exists, leave map unchanged and return a pointer to its value. ++ // If no entry for the key exists, create a new entry from key and value and return a ++ // pointer to the value. ++ // *p_created is true if entry was created, false if entry pre-existed. ++ V* add_if_absent(K key, V value, bool* p_created) { ++ unsigned int hash = HASH(key); ++ int index = BasicHashtable::hash_to_index(hash); ++ for (KVHashtableEntry* e = bucket(index); e != NULL; e = e->next()) { ++ if (e->hash() == hash && EQUALS(e->_key, key)) { ++ *p_created = false; ++ return &(e->_value); ++ } ++ } ++ KVHashtableEntry* entry = new_entry(hash, key, value); ++ BasicHashtable::add_entry(BasicHashtable::hash_to_index(hash), entry); ++ *p_created = true; ++ return &(entry->_value); ++ } ++ ++ int table_size() const { ++ return BasicHashtable::table_size(); ++ } ++ ++ // ITER contains bool do_entry(K, V const&), which will be ++ // called for each entry in the table. If do_entry() returns false, ++ // the iteration is cancelled. ++ template ++ void iterate(ITER* iter) const { ++ for (int index = 0; index < table_size(); index++) { ++ for (KVHashtableEntry* e = bucket(index); e != NULL; e = e->next()) { ++ bool cont = iter->do_entry(e->_key, &e->_value); ++ if (!cont) { return; } ++ } ++ } ++ } ++}; ++ + #endif // SHARE_VM_UTILITIES_HASHTABLE_HPP +diff --git a/hotspot/src/share/vm/utilities/hashtable.inline.hpp b/hotspot/src/share/vm/utilities/hashtable.inline.hpp +index 9356c985e..ee22ba835 100644 +--- a/hotspot/src/share/vm/utilities/hashtable.inline.hpp ++++ b/hotspot/src/share/vm/utilities/hashtable.inline.hpp +@@ -72,7 +72,7 @@ template inline void BasicHashtable::initialize(int table_size, + + + // The following method is MT-safe and may be used with caution. +-template inline BasicHashtableEntry* BasicHashtable::bucket(int i) { ++template inline BasicHashtableEntry* BasicHashtable::bucket(int i) const { + return _buckets[i].get_entry(); + } + +diff --git a/hotspot/src/share/vm/utilities/ostream.cpp b/hotspot/src/share/vm/utilities/ostream.cpp +index fa199a235..14d82ad0f 100644 +--- a/hotspot/src/share/vm/utilities/ostream.cpp ++++ b/hotspot/src/share/vm/utilities/ostream.cpp +@@ -379,6 +379,7 @@ xmlStream* xtty; + outputStream* tty; + outputStream* gclog_or_tty; + CDS_ONLY(jsaFileStream* classlist_file;) // Only dump the classes that can be stored into the CDS archive ++CDS_ONLY(outputStream* dynamic_cds_log;) + extern Mutex* tty_lock; + + #define EXTRACHARLEN 32 +@@ -1402,6 +1403,16 @@ void ostream_init_log() { + jsaFileStream(list_name); + FREE_C_HEAP_ARRAY(char, list_name, mtInternal); + } ++ ++ // For -XX:DynamicCDSLog= option ++ if (DynamicCDSLog != NULL) { ++ const char* log_name = make_log_name(DynamicCDSLog, NULL); ++ dynamic_cds_log = new(ResourceObj::C_HEAP, mtInternal) ++ fileStream(log_name); ++ FREE_C_HEAP_ARRAY(char, log_name, mtInternal); ++ } else { ++ dynamic_cds_log = tty; ++ } + #endif + + // If we haven't lazily initialized the logfile yet, do it now, +diff --git a/hotspot/src/share/vm/utilities/ostream.hpp b/hotspot/src/share/vm/utilities/ostream.hpp +index c69289fb5..d0f9aac57 100644 +--- a/hotspot/src/share/vm/utilities/ostream.hpp ++++ b/hotspot/src/share/vm/utilities/ostream.hpp +@@ -221,7 +221,7 @@ class jsaFileStream : public fileStream { + }; + + CDS_ONLY(extern jsaFileStream* classlist_file;) +- ++CDS_ONLY(extern outputStream* dynamic_cds_log;) + // unlike fileStream, fdStream does unbuffered I/O by calling + // open() and write() directly. It is async-safe, but output + // from multiple thread may be mixed together. Used by fatal +diff --git a/hotspot/src/share/vm/utilities/resourceHash.hpp b/hotspot/src/share/vm/utilities/resourceHash.hpp +index 82c1219b4..941f25996 100644 +--- a/hotspot/src/share/vm/utilities/resourceHash.hpp ++++ b/hotspot/src/share/vm/utilities/resourceHash.hpp +@@ -27,21 +27,13 @@ + + #include "memory/allocation.hpp" + #include "utilities/top.hpp" ++#include "utilities/globalDefinitions.hpp" + + template struct ResourceHashtableFns { + typedef unsigned (*hash_fn)(K const&); + typedef bool (*equals_fn)(K const&, K const&); + }; + +-template unsigned primitive_hash(const K& k) { +- unsigned hash = (unsigned)((uintptr_t)k); +- return hash ^ (hash >> 3); // just in case we're dealing with aligned ptrs +-} +- +-template bool primitive_equals(const K& k0, const K& k1) { +- return k0 == k1; +-} +- + template< + typename K, typename V, + // xlC does not compile this: +@@ -66,6 +58,10 @@ class ResourceHashtable : public ResourceObj { + + Node(unsigned hash, K const& key, V const& value) : + _hash(hash), _key(key), _value(value), _next(NULL) {} ++ ++ // Create a node with a default-constructed value. ++ Node(unsigned hash, K const& key) : ++ _hash(hash), _key(key), _value(), _next(NULL) {} + }; + + Node* _table[SIZE]; +@@ -139,6 +135,19 @@ class ResourceHashtable : public ResourceObj { + } + } + ++ V* put_if_absent(K const& key, bool* p_created) { ++ unsigned hv = HASH(key); ++ Node** ptr = lookup_node(hv, key); ++ if (*ptr == NULL) { ++ *ptr = new (ALLOC_TYPE, MEM_TYPE) Node(hv, key); ++ *p_created = true; ++ } else { ++ *p_created = false; ++ } ++ return &(*ptr)->_value; ++ } ++ ++ + bool remove(K const& key) { + unsigned hv = HASH(key); + Node** ptr = lookup_node(hv, key); +-- +2.17.1 + diff --git a/Fix-compile-and-runtime-failures-for-minimal1-versio.patch b/Fix-compile-and-runtime-failures-for-minimal1-versio.patch index 0cdb49e5e6882372094b8bf3eed7d204a90150f6..8733ee419cf43981d1e493966c77ba9783552672 100644 --- a/Fix-compile-and-runtime-failures-for-minimal1-versio.patch +++ b/Fix-compile-and-runtime-failures-for-minimal1-versio.patch @@ -116,34 +116,6 @@ index c27a534ef..f75501dba 100644 JVM_END JVM_ENTRY(void, JVM_Yield(JNIEnv *env, jclass threadClass)) -diff --git a/hotspot/src/share/vm/prims/jvmtiImpl.hpp b/hotspot/src/share/vm/prims/jvmtiImpl.hpp -index d74789451..ec721ca20 100644 ---- a/hotspot/src/share/vm/prims/jvmtiImpl.hpp -+++ b/hotspot/src/share/vm/prims/jvmtiImpl.hpp -@@ -493,9 +493,9 @@ class JvmtiDeferredEvent VALUE_OBJ_CLASS_SPEC { - // Actually posts the event. - void post() NOT_JVMTI_RETURN; - // Sweeper support to keep nmethods from being zombied while in the queue. -- void nmethods_do(CodeBlobClosure* cf); -+ void nmethods_do(CodeBlobClosure* cf) NOT_JVMTI_RETURN; - // GC support to keep nmethod from being unloaded while in the queue. -- void oops_do(OopClosure* f, CodeBlobClosure* cf); -+ void oops_do(OopClosure* f, CodeBlobClosure* cf) NOT_JVMTI_RETURN; - }; - - /** -@@ -534,9 +534,9 @@ class JvmtiDeferredEventQueue : AllStatic { - static void enqueue(const JvmtiDeferredEvent& event) NOT_JVMTI_RETURN; - static JvmtiDeferredEvent dequeue() NOT_JVMTI_RETURN_(JvmtiDeferredEvent()); - // Sweeper support to keep nmethods from being zombied while in the queue. -- static void nmethods_do(CodeBlobClosure* cf); -+ static void nmethods_do(CodeBlobClosure* cf) NOT_JVMTI_RETURN; - // GC support to keep nmethod from being unloaded while in the queue. -- static void oops_do(OopClosure* f, CodeBlobClosure* cf); -+ static void oops_do(OopClosure* f, CodeBlobClosure* cf) NOT_JVMTI_RETURN; - - // Used to enqueue events without using a lock, for times (such as during - // safepoint) when we can't or don't want to lock the Service_lock. diff --git a/hotspot/src/share/vm/runtime/memprofiler.cpp b/hotspot/src/share/vm/runtime/memprofiler.cpp index ddb22601f..a956c5252 100644 --- a/hotspot/src/share/vm/runtime/memprofiler.cpp diff --git a/improve_algorithmConstraints_checkAlgorithm_performance.patch b/Improve_AlgorithmConstraints_checkAlgorithm_performance.patch old mode 100755 new mode 100644 similarity index 90% rename from improve_algorithmConstraints_checkAlgorithm_performance.patch rename to Improve_AlgorithmConstraints_checkAlgorithm_performance.patch index 025a379bf25ec534b537a9b40c35897e2b2343b9..cf5c5e4aec61758983d6babbaea063ed07d2f9ae --- a/improve_algorithmConstraints_checkAlgorithm_performance.patch +++ b/Improve_AlgorithmConstraints_checkAlgorithm_performance.patch @@ -1,5 +1,17 @@ +From 4e520a51acbb192a0df844fcca247998d7fb8854 Mon Sep 17 00:00:00 2001 +From: wangkun +Date: Thu, 28 Jul 2022 17:19:32 +0800 +Subject: [PATCH 2/3] add + Improve-AlgorithmConstraints-checkAlgorithm-performa.patch + +--- + .../util/AbstractAlgorithmConstraints.java | 30 +++++++------------ + .../util/DisabledAlgorithmConstraints.java | 20 +++++++++---- + .../util/LegacyAlgorithmConstraints.java | 12 ++++++-- + 3 files changed, 35 insertions(+), 27 deletions(-) + diff --git a/jdk/src/share/classes/sun/security/util/AbstractAlgorithmConstraints.java b/jdk/src/share/classes/sun/security/util/AbstractAlgorithmConstraints.java -index 944958de4..5c7602925 100644 +index 944958de..5c760292 100644 --- a/jdk/src/share/classes/sun/security/util/AbstractAlgorithmConstraints.java +++ b/jdk/src/share/classes/sun/security/util/AbstractAlgorithmConstraints.java @@ -77,34 +77,26 @@ public abstract class AbstractAlgorithmConstraints @@ -49,7 +61,7 @@ index 944958de4..5c7602925 100644 return true; diff --git a/jdk/src/share/classes/sun/security/util/DisabledAlgorithmConstraints.java b/jdk/src/share/classes/sun/security/util/DisabledAlgorithmConstraints.java -index 51e625632..6ff26bf2f 100644 +index 51e62563..6ff26bf2 100644 --- a/jdk/src/share/classes/sun/security/util/DisabledAlgorithmConstraints.java +++ b/jdk/src/share/classes/sun/security/util/DisabledAlgorithmConstraints.java @@ -96,7 +96,7 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints { @@ -99,7 +111,7 @@ index 51e625632..6ff26bf2f 100644 /* diff --git a/jdk/src/share/classes/sun/security/util/LegacyAlgorithmConstraints.java b/jdk/src/share/classes/sun/security/util/LegacyAlgorithmConstraints.java -index 4e7502fb5..01d0447ab 100644 +index 4e7502fb..01d0447a 100644 --- a/jdk/src/share/classes/sun/security/util/LegacyAlgorithmConstraints.java +++ b/jdk/src/share/classes/sun/security/util/LegacyAlgorithmConstraints.java @@ -28,6 +28,7 @@ package sun.security.util; @@ -132,3 +144,6 @@ index 4e7502fb5..01d0447ab 100644 } @Override +-- +2.22.0 + diff --git a/The-code-style-is-fixed-and-test-cases-are-added.patch b/The-code-style-is-fixed-and-test-cases-are-added.patch new file mode 100644 index 0000000000000000000000000000000000000000..9219f3dddf289dd403c4ed554b9269d2494aa417 --- /dev/null +++ b/The-code-style-is-fixed-and-test-cases-are-added.patch @@ -0,0 +1,529 @@ +From 0a877e963eeb55b98dcd0194ac44b4f010d382eb Mon Sep 17 00:00:00 2001 +Date: Wed, 21 Sep 2022 09:54:56 +0800 +Subject: The code style is fixed and test cases are added + +--- + hotspot/src/share/vm/cds/archiveBuilder.hpp | 1 - + hotspot/src/share/vm/cds/archiveUtils.hpp | 1 - + hotspot/src/share/vm/cds/dynamicArchive.hpp | 1 - + .../share/vm/classfile/systemDictionary.cpp | 2 +- + .../vm/classfile/systemDictionaryShared.hpp | 1 + + .../shared/parGCAllocBuffer.cpp | 6 +- + hotspot/src/share/vm/memory/filemap.cpp | 7 +- + hotspot/src/share/vm/memory/filemap.hpp | 2 +- + .../src/share/vm/memory/metaspaceClosure.cpp | 25 + + .../src/share/vm/memory/metaspaceClosure.hpp | 25 +- + hotspot/src/share/vm/oops/cpCache.cpp | 1 - + hotspot/src/share/vm/oops/instanceKlass.cpp | 4 - + hotspot/test/runtime/6929067/Test6929067.sh | 2 +- + .../runtime/InitialThreadOverflow/testme.sh | 2 +- + hotspot/test/runtime/Thread/StopAtExit.java | 119 ++++ + jdk/make/profile-rtjar-includes.txt | 7 +- + .../classes/java/io/ObjectInputStream.java | 4 +- + .../classes/java/io/ObjectOutputStream.java | 10 +- + .../classes/sun/awt/FontConfiguration.java | 7 +- + .../security/openssl/kae_cipher_rsa.c | 3 +- + .../security/openssl/kae_keyagreement_dh.c | 4 +- + .../openssl/RSAKeyPairGeneratorBenchmark.java | 2 +- + 23 files changed, 194 insertions(+), 668 deletions(-) + create mode 100644 hotspot/test/runtime/Thread/StopAtExit.java + +diff --git a/hotspot/src/share/vm/cds/archiveBuilder.hpp b/hotspot/src/share/vm/cds/archiveBuilder.hpp +index f7a5c107..93c0e245 100644 +--- a/hotspot/src/share/vm/cds/archiveBuilder.hpp ++++ b/hotspot/src/share/vm/cds/archiveBuilder.hpp +@@ -29,7 +29,6 @@ + #include "cds/archiveUtils.hpp" + #include "cds/dumpAllocStats.hpp" + #include "memory/metaspaceClosure.hpp" +-//#include "oops/array.hpp" + #include "oops/klass.hpp" + #include "runtime/os.hpp" + #include "utilities/align.hpp" +diff --git a/hotspot/src/share/vm/cds/archiveUtils.hpp b/hotspot/src/share/vm/cds/archiveUtils.hpp +index 55c2431a..44f03c8e 100644 +--- a/hotspot/src/share/vm/cds/archiveUtils.hpp ++++ b/hotspot/src/share/vm/cds/archiveUtils.hpp +@@ -133,7 +133,6 @@ public: + _dump_region->append_intptr_t((intptr_t)tag); + } + +- //void do_oop(oop* o); + void do_region(u_char* start, size_t size); + bool reading() const { return false; } + }; +diff --git a/hotspot/src/share/vm/cds/dynamicArchive.hpp b/hotspot/src/share/vm/cds/dynamicArchive.hpp +index 1d5b7122..0e068e65 100644 +--- a/hotspot/src/share/vm/cds/dynamicArchive.hpp ++++ b/hotspot/src/share/vm/cds/dynamicArchive.hpp +@@ -26,7 +26,6 @@ + #ifndef SHARE_VM_CDS_DYNAMICARCHIVE_HPP + #define SHARE_VM_CDS_DYNAMICARCHIVE_HPP + +-//#include "classfile/compactHashtable.hpp" + #include "memory/allocation.hpp" + #include "memory/filemap.hpp" + #include "memory/memRegion.hpp" +diff --git a/hotspot/src/share/vm/classfile/systemDictionary.cpp b/hotspot/src/share/vm/classfile/systemDictionary.cpp +index 2eebdbac..0ea2d9b7 100644 +--- a/hotspot/src/share/vm/classfile/systemDictionary.cpp ++++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp +@@ -1344,7 +1344,7 @@ instanceKlassHandle SystemDictionary::load_shared_class( + Handle klass_name = java_lang_String::create_from_str(name, CHECK_0); + JavaValue result(T_OBJECT); + +- // load_shared_class need protected domain to handle non-bootstrap loaded class, ++ // load_shared_class need protected domain to handle non-bootstrap loaded class, + // so here call_virtual to call getProtectionDomainInternal function of URLClassLoader.java, + // to get protected domain and save into result. + JavaCalls::call_virtual(&result, +diff --git a/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp b/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp +index 36423bee..fb9583d4 100644 +--- a/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp ++++ b/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp +@@ -194,6 +194,7 @@ public: + } + return true; + } ++ + static size_t estimate_size_for_archive(); + static void write_to_archive(); + static void write_dictionary(RunTimeSharedDictionary* dictionary, bool is_builtin); +diff --git a/hotspot/src/share/vm/gc_implementation/shared/parGCAllocBuffer.cpp b/hotspot/src/share/vm/gc_implementation/shared/parGCAllocBuffer.cpp +index bddf14b6..0244bf84 100644 +--- a/hotspot/src/share/vm/gc_implementation/shared/parGCAllocBuffer.cpp ++++ b/hotspot/src/share/vm/gc_implementation/shared/parGCAllocBuffer.cpp +@@ -98,9 +98,9 @@ void PLABStats::adjust_desired_plab_sz(uint no_of_gc_workers) { + if (_allocated == 0) { + assert(_unused == 0, + err_msg("Inconsistency in PLAB stats: " +- "_allocated: "SIZE_FORMAT", " +- "_wasted: "SIZE_FORMAT", " +- "_unused: "SIZE_FORMAT", " ++ "_allocated: " SIZE_FORMAT ", " ++ "_wasted: " SIZE_FORMAT ", " ++ "_unused: " SIZE_FORMAT ", " + "_used : "SIZE_FORMAT, + _allocated, _wasted, _unused, _used)); + +diff --git a/hotspot/src/share/vm/memory/filemap.cpp b/hotspot/src/share/vm/memory/filemap.cpp +index 1891fc80..0682cd67 100644 +--- a/hotspot/src/share/vm/memory/filemap.cpp ++++ b/hotspot/src/share/vm/memory/filemap.cpp +@@ -240,12 +240,7 @@ void FileMapInfo::FileMapHeader::populate(FileMapInfo* mapinfo, size_t alignment + _version = current_version(); + _alignment = alignment; + _obj_alignment = ObjectAlignmentInBytes; +- /* TODO +- _compressed_oops = UseCompressedOops; +- _compressed_class_ptrs = UseCompressedClassPointers; +- _max_heap_size = MaxHeapSize; +- _narrow_klass_shift = CompressedKlassPointers::shift(); +- */ ++ + if (!DynamicDumpSharedSpaces) { + _classpath_entry_table_size = mapinfo->_classpath_entry_table_size; + _classpath_entry_table = mapinfo->_classpath_entry_table; +diff --git a/hotspot/src/share/vm/memory/filemap.hpp b/hotspot/src/share/vm/memory/filemap.hpp +index 36b27f13..27fff35e 100644 +--- a/hotspot/src/share/vm/memory/filemap.hpp ++++ b/hotspot/src/share/vm/memory/filemap.hpp +@@ -232,7 +232,7 @@ public: + char* region_end(int i) { return region_base(i) + used_aligned(i); } + struct FileMapHeader* header() { return _header; } + struct DynamicArchiveHeader* dynamic_header() { +- // assert(!is_static(), "must be"); ++ + return (struct DynamicArchiveHeader*)header(); + } + +diff --git a/hotspot/src/share/vm/memory/metaspaceClosure.cpp b/hotspot/src/share/vm/memory/metaspaceClosure.cpp +index 00ec8fce..e19402cb 100644 +--- a/hotspot/src/share/vm/memory/metaspaceClosure.cpp ++++ b/hotspot/src/share/vm/memory/metaspaceClosure.cpp +@@ -1,3 +1,28 @@ ++/* ++ * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ + #include "precompiled.hpp" + #include "memory/metaspaceClosure.hpp" + +diff --git a/hotspot/src/share/vm/memory/metaspaceClosure.hpp b/hotspot/src/share/vm/memory/metaspaceClosure.hpp +index f67d8d6f..5422e2a0 100644 +--- a/hotspot/src/share/vm/memory/metaspaceClosure.hpp ++++ b/hotspot/src/share/vm/memory/metaspaceClosure.hpp +@@ -1,4 +1,27 @@ +- ++/* ++ * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ + + #ifndef SHARE_VM_MEMORY_METASPACECLOSURE_HPP + #define SHARE_VM_MEMORY_METASPACECLOSURE_HPP +diff --git a/hotspot/src/share/vm/oops/cpCache.cpp b/hotspot/src/share/vm/oops/cpCache.cpp +index 51f5397b..874cef41 100644 +--- a/hotspot/src/share/vm/oops/cpCache.cpp ++++ b/hotspot/src/share/vm/oops/cpCache.cpp +@@ -610,7 +610,6 @@ void ConstantPoolCache::metaspace_pointers_do(MetaspaceClosure* it) { + dynamic_cds_log->print_cr("Iter(ConstantPoolCache): %p", this); + } + it->push(&_constant_pool); +- // it->push(&_reference_map); + } + + void ConstantPoolCache::remove_unshareable_info() { +diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp +index 9276b895..2a9cd92d 100644 +--- a/hotspot/src/share/vm/oops/instanceKlass.cpp ++++ b/hotspot/src/share/vm/oops/instanceKlass.cpp +@@ -526,10 +526,6 @@ void InstanceKlass::metaspace_pointers_do(MetaspaceClosure* it) { + } + } + } +- +- // it->push(&_nest_members); +- // it->push(&_permitted_subclasses); +- // it->push(&_record_components); + } + + klassVtable* InstanceKlass::vtable() const { +diff --git a/hotspot/test/runtime/6929067/Test6929067.sh b/hotspot/test/runtime/6929067/Test6929067.sh +index 438a287c..c78e1787 100644 +--- a/hotspot/test/runtime/6929067/Test6929067.sh ++++ b/hotspot/test/runtime/6929067/Test6929067.sh +@@ -102,7 +102,7 @@ esac + + + if [ "${VM_CPU}" == "aarch64" ]; then +- COMP_FLAG="-mabi=lp64" ++ COMP_FLAG="" + fi + + # VM type: need to know server or client +diff --git a/hotspot/test/runtime/InitialThreadOverflow/testme.sh b/hotspot/test/runtime/InitialThreadOverflow/testme.sh +index ffd7d6e3..cf48c2fe 100644 +--- a/hotspot/test/runtime/InitialThreadOverflow/testme.sh ++++ b/hotspot/test/runtime/InitialThreadOverflow/testme.sh +@@ -52,7 +52,7 @@ fi + CFLAGS="-m${VM_BITS}" + + if [ "${VM_CPU}" == "aarch64" ]; then +- CFLAGS="-mabi=lp64" ++ CFLAGS="" + fi + + LD_LIBRARY_PATH=.:${COMPILEJAVA}/jre/lib/${VM_CPU}/${VM_TYPE}:/usr/lib:$LD_LIBRARY_PATH +diff --git a/hotspot/test/runtime/Thread/StopAtExit.java b/hotspot/test/runtime/Thread/StopAtExit.java +new file mode 100644 +index 00000000..8d6344a6 +--- /dev/null ++++ b/hotspot/test/runtime/Thread/StopAtExit.java +@@ -0,0 +1,119 @@ ++/* ++ * Copyright (c) 2017, 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++/** ++ * @test ++ * @bug 8167108 ++ * @summary Stress test java.lang.Thread.stop() at thread exit. ++ * @run main/othervm StopAtExit ++ */ ++ ++import java.util.concurrent.CountDownLatch; ++import java.util.concurrent.TimeUnit; ++ ++public class StopAtExit extends Thread { ++ final static int N_THREADS = 32; ++ final static int N_LATE_CALLS = 1000; ++ ++ public CountDownLatch exitSyncObj = new CountDownLatch(1); ++ public CountDownLatch startSyncObj = new CountDownLatch(1); ++ ++ @Override ++ public void run() { ++ try { ++ // Tell main thread we have started. ++ startSyncObj.countDown(); ++ try { ++ // Wait for main thread to interrupt us so we ++ // can race to exit. ++ exitSyncObj.await(); ++ } catch (InterruptedException e) { ++ // ignore because we expect one ++ } ++ } catch (ThreadDeath td) { ++ // ignore because we're testing Thread.stop() which throws it ++ } catch (NoClassDefFoundError ncdfe) { ++ // ignore because we're testing Thread.stop() which can cause it ++ } ++ } ++ ++ public static void main(String[] args) { ++ StopAtExit threads[] = new StopAtExit[N_THREADS]; ++ ++ for (int i = 0; i < N_THREADS; i++ ) { ++ threads[i] = new StopAtExit(); ++ int late_count = 1; ++ threads[i].start(); ++ try { ++ // Wait for the worker thread to get going. ++ threads[i].startSyncObj.await(); ++ ++ // This interrupt() call will break the worker out ++ // of the exitSyncObj.await() call and the stop() ++ // calls will come in during thread exit. ++ threads[i].interrupt(); ++ for (; late_count <= N_LATE_CALLS; late_count++) { ++ threads[i].stop(); ++ ++ if (!threads[i].isAlive()) { ++ // Done with Thread.stop() calls since ++ // thread is not alive. ++ break; ++ } ++ } ++ } catch (InterruptedException e) { ++ throw new Error("Unexpected: " + e); ++ } catch (NoClassDefFoundError ncdfe) { ++ // Ignore because we're testing Thread.stop() which can ++ // cause it. Yes, a NoClassDefFoundError that happens ++ // in a worker thread can subsequently be seen in the ++ // main thread. ++ } ++ ++ System.out.println("INFO: thread #" + i + ": made " + late_count + ++ " late calls to java.lang.Thread.stop()"); ++ System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" + ++ N_LATE_CALLS + " value is " + ++ ((late_count >= N_LATE_CALLS) ? "NOT " : "") + ++ "large enough to cause a Thread.stop() " + ++ "call after thread exit."); ++ ++ try { ++ threads[i].join(); ++ } catch (InterruptedException e) { ++ throw new Error("Unexpected: " + e); ++ } ++ threads[i].stop(); ++ if (threads[i].isAlive()) { ++ throw new Error("Expected !Thread.isAlive() after thread #" + ++ i + " has been join()'ed"); ++ } ++ } ++ ++ String cmd = System.getProperty("sun.java.command"); ++ if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) { ++ // Exit with success in a non-JavaTest environment: ++ System.exit(0); ++ } ++ } ++} +diff --git a/jdk/make/profile-rtjar-includes.txt b/jdk/make/profile-rtjar-includes.txt +index f36d1d5c..dd275590 100644 +--- a/jdk/make/profile-rtjar-includes.txt ++++ b/jdk/make/profile-rtjar-includes.txt +@@ -73,8 +73,8 @@ PROFILE_1_RTJAR_INCLUDE_PACKAGES := \ + + PROFILE_1_RTJAR_INCLUDE_TYPES := + +-PROFILE_1_RTJAR_EXCLUDE_TYPES := +- ++PROFILE_1_RTJAR_EXCLUDE_TYPES := \ ++ com/huawei + PROFILE_1_INCLUDE_METAINF_SERVICES := + + +@@ -99,7 +99,8 @@ PROFILE_2_RTJAR_INCLUDE_PACKAGES := \ + + PROFILE_2_RTJAR_INCLUDE_TYPES := + +-PROFILE_2_RTJAR_EXCLUDE_TYPES := ++PROFILE_2_RTJAR_EXCLUDE_TYPES := \ ++ com/huawei + + PROFILE_2_INCLUDE_METAINF_SERVICES := \ + META-INF/services/sun.util.spi.XmlPropertiesProvider +diff --git a/jdk/src/share/classes/java/io/ObjectInputStream.java b/jdk/src/share/classes/java/io/ObjectInputStream.java +index af6c5dd6..85e3958b 100644 +--- a/jdk/src/share/classes/java/io/ObjectInputStream.java ++++ b/jdk/src/share/classes/java/io/ObjectInputStream.java +@@ -768,7 +768,7 @@ public class ObjectInputStream + * Cache the class meta during serialization. + * Only used in FastSerilizer. + */ +- protected static ConcurrentHashMap> nameToClass = new ConcurrentHashMap<>(); ++ private static ConcurrentHashMap> nameToClass = new ConcurrentHashMap<>(); + + /** + * Load the local class equivalent of the specified stream class +@@ -1013,7 +1013,7 @@ public class ObjectInputStream + + if (s0 != STREAM_MAGIC) { + throw new StreamCorruptedException( +- String.format("invalid stream header: %04X%04X, and FastSerializer is activated", s0, s1)); ++ String.format("invalid stream header: %04X%04X", s0, s1)); + } + + if (!fastSerializerEscapeMode) { +diff --git a/jdk/src/share/classes/java/io/ObjectOutputStream.java b/jdk/src/share/classes/java/io/ObjectOutputStream.java +index 840f7fdc..23c1fff5 100644 +--- a/jdk/src/share/classes/java/io/ObjectOutputStream.java ++++ b/jdk/src/share/classes/java/io/ObjectOutputStream.java +@@ -234,11 +234,6 @@ public class ObjectOutputStream + new sun.security.action.GetBooleanAction( + "sun.io.serialization.extendedDebugInfo")).booleanValue(); + +- /** +- * Magic number that is written to the stream header when using fastserilizer. +- */ +- private static final short STREAM_MAGIC_FAST = (short)0xdeca; +- + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + /** +@@ -255,6 +250,11 @@ public class ObjectOutputStream + new sun.security.action.GetBooleanAction( + "printFastSerializer")).booleanValue(); + ++ /** ++ * Magic number that is written to the stream header when using fastserilizer. ++ */ ++ private static final short STREAM_MAGIC_FAST = (short)0xdeca; ++ + /** + * Creates an ObjectOutputStream that writes to the specified OutputStream. + * This constructor writes the serialization stream header to the +diff --git a/jdk/src/share/classes/sun/awt/FontConfiguration.java b/jdk/src/share/classes/sun/awt/FontConfiguration.java +index 93e38e06..c2e94d15 100644 +--- a/jdk/src/share/classes/sun/awt/FontConfiguration.java ++++ b/jdk/src/share/classes/sun/awt/FontConfiguration.java +@@ -300,12 +300,7 @@ public abstract class FontConfiguration { + } + } + foundOsSpecificFile = false; +- +- configFile = findImpl(baseName); +- if (configFile != null) { +- return configFile; +- } +- return null; ++ return (configFile = findImpl(baseName)); + } + + /* Initialize the internal data tables from binary format font +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_cipher_rsa.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_cipher_rsa.c +index 73b94cbe..d9b16ab9 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_cipher_rsa.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_cipher_rsa.c +@@ -174,6 +174,7 @@ static int RSACryptOAEPPadding(JNIEnv* env, jlong keyAddress, jint inLen, jbyteA + jbyte* inBytes = NULL; + // outLen type should be size_t + // EVP_PKEY_encrypt takes the outLen address as a parameter, and the parameter type is size_t* ++ // You can refer to the issue #2774 to see more content + size_t outLen = 0; + ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); + KAE_TRACE("RSACryptOAEPPadding: kaeEngine => %p", kaeEngine); +@@ -366,7 +367,7 @@ JNIEXPORT jlong JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeC + } + + // set rsa public key params n and e +- if(RSA_set0_key(rsa, bnN, bnE, NULL) <= 0) { ++ if (RSA_set0_key(rsa, bnN, bnE, NULL) <= 0) { + KAE_ThrowFromOpenssl(env, "RSA_set0_key", KAE_ThrowRuntimeException); + goto cleanup; + } +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_dh.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_dh.c +index 90b33045..d8d2ee7c 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_dh.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_dh.c +@@ -131,9 +131,9 @@ cleanup: + if (g_bn != NULL) + KAE_ReleaseBigNumFromByteArray(g_bn); + if (secret != NULL) +- free(secret); ++ free(secret); + if (computeKeyRetBn != NULL) +- BN_free(computeKeyRetBn); ++ BN_free(computeKeyRetBn); + + return retByteArray; + } +diff --git a/jdk/test/micro/org/openeuler/bench/security/openssl/RSAKeyPairGeneratorBenchmark.java b/jdk/test/micro/org/openeuler/bench/security/openssl/RSAKeyPairGeneratorBenchmark.java +index b1739222..13d3e8cf 100644 +--- a/jdk/test/micro/org/openeuler/bench/security/openssl/RSAKeyPairGeneratorBenchmark.java ++++ b/jdk/test/micro/org/openeuler/bench/security/openssl/RSAKeyPairGeneratorBenchmark.java +@@ -54,7 +54,7 @@ public class RSAKeyPairGeneratorBenchmark extends BenchmarkBase { + public KeyPair generateKeyPair() throws Exception { + return keyPairGenerator.generateKeyPair(); + } +- ++ + private KeyPairGenerator createKeyPairGenerator() throws Exception { + if (prov != null) { + return KeyPairGenerator.getInstance(algorithm, prov); +-- +2.22.0 + diff --git a/add-DumpSharedSpace-guarantee-when-create-anonymous-classes.patch b/add-DumpSharedSpace-guarantee-when-create-anonymous-classes.patch index 85ff56193248923a9450520c194454c86287486c..1a8df78fabbe1177bdc7d95f012ae212dce49756 100644 --- a/add-DumpSharedSpace-guarantee-when-create-anonymous-classes.patch +++ b/add-DumpSharedSpace-guarantee-when-create-anonymous-classes.patch @@ -17,7 +17,7 @@ index f20bf3d2b..3ab82c5c4 100644 + if (DumpSharedSpaces) { + tty->print_cr("failed: must not create anonymous classes when dumping."); -+ JVM_Exit(0); ++ JVM_Halt(0); + } + if (UsePerfData) { diff --git a/add-appcds-file-lock.patch b/add-appcds-file-lock.patch index f422f5469d2bfc53b87b2260b3cbb049f4b3e6f1..b34121e7c043dc09fc893d87ab4fbffb9e2928a5 100644 --- a/add-appcds-file-lock.patch +++ b/add-appcds-file-lock.patch @@ -224,7 +224,7 @@ index 17447587..d2095e63 100644 + tty->print_cr("The lock path is: %s", _appcds_file_lock_path); + tty->print_cr("Failed to create jsa file !\n Please check: \n 1. The directory exists.\n " + "2. You have the permission.\n 3. Make sure no other process using the same lock file.\n"); -+ JVM_Exit(0); ++ JVM_Halt(0); + } + tty->print_cr("You are using file lock %s in concurrent mode", AppCDSLockFile); + } diff --git a/add-configuration-option-of-huawei-internal-version-shown-in-release-file.patch b/add-configuration-option-of-huawei-internal-version-shown-in-release-file.patch new file mode 100644 index 0000000000000000000000000000000000000000..cb2c2625ba5fd3ddb226ba447323b69d9431a2aa --- /dev/null +++ b/add-configuration-option-of-huawei-internal-version-shown-in-release-file.patch @@ -0,0 +1,111 @@ +From 59040a3951dfdf21ba646cc9510739f175751469 Mon Sep 17 00:00:00 2001 +Date: Wed, 21 Sep 2022 09:54:04 +0800 +Subject: [PATCH 2/5] add configuration option of huawei internal version shown in release file + +--- + common/autoconf/generated-configure.sh | 17 +++++++++++++++++ + common/autoconf/jdk-options.m4 | 11 +++++++++++ + common/autoconf/spec.gmk.in | 3 +++ + jdk/make/Images.gmk | 1 + + 4 files changed, 32 insertions(+) + +diff --git a/common/autoconf/generated-configure.sh b/common/autoconf/generated-configure.sh +index 53e6cf18..a6ba1ac9 100644 +--- a/common/autoconf/generated-configure.sh ++++ b/common/autoconf/generated-configure.sh +@@ -831,6 +831,7 @@ COPYRIGHT_YEAR + VENDOR_URL_VM_BUG + VENDOR_URL_BUG + VENDOR_URL ++INTERNAL_VERSION + COMPANY_NAME + MACOSX_BUNDLE_ID_BASE + MACOSX_BUNDLE_NAME_BASE +@@ -1077,6 +1078,7 @@ with_vendor_url + with_vendor_bug_url + with_vendor_vm_bug_url + with_copyright_year ++with_internal_version + with_boot_jdk + with_boot_jdk_jvmargs + with_add_source_root +@@ -1937,6 +1939,9 @@ Optional Packages: + --with-vendor-vm-bug-url + Sets the bug URL which will be displayed when the VM + crashes [not specified] ++ --with-internal-version ++ Sets the internal version which will be ++ displayed in the release file [not specified] + --with-copyright-year Set copyright year value for build [current year] + --with-boot-jdk path to Boot JDK (used to bootstrap build) [probed] + --with-boot-jdk-jvmargs specify JVM arguments to be passed to all +@@ -20301,6 +20306,18 @@ fi + COPYRIGHT_YEAR=`date +'%Y'` + fi + ++# Check whether --with-internal-version was given. ++if test "${with_internal_version+set}" = set; then : ++ withval=$with_internal_version; ++fi ++ ++ if test "x$with_internal_version" = xyes; then ++ as_fn_error $? "--with-internal-version must have a value" "$LINENO" 5 ++ elif ! [[ $with_internal_version =~ ^[[:print:]]*$ ]] ; then ++ as_fn_error $? "--with-internal-version contains non-printing characters: $with_internal_version" "$LINENO" 5 ++ else ++ INTERNAL_VERSION="$with_internal_version" ++ fi + + if test "x$JDK_UPDATE_VERSION" != x; then + JDK_VERSION="${JDK_MAJOR_VERSION}.${JDK_MINOR_VERSION}.${JDK_MICRO_VERSION}_${JDK_UPDATE_VERSION}" +diff --git a/common/autoconf/jdk-options.m4 b/common/autoconf/jdk-options.m4 +index c506086d..b9f25175 100644 +--- a/common/autoconf/jdk-options.m4 ++++ b/common/autoconf/jdk-options.m4 +@@ -627,6 +627,17 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_JDK_VERSION_NUMBERS], + fi + AC_SUBST(COPYRIGHT_YEAR) + ++ AC_ARG_WITH(internal-version, [AS_HELP_STRING([--with-internal-version], ++ [Sets the internal version which will be displayed in the release file @<:@not specified@:>@])]) ++ if test "x$with_internal_version" = xyes; then ++ AC_MSG_ERROR([--with-internal-version must have a value]) ++ elif [ ! [[ $with_internal_version =~ ^[[:print:]]*$ ]] ]; then ++ AC_MSG_ERROR([--with-internal-version contains non-printing characters: $with_internal_version]) ++ else ++ INTERNAL_VERSION="$with_internal_version" ++ fi ++ AC_SUBST(INTERNAL_VERSION) ++ + if test "x$JDK_UPDATE_VERSION" != x; then + JDK_VERSION="${JDK_MAJOR_VERSION}.${JDK_MINOR_VERSION}.${JDK_MICRO_VERSION}_${JDK_UPDATE_VERSION}" + else +diff --git a/common/autoconf/spec.gmk.in b/common/autoconf/spec.gmk.in +index 79248cbf..ca5e2d74 100644 +--- a/common/autoconf/spec.gmk.in ++++ b/common/autoconf/spec.gmk.in +@@ -162,6 +162,9 @@ VENDOR_URL:=@VENDOR_URL@ + VENDOR_URL_BUG:=@VENDOR_URL_BUG@ + VENDOR_URL_VM_BUG:=@VENDOR_URL_VM_BUG@ + ++# Huawei internal version for use in release file. ++INTERNAL_VERSION:=@INTERNAL_VERSION@ ++ + # Location where build customization files may be found + CUSTOM_MAKE_DIR:=@CUSTOM_MAKE_DIR@ + +diff --git a/jdk/make/Images.gmk b/jdk/make/Images.gmk +index ac39ad33..233ce703 100644 +--- a/jdk/make/Images.gmk ++++ b/jdk/make/Images.gmk +@@ -618,6 +618,7 @@ define create-info-file + $(call info-file-item, "OS_ARCH", "$(OPENJDK_TARGET_CPU_LEGACY)") + if [ -n "$(JDK_ARCH_ABI_PROP_NAME)" ]; then $(call info-file-item, "SUN_ARCH_ABI", "$(JDK_ARCH_ABI_PROP_NAME)"); fi + $(call info-file-item, "SOURCE", "$(strip $(SOURCE_REVISION))") ++ if [ -n "$(INTERNAL_VERSION)" ]; then $(call info-file-item, "INTERNAL_VERSION", "$(INTERNAL_VERSION)"); fi + endef + + SOURCE_REVISION = $(shell \ +-- +2.22.0 + diff --git a/add-missing-test-case.patch b/add-missing-test-case.patch index d17f6cacfb0e60c2a784c63146be16169422a765..ef14a15d5ccea62fb9bef0a245a41ce851f6d82e 100644 --- a/add-missing-test-case.patch +++ b/add-missing-test-case.patch @@ -125,7 +125,7 @@ index 00000000..9b614024 --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ -+8.332.9.0.13 ++8.352.8.0.13 -- 2.23.0 diff --git a/dynamic-cds-_header-and-_fd-handles-are-not-free.patch b/dynamic-cds-_header-and-_fd-handles-are-not-free.patch new file mode 100644 index 0000000000000000000000000000000000000000..e11210e9af5b4197ea704838d66ba9685d264177 --- /dev/null +++ b/dynamic-cds-_header-and-_fd-handles-are-not-free.patch @@ -0,0 +1,34 @@ +From cf12a2fae11baf41773308a48d9cfad9031f5344 Mon Sep 17 00:00:00 2001 +Date: Fri, 9 Sep 2022 11:26:22 +0800 +Subject: dynamic cds _header and _fd handles are not free. + +--- + hotspot/src/share/vm/memory/filemap.cpp | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/hotspot/src/share/vm/memory/filemap.cpp b/hotspot/src/share/vm/memory/filemap.cpp +index adb043f77..6549828e3 100644 +--- a/hotspot/src/share/vm/memory/filemap.cpp ++++ b/hotspot/src/share/vm/memory/filemap.cpp +@@ -169,6 +169,18 @@ FileMapInfo::~FileMapInfo() { + assert(_dynamic_archive_info == this, "must be singleton"); // not thread safe + _dynamic_archive_info = NULL; + } ++ ++ if (_header != NULL) { ++ delete _header; ++ } ++ ++ if (_file_open) { ++ if (::close(_fd) < 0) { ++ fail_stop("Unable to close the shared archive file."); ++ } ++ _file_open = false; ++ _fd = -1; ++ } + } + + void FileMapInfo::populate_header(size_t alignment) { +-- +2.22.0 + diff --git a/fix-appcds-s-option-AppCDSLockFile.patch b/fix-appcds-s-option-AppCDSLockFile.patch index 37eae2257c45f2bcc66a120258c935f344c009cd..88b6f4df2562877f78cb56b6e0b43672a8a3db71 100755 --- a/fix-appcds-s-option-AppCDSLockFile.patch +++ b/fix-appcds-s-option-AppCDSLockFile.patch @@ -33,7 +33,7 @@ index 5858c9355..99b1f58d0 100644 - tty->print_cr("The lock path is: %s", _appcds_file_lock_path); tty->print_cr("Failed to create jsa file !\n Please check: \n 1. The directory exists.\n " "2. You have the permission.\n 3. Make sure no other process using the same lock file.\n"); -- JVM_Exit(0); +- JVM_Halt(0); + fail_stop("Failed to create appcds lock file, the lock path is: %s.", _appcds_file_lock_path); } tty->print_cr("You are using file lock %s in concurrent mode", AppCDSLockFile); diff --git a/fix-dumped-heap-using-jhat-parsing-to-appear-failed-to-resolve-object-id-warning-message.patch b/fix-dumped-heap-using-jhat-parsing-to-appear-failed-to-resolve-object-id-warning-message.patch new file mode 100644 index 0000000000000000000000000000000000000000..f26dd692779fa3a48d12f78555f4ec509acb49d9 --- /dev/null +++ b/fix-dumped-heap-using-jhat-parsing-to-appear-failed-to-resolve-object-id-warning-message.patch @@ -0,0 +1,113 @@ +From 68293d50de005b5982a3cce437fc7af807d7264e Mon Sep 17 00:00:00 2001 +Date: Wed, 14 Sep 2022 14:57:25 +0800 +Subject: fix dumped heap using jhat parsing to appear failed to + resolve object id warning message + +--- + hotspot/src/share/vm/services/heapDumper.cpp | 5 -- + .../serviceability/dcmd/gc/HeapDumpTest.java | 77 +++++++++++++++++++ + 2 files changed, 77 insertions(+), 5 deletions(-) + create mode 100644 jdk/test/serviceability/dcmd/gc/HeapDumpTest.java + +diff --git a/hotspot/src/share/vm/services/heapDumper.cpp b/hotspot/src/share/vm/services/heapDumper.cpp +index f7aba2a84..b5915c412 100644 +--- a/hotspot/src/share/vm/services/heapDumper.cpp ++++ b/hotspot/src/share/vm/services/heapDumper.cpp +@@ -984,11 +984,6 @@ void DumperSupport::dump_class_and_array_classes(DumpWriter* writer, Klass* k) { + return; + } + +- // Ignore the class if it hasn't been initialized yet +- if (!ik->is_linked()) { +- return; +- } +- + writer->write_u1(HPROF_GC_CLASS_DUMP); + + // class ID +diff --git a/jdk/test/serviceability/dcmd/gc/HeapDumpTest.java b/jdk/test/serviceability/dcmd/gc/HeapDumpTest.java +new file mode 100644 +index 000000000..7204c2c37 +--- /dev/null ++++ b/jdk/test/serviceability/dcmd/gc/HeapDumpTest.java +@@ -0,0 +1,77 @@ ++/* ++ * Copyright (c) 2015, 2017, 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++import org.testng.annotations.Test; ++import org.testng.Assert; ++import java.io.File; ++import java.nio.file.Files; ++import java.io.IOException; ++import java.util.List; ++import jdk.test.lib.hprof.HprofParser; ++import jdk.test.lib.hprof.model.Snapshot; ++import jdk.test.lib.JDKToolFinder; ++import jdk.test.lib.process.OutputAnalyzer; ++import jdk.test.lib.dcmd.CommandExecutor; ++import jdk.test.lib.dcmd.PidJcmdExecutor; ++/* ++ * @test ++ * @summary Test of diagnostic command GC.heap_dump ++ * @library /lib ++ * @run testng HeapDumpTest ++ */ ++public class HeapDumpTest { ++ protected String heapDumpArgs = ""; ++ ++ public void run(CommandExecutor executor) throws IOException { ++ File dump = new File("jcmd.gc.heap_dump." + System.currentTimeMillis() + ".hprof"); ++ if (dump.exists()) { ++ dump.delete(); ++ } ++ ++ String cmd = "GC.heap_dump " + heapDumpArgs + " " + dump.getAbsolutePath(); ++ executor.execute(cmd); ++ ++ verifyHeapDump(dump); ++ dump.delete(); ++ } ++ private void verifyHeapDump(File dump) { ++ Assert.assertTrue(dump.exists() && dump.isFile(), "Could not create dump file " + dump.getAbsolutePath()); ++ try { ++ File out = HprofParser.parse(dump); ++ Assert.assertTrue(out != null && out.exists() && out.isFile(), "Could not find hprof parser output file"); ++ List lines = Files.readAllLines(out.toPath()); ++ Assert.assertTrue(lines.size() > 0, "hprof parser output file is empty"); ++ for (String line : lines) { ++ Assert.assertFalse(line.matches(".*WARNING(?!.*Failed to resolve object.*constantPoolOop.*).*")); ++ } ++ out.delete(); ++ } catch (Exception e) { ++ e.printStackTrace(); ++ Assert.fail("Could not parse dump file " + dump.getAbsolutePath()); ++ } ++ } ++ /* GC.heap_dump is not available over JMX, running jcmd pid executor instead */ ++ @Test ++ public void pid() throws IOException { ++ run(new PidJcmdExecutor()); ++ } ++} +-- +2.22.0 + diff --git a/fix-windows-compile-fail.patch b/fix-windows-compile-fail.patch index 196913dee9811bcbe2476244f2780047a6f7afc1..fcc90b7f7151a44431393a9839d168e16d1137e6 100644 --- a/fix-windows-compile-fail.patch +++ b/fix-windows-compile-fail.patch @@ -38,7 +38,7 @@ index 9cfa0451..170f1fd9 100644 if (match_option(option, "-XX:+UseAppCDS", &tail)) { +#ifndef __linux__ + tty->print_cr("failed: must not use AppCDS on non-linux system."); -+ JVM_Exit(0); ++ JVM_Halt(0); +#endif if (!process_argument("+UseAppCDS", args->ignoreUnrecognized, origin)) { return JNI_EINVAL; diff --git a/fix_wrap_memcpy_undefined_gcc10_3.patch b/fix_wrap_memcpy_undefined_gcc10_3.patch new file mode 100644 index 0000000000000000000000000000000000000000..9da21e6117853b5d3a5a8c59bc30eafeb337e9aa --- /dev/null +++ b/fix_wrap_memcpy_undefined_gcc10_3.patch @@ -0,0 +1,291 @@ +diff --git a/jdk/make/CompileDemos.gmk b/jdk/make/CompileDemos.gmk +index 763c968e..6c5eb432 100644 +--- a/jdk/make/CompileDemos.gmk ++++ b/jdk/make/CompileDemos.gmk +@@ -250,7 +250,6 @@ define SetupJVMTIDemo + SRC := $(JDK_TOPDIR)/src/share/demo/jvmti/$1 $$(BUILD_DEMO_JVMTI_$1_EXTRA_SRC), \ + LANG := $$(BUILD_DEMO_JVMTI_$1_LANG), \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CXXFLAGS := $$($1_CXXFLAGS), \ + LDFLAGS := $(filter-out -incremental:no -opt:ref, $$(LDFLAGS_JDKLIB)), \ + LDFLAGS_macosx := $$(call SET_EXECUTABLE_ORIGIN), \ +diff --git a/jdk/make/CompileLaunchers.gmk b/jdk/make/CompileLaunchers.gmk +index 29211f83..2ac718fc 100644 +--- a/jdk/make/CompileLaunchers.gmk ++++ b/jdk/make/CompileLaunchers.gmk +@@ -512,7 +512,6 @@ $(eval $(call SetupNativeCompilation,BUILD_UNPACKEXE, \ + EXCLUDE_FILES := jni.cpp, \ + LANG := $(UNPACKEXE_LANG), \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(UNPACKEXE_CFLAGS) $(CXXFLAGS_JDKEXE) \ + -DFULL, \ + CFLAGS_release := -DPRODUCT, \ +diff --git a/jdk/make/lib/Awt2dLibraries.gmk b/jdk/make/lib/Awt2dLibraries.gmk +index 71d87c37..9368a9d5 100644 +--- a/jdk/make/lib/Awt2dLibraries.gmk ++++ b/jdk/make/lib/Awt2dLibraries.gmk +@@ -52,7 +52,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBMLIB_IMAGE, \ + EXCLUDE_FILES := awt_ImagingLib.c mlib_c_ImageBlendTable.c, \ + LANG := C, \ + OPTIMIZATION := HIGHEST, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) \ + $(BUILD_LIBMLIB_CFLAGS), \ + MAPFILE := $(BUILD_LIBMLIB_IMAGE_MAPFILE), \ +@@ -471,7 +470,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBAWT, \ + INCLUDE_FILES := $(LIBAWT_FILES), \ + LANG := $(LIBAWT_LANG), \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) $(LIBAWT_CFLAGS), \ + ASFLAGS := $(LIBAWT_ASFLAGS), \ + MAPFILE := $(LIBAWT_MAPFILE), \ +@@ -633,7 +631,6 @@ ifeq ($(findstring $(OPENJDK_TARGET_OS),windows macosx),) + INCLUDE_FILES := $(LIBAWT_XAWT_FILES), \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) $(LIBAWT_XAWT_CFLAGS) \ + $(X_CFLAGS), \ + MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libawt_xawt/mapfile-vers, \ +@@ -675,7 +672,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBLCMS, \ + SRC := $(JDK_TOPDIR)/src/share/native/sun/java2d/cmm/lcms, \ + LANG := C, \ + OPTIMIZATION := HIGHEST, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(filter-out -xc99=%none, $(CFLAGS_JDKLIB)) \ + -DCMS_DONT_USE_FAST_FLOOR \ + $(SHARED_LIBRARY_FLAGS) \ +@@ -743,7 +739,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBJPEG, \ + $(JDK_TOPDIR)/src/share/native/sun/awt/image/jpeg, \ + LANG := C, \ + OPTIMIZATION := HIGHEST, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) \ + $(BUILD_LIBJPEG_CLOSED_INCLUDES) \ + -I$(JDK_TOPDIR)/src/share/native/sun/awt/image/jpeg, \ +@@ -919,7 +914,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBFONTMANAGER, \ + EXCLUDE_FILES := $(LIBFONTMANAGER_EXCLUDE_FILES) \ + AccelGlyphCache.c, \ + LANG := C++, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) $(BUILD_LIBFONTMANAGER_CFLAGS_COMMON), \ + CXXFLAGS := $(CXXFLAGS_JDKLIB) $(BUILD_LIBFONTMANAGER_CFLAGS_COMMON), \ + OPTIMIZATION := $(LIBFONTMANAGER_OPTIMIZATION), \ +@@ -1211,7 +1205,6 @@ ifndef BUILD_HEADLESS_ONLY + EXCLUDE_FILES := imageioJPEG.c jpegdecoder.c pngtest.c, \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(LIBSPLASHSCREEN_CFLAGS) $(CFLAGS_JDKLIB) $(GIFLIB_CFLAGS), \ + MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libsplashscreen/mapfile-vers, \ + LDFLAGS := $(LDFLAGS_JDKLIB) \ +diff --git a/jdk/make/lib/CoreLibraries.gmk b/jdk/make/lib/CoreLibraries.gmk +index b444abf9..e43fc2ed 100644 +--- a/jdk/make/lib/CoreLibraries.gmk ++++ b/jdk/make/lib/CoreLibraries.gmk +@@ -113,7 +113,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBVERIFY, \ + INCLUDE_FILES := $(BUILD_LIBVERIFY_SRC), \ + LANG := C, \ + OPTIMIZATION := $(LIBVERIFY_OPTIMIZATION), \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB), \ + MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libverify/mapfile-vers, \ + LDFLAGS := $(LDFLAGS_JDKLIB) \ +@@ -225,7 +224,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBJAVA, \ + EXCLUDE_FILES := $(LIBJAVA_EXCLUDE_FILES), \ + LANG := C, \ + OPTIMIZATION := HIGH, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) \ + $(LIBJAVA_CFLAGS), \ + MAPFILE := $(LIBJAVA_MAPFILE), \ +@@ -287,7 +285,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBZIP, \ + OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + SRC := $(JDK_TOPDIR)/src/share/native/java/util/zip, \ + EXCLUDES := $(LIBZIP_EXCLUDES), \ + CFLAGS := $(CFLAGS_JDKLIB) \ +@@ -329,7 +326,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBUNPACK, \ + EXCLUDE_FILES := main.cpp, \ + LANG := C++, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CXXFLAGS_JDKLIB) \ + -DNO_ZLIB -DUNPACK_JNI -DFULL, \ + CFLAGS_release := -DPRODUCT, \ +@@ -442,7 +438,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBJLI, \ + INCLUDE_FILES := $(BUILD_LIBJLI_FILES), \ + LANG := C, \ + OPTIMIZATION := HIGH, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(LIBJLI_CFLAGS), \ + MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libjli/mapfile-vers, \ + LDFLAGS := $(LDFLAGS_JDKLIB) \ +@@ -544,7 +539,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBNPT, \ + SRC := $(JDK_TOPDIR)/src/share/npt $(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/npt, \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) \ + -I$(JDK_TOPDIR)/src/share/npt \ + -I$(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/npt, \ +diff --git a/jdk/make/lib/NetworkingLibraries.gmk b/jdk/make/lib/NetworkingLibraries.gmk +index f826c66d..347c3237 100644 +--- a/jdk/make/lib/NetworkingLibraries.gmk ++++ b/jdk/make/lib/NetworkingLibraries.gmk +@@ -65,7 +65,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBNET, \ + EXCLUDE_FILES := $(LIBNET_EXCLUDE_FILES), \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) \ + $(LIBNET_CFLAGS), \ + MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libnet/mapfile-vers, \ +diff --git a/jdk/make/lib/NioLibraries.gmk b/jdk/make/lib/NioLibraries.gmk +index 54c9c29e..6c9c46a3 100644 +--- a/jdk/make/lib/NioLibraries.gmk ++++ b/jdk/make/lib/NioLibraries.gmk +@@ -181,7 +181,6 @@ ifeq ($(OPENJDK_TARGET_OS_API), posix) + SRC := $(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/native/sun/nio/ch/sctp, \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) \ + -I$(JDK_TOPDIR)/src/share/native/sun/nio/ch \ + -I$(JDK_TOPDIR)/src/share/native/sun/nio/ch/sctp \ +diff --git a/jdk/make/lib/SecurityLibraries.gmk b/jdk/make/lib/SecurityLibraries.gmk +index 10ab8043..5b9ec17f 100644 +--- a/jdk/make/lib/SecurityLibraries.gmk ++++ b/jdk/make/lib/SecurityLibraries.gmk +@@ -196,7 +196,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBJ2PKCS11, \ + $(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/native/sun/security/pkcs11/wrapper, \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) \ + -I$(JDK_TOPDIR)/src/share/native/sun/security/pkcs11 \ + -I$(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/native/sun/security/pkcs11 \ +@@ -242,7 +241,6 @@ ifeq ($(ENABLE_INTREE_EC), yes) + $(JDK_TOPDIR)/src/share/native/sun/security/ec/impl, \ + LANG := C++, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(filter-out $(ECC_JNI_SOLSPARC_FILTER), $(CFLAGS_JDKLIB)) \ + $(BUILD_LIBSUNEC_FLAGS) \ + -DMP_API_COMPATIBLE -DNSS_ECC_MORE_THAN_SUITE_B, \ +@@ -300,7 +298,6 @@ ifeq ($(ENABLE_KAE), true) + SRC := $(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/native/org/openeuler/security/openssl, \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) \ + -I$(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/native/org/openeuler/security/openssl, \ + MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libj2kae/mapfile-vers, \ +diff --git a/jdk/make/lib/ServiceabilityLibraries.gmk b/jdk/make/lib/ServiceabilityLibraries.gmk +index 2c80ffc0..19c8601d 100644 +--- a/jdk/make/lib/ServiceabilityLibraries.gmk ++++ b/jdk/make/lib/ServiceabilityLibraries.gmk +@@ -83,7 +83,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBDT_SOCKET, \ + $(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/transport/socket, \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) $(CFLAGS_WARNINGS_ARE_ERRORS) -DUSE_MMAP \ + -I$(INCLUDEDIR) -I$(JDK_OUTPUTDIR)/include/$(OPENJDK_TARGET_OS) \ + -I$(JDK_TOPDIR)/src/share/transport/socket \ +@@ -149,7 +148,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBJDWP, \ + SRC := $(JDK_TOPDIR)/src/share/back $(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/back, \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) $(CFLAGS_WARNINGS_ARE_ERRORS) -DJDWP_LOGGING \ + -I$(JDK_TOPDIR)/src/share/transport/export \ + -I$(JDK_TOPDIR)/src/share/back/export \ +@@ -255,7 +253,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBINSTRUMENT, \ + INCLUDE_FILES := $(LIBINSTRUMENT_FILES), \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(LIBINSTRUMENT_CFLAGS) $(CFLAGS_WARNINGS_ARE_ERRORS), \ + CFLAGS_debug := -DJPLIS_LOGGING, \ + CFLAGS_release := -DNO_JPLIS_LOGGING, \ +@@ -379,7 +376,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBHPROF, \ + SRC := $(BUILD_LIBHPROF_SRC), \ + LANG := C, \ + OPTIMIZATION := $(LIBHPROF_OPTIMIZATION), \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) $(CFLAGS_WARNINGS_ARE_ERRORS) \ + $(BUILD_LIBHPROF_CFLAGS), \ + CFLAGS_debug := -DHPROF_LOGGING, \ +@@ -408,7 +404,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBJAVA_CRW_DEMO, \ + SRC := $(JDK_TOPDIR)/src/share/demo/jvmti/java_crw_demo, \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) $(CFLAGS_WARNINGS_ARE_ERRORS) \ + -I$(JDK_TOPDIR)/src/share/demo/jvmti/java_crw_demo, \ + MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libjava_crw_demo/mapfile-vers, \ +diff --git a/jdk/make/lib/SoundLibraries.gmk b/jdk/make/lib/SoundLibraries.gmk +index 0ea9ba84..b59a9462 100644 +--- a/jdk/make/lib/SoundLibraries.gmk ++++ b/jdk/make/lib/SoundLibraries.gmk +@@ -201,7 +201,6 @@ ifneq ($(filter jsoundalsa, $(EXTRA_SOUND_JNI_LIBS)), ) + PLATFORM_API_LinuxOS_ALSA_Ports.c, \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) $(ALSA_CFLAGS) \ + $(LIBJSOUND_CFLAGS) \ + -DUSE_DAUDIO=TRUE \ +diff --git a/make/common/NativeCompilation.gmk b/make/common/NativeCompilation.gmk +index 2d9bdbee..9586d20e 100644 +--- a/make/common/NativeCompilation.gmk ++++ b/make/common/NativeCompilation.gmk +@@ -271,6 +271,7 @@ define SetupNativeCompilation + + # Find all files in the source trees. Sort to remove duplicates. + $1_ALL_SRCS := $$(sort $$(call CacheFind,$$($1_SRC))) ++ + # Extract the C/C++ files. + $1_EXCLUDE_FILES:=$$(foreach i,$$($1_SRC),$$(addprefix $$i/,$$($1_EXCLUDE_FILES))) + $1_INCLUDE_FILES:=$$(foreach i,$$($1_SRC),$$(addprefix $$i/,$$($1_INCLUDE_FILES))) +@@ -281,13 +282,20 @@ define SetupNativeCompilation + ifneq (,$$(strip $$($1_INCLUDE_FILES))) + $1_SRCS := $$(filter $$($1_INCLUDE_FILES),$$($1_SRCS)) + endif ++ ++ # Pickup extra OPENJDK_TARGET_OS_API and/or OPENJDK_TARGET_OS dependent variables ++ # for LDFLAGS and LDFLAGS_SUFFIX ++ $1_EXTRA_LDFLAGS:=$$($1_LDFLAGS_$(OPENJDK_TARGET_OS_API)) $$($1_LDFLAGS_$(OPENJDK_TARGET_OS)) ++ $1_EXTRA_LDFLAGS_SUFFIX:=$$($1_LDFLAGS_SUFFIX_$(OPENJDK_TARGET_OS_API)) $$($1_LDFLAGS_SUFFIX_$(OPENJDK_TARGET_OS)) ++ + ifeq ($(OPENJDK_TARGET_OS), linux) # only on linux +- ifneq ($(OPENJDK_TARGET_CPU_ARCH), aarch64) # not need on the arm arch +- ifneq (,$$(strip $$($1_EXTRA_FILES))) +- $1_SRCS += $$($1_EXTRA_FILES) ++ ifneq ($$(findstring wrap=memcpy, $$($1_LDFLAGS)$$($1_EXTRA_LDFLAGS))$$($1_EXTRA_LDFLAGS_SUFFIX),) ++ ifeq ($$(findstring memcpy.cpp, $$($1_SRCS)),) ++ $1_SRCS += $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp + endif + endif + endif ++ + ifeq (,$$($1_SRCS)) + $$(error No sources found for $1 when looking inside the dirs $$($1_SRC)) + endif +@@ -432,10 +440,6 @@ define SetupNativeCompilation + endif + endif + +- # Pickup extra OPENJDK_TARGET_OS_API and/or OPENJDK_TARGET_OS dependent variables +- # for LDFLAGS and LDFLAGS_SUFFIX +- $1_EXTRA_LDFLAGS:=$$($1_LDFLAGS_$(OPENJDK_TARGET_OS_API)) $$($1_LDFLAGS_$(OPENJDK_TARGET_OS)) +- $1_EXTRA_LDFLAGS_SUFFIX:=$$($1_LDFLAGS_SUFFIX_$(OPENJDK_TARGET_OS_API)) $$($1_LDFLAGS_SUFFIX_$(OPENJDK_TARGET_OS)) + ifneq (,$$($1_REAL_MAPFILE)) + $1_EXTRA_LDFLAGS += $(call SET_SHARED_LIBRARY_MAPFILE,$$($1_REAL_MAPFILE)) + endif diff --git a/implementation_of_Blas_hotspot_function_in_Intrinsics.patch b/implementation_of_Blas_hotspot_function_in_Intrinsics.patch index e731e53bcf5c923cd8c9e7a42c9a1c904bca3dd4..50f26b4fb6e3b488a7d2f8f07451517614c048cd 100755 --- a/implementation_of_Blas_hotspot_function_in_Intrinsics.patch +++ b/implementation_of_Blas_hotspot_function_in_Intrinsics.patch @@ -289,8 +289,8 @@ index c5ec637a1..125983179 100644 + // Search path: /jre/lib///libopenblas.so + if (jvm_offset >= 0) { + if (jvm_offset + strlen(library_name) + strlen(os::dll_file_extension()) < JVM_MAXPATHLEN) { -+ strncpy(&path[jvm_offset], library_name, strlen(library_name)); -+ strncat(&path[jvm_offset], os::dll_file_extension(), strlen(os::dll_file_extension())); ++ strncpy(&path[jvm_offset], library_name, JVM_MAXPATHLEN - jvm_offset); ++ strncat(path, os::dll_file_extension(), strlen(os::dll_file_extension())); + library = (address)os::dll_load(path, err_buf, sizeof(err_buf)); + } + } diff --git a/jdk8u-jdk8u342-b07.tar.xz b/jdk8u-jdk8u352-b08.tar.xz similarity index 82% rename from jdk8u-jdk8u342-b07.tar.xz rename to jdk8u-jdk8u352-b08.tar.xz index 1ec85bbd33f44e7bc8bc70455c82e2fc28d7e27d..19d13e4a3511e06ec5acd8548c55b253065cffb0 100644 Binary files a/jdk8u-jdk8u342-b07.tar.xz and b/jdk8u-jdk8u352-b08.tar.xz differ diff --git a/kae-usability-enhancement.patch b/kae-usability-enhancement.patch new file mode 100644 index 0000000000000000000000000000000000000000..ec1207a0fd7758557930ae10228489caeccb3f89 --- /dev/null +++ b/kae-usability-enhancement.patch @@ -0,0 +1,3640 @@ +From 8545f560d406db592303b09fc576c13ba9a8caa0 Mon Sep 17 00:00:00 2001 +From: kuenking111 +Date: Sat, 3 Sep 2022 14:18:42 +0000 +Subject: [PATCH 2/6] kae-usability-enhancement + +--- + jdk/make/CopyFiles.gmk | 2 +- + jdk/make/mapfiles/libj2kae/mapfile-vers | 1 + + jdk/src/share/lib/security/kaeprovider.conf | 65 ++- + .../openeuler/security/openssl/KAEConfig.java | 386 ++++++++++++++++++ + .../openeuler/security/openssl/KAELog.java | 183 +++++++++ + .../security/openssl/KAEProvider.java | 151 ++++--- + .../security/openssl/KAESM4Cipher.java | 181 ++++++++ + .../security/openssl/kae_cipher_rsa.c | 13 +- + .../openeuler/security/openssl/kae_digest.c | 9 +- + .../org/openeuler/security/openssl/kae_hmac.c | 9 +- + .../security/openssl/kae_keyagreement_dh.c | 4 +- + .../openssl/kae_keypairgenerator_dh.c | 4 +- + .../openssl/kae_keypairgenerator_rsa.c | 6 +- + .../openeuler/security/openssl/kae_provider.c | 54 ++- + .../security/openssl/kae_signature_rsa.c | 21 +- + .../security/openssl/kae_symmetric_cipher.c | 9 +- + .../org/openeuler/security/openssl/kae_util.c | 138 ++++++- + .../org/openeuler/security/openssl/kae_util.h | 51 ++- + .../openeuler/security/openssl/AESTest.java | 114 ++++++ + .../openeuler/security/openssl/DHTest.java | 9 +- + .../security/openssl/DigestTest.java | 60 +++ + .../openeuler/security/openssl/ECDHTest.java | 1 + + .../openeuler/security/openssl/HmacTest.java | 88 ++++ + .../security/openssl/KAEConfTest.java | 121 ++++++ + .../openssl/KAEDisabledAlgorithmsTest.java | 164 ++++++++ + .../security/openssl/KAEEngineIdTest.java | 76 ++++ + .../security/openssl/KAELogTest.java | 126 ++++++ + .../security/openssl/KAETestHelper.java | 209 ++++++++++ + .../security/openssl/KAEUseEngineTest.java | 262 ++++++++++++ + .../security/openssl/KaeDebugLogTest.java | 88 ++++ + .../security/openssl/KaeProviderTest.java | 170 ++++++++ + .../openeuler/security/openssl/RSATest.java | 137 +++++++ + .../openeuler/security/openssl/SM3Test.java | 54 --- + .../openeuler/security/openssl/SM4Test.java | 62 ++- + 34 files changed, 2844 insertions(+), 184 deletions(-) + create mode 100644 jdk/src/solaris/classes/org/openeuler/security/openssl/KAEConfig.java + create mode 100644 jdk/src/solaris/classes/org/openeuler/security/openssl/KAELog.java + create mode 100644 jdk/test/org/openeuler/security/openssl/AESTest.java + create mode 100644 jdk/test/org/openeuler/security/openssl/DigestTest.java + create mode 100644 jdk/test/org/openeuler/security/openssl/HmacTest.java + create mode 100644 jdk/test/org/openeuler/security/openssl/KAEConfTest.java + create mode 100644 jdk/test/org/openeuler/security/openssl/KAEDisabledAlgorithmsTest.java + create mode 100644 jdk/test/org/openeuler/security/openssl/KAEEngineIdTest.java + create mode 100644 jdk/test/org/openeuler/security/openssl/KAELogTest.java + create mode 100644 jdk/test/org/openeuler/security/openssl/KAETestHelper.java + create mode 100644 jdk/test/org/openeuler/security/openssl/KAEUseEngineTest.java + create mode 100644 jdk/test/org/openeuler/security/openssl/KaeDebugLogTest.java + create mode 100644 jdk/test/org/openeuler/security/openssl/KaeProviderTest.java + create mode 100644 jdk/test/org/openeuler/security/openssl/RSATest.java + delete mode 100644 jdk/test/org/openeuler/security/openssl/SM3Test.java + +diff --git a/jdk/make/CopyFiles.gmk b/jdk/make/CopyFiles.gmk +index 2a6fc0932..806d7bec1 100644 +--- a/jdk/make/CopyFiles.gmk ++++ b/jdk/make/CopyFiles.gmk +@@ -634,7 +634,7 @@ endif + ifeq ($(ENABLE_KAE), true) + ifeq ($(OPENJDK_TARGET_CPU_ARCH), aarch64) + +- KAE_CONF_PATH= $(JDK_OUTPUTDIR)/lib/ext ++ KAE_CONF_PATH= $(JDK_OUTPUTDIR)/lib + $(KAE_CONF_PATH)/kaeprovider.conf: $(JDK_TOPDIR)/src/share/lib/security/kaeprovider.conf + $(call install-file) + +diff --git a/jdk/make/mapfiles/libj2kae/mapfile-vers b/jdk/make/mapfiles/libj2kae/mapfile-vers +index 128d1e322..a1bdb830b 100644 +--- a/jdk/make/mapfiles/libj2kae/mapfile-vers ++++ b/jdk/make/mapfiles/libj2kae/mapfile-vers +@@ -27,6 +27,7 @@ SUNWprivate_1.1 { + global: + JNI_OnLoad; + Java_org_openeuler_security_openssl_KAEProvider_initOpenssl; ++ Java_org_openeuler_security_openssl_KAEProvider_getEngineFlags; + Java_org_openeuler_security_openssl_KAEDigest_nativeInit; + Java_org_openeuler_security_openssl_KAEDigest_nativeUpdate; + Java_org_openeuler_security_openssl_KAEDigest_nativeDigest; +diff --git a/jdk/src/share/lib/security/kaeprovider.conf b/jdk/src/share/lib/security/kaeprovider.conf +index a48969669..cc50611d1 100644 +--- a/jdk/src/share/lib/security/kaeprovider.conf ++++ b/jdk/src/share/lib/security/kaeprovider.conf +@@ -1,9 +1,13 @@ + # +-# This is the config file for KAEProvider ++# This is the config file for KAEProvider. ++# These configuration properties support the use of jdk system properties, ++# and jdk system properties take precedence over file configuration properties. ++# For detailed usage, please refer to the user manual: ++# https://gitee.com/openeuler/bishengjdk-8/wikis/%E4%B8%AD%E6%96%87%E6%96%87%E6%A1%A3/KAE%20Provider%E7%94%A8%E6%88%B7%E4%BD%BF%E7%94%A8%E6%89%8B%E5%86%8C + # +-# Algorithms are enabled by default if KAEProvider is used. +-# Delete # if you want to disable certain algorithm. + ++# Algorithms are enabled by default if KAEProvider is used. ++# Delete # if you want to disable certain algorithm. + # kae.md5=false + # kae.sha256=false + # kae.sha384=false +@@ -15,5 +19,58 @@ + # kae.dh=false + # kae.ec=false + +-# enable KAEProvider log setting ++# Configure engine id, the default value is kae. ++# kae.engine.id=kae ++ ++# Configure whether libcrypto.so uses GLOBAL mode, uses LOCAL mode by default. ++# If you use uadk_engine, you need to enable this option. ++# kae.libcrypto.useGlobalMode=false ++ ++# The following configuration will only take effect when using KAEProvider. ++# Configure whether to enable KAE hardware acceleration for each category of algorithm. ++# The configurable value are as follows: ++# true : enable KAE hardware acceleration by default ++# false: use openssl soft calculation by default ++# The digest/sm4/rsa/dh category algorithm enable KAE hardware acceleration by default. ++# The aes/hmac/ec category algorithm use openssl soft calculation by default. ++# The ec category algorithm configuration does not take effect temporarily. and it ++# currently does not support KAE hardware acceleration, temporarily use openssl soft calculation. ++# kae.digest.useKaeEngine=true ++# kae.aes.useKaeEngine=false ++# kae.sm4.useKaeEngine=true ++# kae.hmac.useKaeEngine=false ++# kae.rsa.useKaeEngine=true ++# kae.dh.useKaeEngine=true ++# kae.ec.useKaeEngine=false ++# ++# Some engines do not fully support certain categories of algorithms, for example, the digest ++# algorithm implemented by kae engine only supports md5 and sm3.For more information, please refer to: ++# KAE : https://github.com/kunpengcompute/KAE#:~:text=Digest%20algorithm%3A%20SM3/MD5 ++# UADK: https://gitee.com/openeuler/uadk/wikis/%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3/UADK%20quick%20start#11-uadk ++# ++# Users can disable unsupported algorithms through the following property configuration. ++# Disable algorithm to enable KAE hardware acceleration, use openssl soft algorithm instead. ++# The sha256, sha384 algorithms are disabled by default. ++# digest : md5,sha256,sha384,sm3 ++# aes : aes-128-ecb,aes-128-cbc,aes-128-ctr,aes-128-gcm, ++# aes-192-ecb,aes-192-cbc,aes-192-ctr,aes-192-gcm, ++# aes-256-ecb,aes-256-cbc,aes-256-ctr,aes-256-gcm ++# sm4 : sm4-ecb,sm4-cbc,sm4-ctr,sm4-ofb ++# hmac : hmac-md5,hmac-sha1,hmac-sha224,hmac-sha256,hmac-sha384,hmac-sha512 ++# rsa : rsa ++# dh : dh ++# ec : ec ++# kae.engine.disabledAlgorithms=sha256,sha384 ++ ++# SM4 max chunk size of each encryption or decryption. ++# when input data does not have an accessible byte[]. ++# The default value is 4096, when configuring a non-positive Integer type, use the default value of 4096. ++# kae.sm4.maxChunkSize=4096 ++ ++# Enable engine load log. + # kae.log=true ++# ++# It only takes effect when the property kae.log value is true. ++# Configure log file path, default value is System.getProperty("user.dir") + "/ + "kae.log". ++# kae.log.file=/home/user/kae.log ++ +diff --git a/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEConfig.java b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEConfig.java +new file mode 100644 +index 000000000..07294dbd6 +--- /dev/null ++++ b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEConfig.java +@@ -0,0 +1,386 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++package org.openeuler.security.openssl; ++ ++import sun.security.util.Debug; ++ ++import java.io.BufferedInputStream; ++import java.io.File; ++import java.io.FileInputStream; ++import java.io.IOException; ++import java.io.InputStream; ++import java.security.AccessController; ++import java.security.PrivilegedAction; ++import java.util.Arrays; ++import java.util.HashMap; ++import java.util.Map; ++import java.util.Properties; ++ ++public class KAEConfig { ++ private static final Debug kaeDebug = Debug.getInstance("kae"); ++ ++ // these property names indicates whether each algorithm uses KAEProvider ++ private static final String[] useKaeProviderPropertyNames = new String[]{ ++ "kae.md5", ++ "kae.sha256", ++ "kae.sha384", ++ "kae.sm3", ++ "kae.aes", ++ "kae.sm4", ++ "kae.hmac", ++ "kae.rsa", ++ "kae.dh", ++ "kae.ec" ++ }; ++ ++ // these property names indicate whether KAE hardware acceleration is enabled for each algorithm ++ private static final String[] useKaeEnginePropertyNames = new String[]{ ++ "kae.digest.useKaeEngine", ++ "kae.aes.useKaeEngine", ++ "kae.sm4.useKaeEngine", ++ "kae.hmac.useKaeEngine", ++ "kae.rsa.useKaeEngine", ++ "kae.dh.useKaeEngine", ++ "kae.ec.useKaeEngine" ++ }; ++ ++ // algorithm names ++ private static final String[] algorithmNames = new String[]{ ++ "md5", ++ "sha256", ++ "sha384", ++ "sm3", ++ "aes-128-ecb", ++ "aes-128-cbc", ++ "aes-128-ctr", ++ "aes-128-gcm", ++ "aes-192-ecb", ++ "aes-192-cbc", ++ "aes-192-ctr", ++ "aes-192-gcm", ++ "aes-256-ecb", ++ "aes-256-cbc", ++ "aes-256-ctr", ++ "aes-256-gcm", ++ "sm4-ecb", ++ "sm4-cbc", ++ "sm4-ctr", ++ "sm4-ofb", ++ "hmac-md5", ++ "hmac-sha1", ++ "hmac-sha224", ++ "hmac-sha256", ++ "hmac-sha384", ++ "hmac-sha512", ++ "rsa", ++ "dh", ++ "ec" ++ }; ++ ++ // algorithm name and algorithm index mapping ++ private static final Map algorithmNameIndexMap = new HashMap<>(); ++ ++ // algorithm name and algorithm category index mapping ++ private static final Map algorithmNameCategoryMap = new HashMap<>(); ++ ++ // whether use KAEProvider for each algorithm ++ private static final boolean[] useKaeProviderFlags = new boolean[algorithmNames.length]; ++ ++ // whether use KAEProvider for each category algorithm ++ private static final Map useKaeProviderCategoryMap = new HashMap<>(); ++ ++ // whether enable the Kunpeng acceleration engine for each algorithm ++ private static final boolean[] useKaeEngineFlags = new boolean[algorithmNames.length]; ++ ++ // The kaeprovider.cnf properties ++ private static Properties props; ++ ++ private KAEConfig() { ++ ++ } ++ ++ static { ++ AccessController.doPrivileged(new PrivilegedAction() { ++ public Void run() { ++ initialize(); ++ return null; ++ } ++ }); ++ } ++ ++ private static File kaePropFile(String filename) { ++ String sep = File.separator; ++ String defaultKaeConf = System.getProperty("java.home") + sep + "lib" + sep + filename; ++ String kaeConf = System.getProperty("kae.conf", defaultKaeConf); ++ return new File(kaeConf); ++ } ++ ++ private static void initialize() { ++ initProperties(); ++ initAlgorithmNameMap(); ++ initUseKaeProviderFlags(); ++ initUseKaeEngineFlags(); ++ } ++ ++ private static void initProperties() { ++ props = new Properties(); ++ File propFile = kaePropFile("kaeprovider.conf"); ++ if (propFile.exists()) { ++ InputStream is = null; ++ try { ++ FileInputStream fis = new FileInputStream(propFile); ++ is = new BufferedInputStream(fis); ++ props.load(is); ++ ++ if (kaeDebug != null) { ++ kaeDebug.println("reading kae properties file: " + ++ propFile); ++ } ++ } catch (IOException e) { ++ if (kaeDebug != null) { ++ kaeDebug.println("unable to load kae properties from " + ++ propFile); ++ e.printStackTrace(); ++ } ++ } finally { ++ if (is != null) { ++ try { ++ is.close(); ++ } catch (IOException ioe) { ++ if (kaeDebug != null) { ++ kaeDebug.println("unable to close input stream"); ++ } ++ } ++ } ++ } ++ } else { ++ if (kaeDebug != null) { ++ kaeDebug.println("not found kae properties file: " + ++ propFile); ++ } ++ } ++ } ++ ++ public static Boolean useKaeProvider(String key) { ++ return useKaeProviderCategoryMap.getOrDefault(key, Boolean.TRUE); ++ } ++ ++ private static void initUseKaeProviderFlags() { ++ boolean[] categoryFlagsForProvider = new boolean[useKaeProviderPropertyNames.length]; ++ Arrays.fill(categoryFlagsForProvider, true); ++ for (int i = 0; i < useKaeProviderPropertyNames.length; i++) { ++ String configValue = privilegedGetOverridable(useKaeProviderPropertyNames[i]); ++ if (configValue != null) { ++ categoryFlagsForProvider[i] = Boolean.parseBoolean(configValue); ++ } ++ useKaeProviderCategoryMap.put(useKaeProviderPropertyNames[i], categoryFlagsForProvider[i]); ++ } ++ int offset = useKaeProviderPropertyNames.length - useKaeEnginePropertyNames.length; ++ int digestAlgorithmLen = offset + 1; ++ // digest ++ System.arraycopy(categoryFlagsForProvider, 0, useKaeProviderFlags, 0, digestAlgorithmLen); ++ ++ // non-digest ++ for (int i = digestAlgorithmLen; i < useKaeProviderFlags.length; i++) { ++ Integer algorithmCategoryIndex = algorithmNameCategoryMap.get(algorithmNames[i]); ++ if (categoryFlagsForProvider[algorithmCategoryIndex + offset]) { ++ useKaeProviderFlags[i] = true; ++ } ++ } ++ ++ if (kaeDebug != null) { ++ kaeDebug.println("useKaeProviderPropertyNames: "); ++ for (int i = 0; i < categoryFlagsForProvider.length; i++) { ++ kaeDebug.println(useKaeProviderPropertyNames[i] + "=" + categoryFlagsForProvider[i]); ++ } ++ ++ kaeDebug.println("useKaeProviderFlags: "); ++ for (int i = 0; i < useKaeProviderFlags.length; i++) { ++ kaeDebug.println(algorithmNames[i] + "=" + useKaeProviderFlags[i]); ++ } ++ } ++ } ++ ++ public static boolean[] getUseKaeProviderFlags() { ++ return useKaeProviderFlags; ++ } ++ ++ private static void initUseKaeEngineFlags() { ++ boolean[] categoryFlagsForEngine = new boolean[]{ ++ true, // digest ++ false, // aes ++ true, // sm4 ++ false, // hmac ++ true, // rsa ++ true, // dh ++ false // ec ++ }; ++ for (int i = 0; i < useKaeEnginePropertyNames.length; i++) { ++ String configValue = privilegedGetOverridable(useKaeEnginePropertyNames[i]); ++ if (configValue != null) { ++ categoryFlagsForEngine[i] = Boolean.parseBoolean(configValue); ++ } ++ } ++ ++ // EC algorithm currently does not support KAE hardware acceleration, temporarily use openssl soft calculation. ++ categoryFlagsForEngine[useKaeEnginePropertyNames.length - 1] = false; ++ ++ for (int i = 0; i < useKaeEngineFlags.length; i++) { ++ Integer algorithmCategoryIndex = algorithmNameCategoryMap.get(algorithmNames[i]); ++ if (categoryFlagsForEngine[algorithmCategoryIndex]) { ++ useKaeEngineFlags[i] = true; ++ } ++ } ++ ++ String[] disabledAlgorithms = getDisabledAlgorithms(); ++ for (String disabledAlgorithm : disabledAlgorithms) { ++ Integer algorithmIndex = algorithmNameIndexMap.get(disabledAlgorithm); ++ if (algorithmIndex != null) { ++ useKaeEngineFlags[algorithmIndex] = false; ++ } ++ } ++ if (kaeDebug != null) { ++ kaeDebug.println("useKaeEnginePropertyNames: "); ++ for (int i = 0; i < categoryFlagsForEngine.length; i++) { ++ kaeDebug.println(useKaeEnginePropertyNames[i] + "=" + categoryFlagsForEngine[i]); ++ } ++ ++ kaeDebug.println("disabledAlgorithms: "); ++ for (int i = 0; i < disabledAlgorithms.length; i++) { ++ kaeDebug.println(disabledAlgorithms[i]); ++ } ++ ++ kaeDebug.println("useKaeEngineFlags: "); ++ for (int i = 0; i < useKaeEngineFlags.length; i++) { ++ kaeDebug.println(algorithmNames[i] + "=" + useKaeEngineFlags[i]); ++ } ++ } ++ } ++ ++ public static boolean[] getUseKaeEngineFlags() { ++ return useKaeEngineFlags; ++ } ++ ++ private static void initAlgorithmNameIndexMap() { ++ for (int i = 0; i < algorithmNames.length; i++) { ++ algorithmNameIndexMap.put(algorithmNames[i], i); ++ } ++ } ++ ++ /* ++ * 0 : digest ++ * 1 : aes ++ * 2 : sm4 ++ * 3 : hmac ++ * 4 : rsa ++ * 5 : dh ++ * 6 : ec ++ */ ++ private static void initAlgorithmNameCategoryMap() { ++ algorithmNameCategoryMap.put("md5", 0); ++ algorithmNameCategoryMap.put("sha256", 0); ++ algorithmNameCategoryMap.put("sha384", 0); ++ algorithmNameCategoryMap.put("sm3", 0); ++ algorithmNameCategoryMap.put("aes-128-ecb", 1); ++ algorithmNameCategoryMap.put("aes-128-cbc", 1); ++ algorithmNameCategoryMap.put("aes-128-ctr", 1); ++ algorithmNameCategoryMap.put("aes-128-gcm", 1); ++ algorithmNameCategoryMap.put("aes-192-ecb", 1); ++ algorithmNameCategoryMap.put("aes-192-cbc", 1); ++ algorithmNameCategoryMap.put("aes-192-ctr", 1); ++ algorithmNameCategoryMap.put("aes-192-gcm", 1); ++ algorithmNameCategoryMap.put("aes-256-ecb", 1); ++ algorithmNameCategoryMap.put("aes-256-cbc", 1); ++ algorithmNameCategoryMap.put("aes-256-ctr", 1); ++ algorithmNameCategoryMap.put("aes-256-gcm", 1); ++ algorithmNameCategoryMap.put("sm4-ecb", 2); ++ algorithmNameCategoryMap.put("sm4-cbc", 2); ++ algorithmNameCategoryMap.put("sm4-ctr", 2); ++ algorithmNameCategoryMap.put("sm4-ofb", 2); ++ algorithmNameCategoryMap.put("hmac-md5", 3); ++ algorithmNameCategoryMap.put("hmac-sha1", 3); ++ algorithmNameCategoryMap.put("hmac-sha224", 3); ++ algorithmNameCategoryMap.put("hmac-sha256", 3); ++ algorithmNameCategoryMap.put("hmac-sha384", 3); ++ algorithmNameCategoryMap.put("hmac-sha512", 3); ++ algorithmNameCategoryMap.put("rsa", 4); ++ algorithmNameCategoryMap.put("dh", 5); ++ algorithmNameCategoryMap.put("ec", 6); ++ } ++ ++ private static void initAlgorithmNameMap() { ++ initAlgorithmNameIndexMap(); ++ initAlgorithmNameCategoryMap(); ++ } ++ ++ private static String[] getDisabledAlgorithms() { ++ String disabledAlgorithms = privilegedGetOverridable("kae.engine.disabledAlgorithms", ++ "sha256,sha384"); ++ return disabledAlgorithms.replaceAll(" ", "").split("\\,"); ++ } ++ ++ public static String privilegedGetProperty(String key) { ++ if (System.getSecurityManager() == null) { ++ return getProperty(key); ++ } else { ++ return AccessController.doPrivileged((PrivilegedAction) () -> getOverridableProperty(key)); ++ } ++ } ++ ++ public static String privilegedGetOverridable(String key) { ++ if (System.getSecurityManager() == null) { ++ return getOverridableProperty(key); ++ } else { ++ return AccessController.doPrivileged((PrivilegedAction) () -> getOverridableProperty(key)); ++ } ++ } ++ ++ public static String privilegedGetOverridable(String key, String defaultValue) { ++ String val = privilegedGetOverridable(key); ++ return (val == null) ? defaultValue : val; ++ } ++ ++ private static String getProperty(String key) { ++ String val = props.getProperty(key); ++ if (val != null) ++ val = val.trim(); ++ return val; ++ } ++ ++ private static String getOverridableProperty(String key) { ++ String val = System.getProperty(key); ++ if (val == null) { ++ return getProperty(key); ++ } else { ++ return val; ++ } ++ } ++ ++ public static String getAlgorithmName(int index) { ++ if (index < 0 || index >= algorithmNames.length) { ++ throw new IndexOutOfBoundsException(); ++ } ++ return algorithmNames[index]; ++ } ++} +diff --git a/jdk/src/solaris/classes/org/openeuler/security/openssl/KAELog.java b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAELog.java +new file mode 100644 +index 000000000..434f773a1 +--- /dev/null ++++ b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAELog.java +@@ -0,0 +1,183 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++package org.openeuler.security.openssl; ++ ++import sun.security.util.Debug; ++ ++import java.io.BufferedWriter; ++import java.io.File; ++import java.io.IOException; ++import java.nio.file.Files; ++import java.nio.file.Path; ++import java.nio.file.StandardOpenOption; ++import java.security.AccessController; ++import java.security.PrivilegedAction; ++import java.text.SimpleDateFormat; ++import java.util.Arrays; ++import java.util.Date; ++ ++public class KAELog { ++ private static final Debug kaeDebug = Debug.getInstance("kae"); ++ private static File logFile; ++ private static boolean exist; ++ ++ private KAELog() { ++ ++ } ++ ++ static { ++ AccessController.doPrivileged(new PrivilegedAction() { ++ public Void run() { ++ initialize(); ++ return null; ++ } ++ }); ++ } ++ ++ private static void initialize() { ++ if (!enableKaeLog()) { ++ if (kaeDebug != null) { ++ kaeDebug.println("kae logging is not enabled"); ++ } ++ return; ++ } ++ ++ logFile = kaeLogFile("kae.log"); ++ File parentFile = logFile.getParentFile(); ++ if (!parentFile.exists()) { ++ try { ++ Files.createDirectories(parentFile.toPath()); ++ } catch (IOException e) { ++ if (kaeDebug != null) { ++ kaeDebug.println("failed to create directory :" + parentFile); ++ e.printStackTrace(); ++ } ++ return; ++ } ++ } ++ ++ if (logFile.exists()) { ++ if (kaeDebug != null) { ++ kaeDebug.println("found kae log file :" + logFile); ++ } ++ exist = true; ++ } else { ++ if (kaeDebug != null) { ++ kaeDebug.println("not found kae log file :" + logFile); ++ } ++ try { ++ Path path = Files.createFile(logFile.toPath()); ++ if (path != null) { ++ exist = true; ++ } ++ } catch (IOException e) { ++ if (kaeDebug != null) { ++ kaeDebug.println("unable to create new kae log file :" + logFile); ++ e.printStackTrace(); ++ } ++ } ++ ++ if (exist) { ++ if (kaeDebug != null) { ++ kaeDebug.println("create new kae log file :" + logFile); ++ } ++ } ++ } ++ } ++ ++ public static boolean enableKaeLog() { ++ String debug = KAEConfig.privilegedGetOverridable("kae.log"); ++ return Boolean.parseBoolean(debug); ++ } ++ ++ private static File kaeLogFile(String filename) { ++ String sep = File.separator; ++ String defaultKaeLog = System.getProperty("user.dir") + sep + filename; ++ String kaeLog = KAEConfig.privilegedGetOverridable("kae.log.file", defaultKaeLog); ++ return new File(kaeLog); ++ } ++ ++ private static String getLogTime() { ++ SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); ++ return simpleDateFormat.format(new Date()); ++ } ++ ++ public static void log(String engineId, Throwable throwable, boolean[] engineFlags, boolean[] kaeProviderFlags) { ++ if (engineFlags.length != kaeProviderFlags.length) { ++ if (kaeDebug != null) { ++ kaeDebug.println("The length of engineFlags is not equal to the length of kaeProviderFlags."); ++ kaeDebug.println(String.format("engineFlags : %s", Arrays.toString(engineFlags))); ++ kaeDebug.println(String.format("kaeProviderFlags : %s", Arrays.toString(kaeProviderFlags))); ++ } ++ return; ++ } ++ if (!exist) { ++ return; ++ } ++ ++ try (BufferedWriter writer = Files.newBufferedWriter(logFile.toPath(), ++ StandardOpenOption.APPEND)) { ++ logEngine(writer, engineId, throwable); ++ writer.newLine(); ++ logAlgorithmStrategy(writer, engineFlags, kaeProviderFlags); ++ writer.newLine(); ++ } catch (IOException e) { ++ if (kaeDebug != null) { ++ kaeDebug.println("write kae log failed"); ++ e.printStackTrace(); ++ } ++ } ++ } ++ ++ // log engine ++ private static void logEngine(BufferedWriter writer, String engineId, Throwable throwable) throws IOException { ++ writer.write(String.format("[%s] ", getLogTime())); ++ if (throwable == null) { ++ writer.write(String.format("%s engine was found.", engineId)); ++ } else if (throwable instanceof RuntimeException) { ++ writer.write(String.format("%s engine was not found. %s", engineId, throwable.getMessage())); ++ } else { ++ writer.write(throwable.getMessage()); ++ } ++ } ++ ++ // log algorithm strategy ++ private static void logAlgorithmStrategy(BufferedWriter writer, boolean[] engineFlags, boolean[] kaeProviderFlags) ++ throws IOException { ++ writer.write(String.format("[%s] ", getLogTime())); ++ writer.write("The implementation strategy of each algorithm is as follows : "); ++ for (int i = 0; i < engineFlags.length; i++) { ++ writer.newLine(); ++ String algorithmName = KAEConfig.getAlgorithmName(i); ++ String message; ++ if (kaeProviderFlags[i]) { ++ String detail = engineFlags[i] ? "enable KAE hardware acceleration" : "Use openssl soft calculation"; ++ message = String.format(" %-11s => %s: %s", algorithmName, "KAEProvider", detail); ++ } else { ++ message = String.format(" %-11s => %s", algorithmName, "Non-KAEProvider"); ++ } ++ writer.write(message); ++ } ++ } ++} +diff --git a/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEProvider.java b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEProvider.java +index 83ed8649c..3e7f54638 100644 +--- a/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEProvider.java ++++ b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEProvider.java +@@ -24,116 +24,103 @@ + + package org.openeuler.security.openssl; + +-import java.io.BufferedWriter; +-import java.io.BufferedInputStream; +-import java.io.File; +-import java.io.FileInputStream; +-import java.io.InputStream; +-import java.io.IOException; +-import java.nio.file.Files; +-import java.nio.file.Path; +-import java.nio.file.Paths; +-import java.nio.file.StandardOpenOption; +-import java.util.Date; +-import java.util.Properties; ++import sun.security.util.Debug; ++ ++import java.security.AccessController; ++import java.security.PrivilegedAction; + import java.security.Provider; + + /** + * KAE Provider + */ + public class KAEProvider extends Provider { +- private static Throwable excp; +- private static boolean needLog = true; ++ private static final Debug kaeDebug = Debug.getInstance("kae"); ++ ++ // default engine id ++ private static final String DEFAULT_ENGINE_ID = "kae"; + + static { +- Throwable status = null; +- try { +- System.loadLibrary("j2kae"); +- initOpenssl(); +- } catch (UnsatisfiedLinkError t) { +- status = t; +- } catch (RuntimeException e) { +- status = e; +- } +- excp = status; ++ initialize(); + } + +- private void logStart(Throwable excp) { +- File file = new File(System.getProperty("user.dir"), "kae.log"); +- Path fpath = file.toPath(); +- if (!Files.exists(fpath)) { +- try { +- file.createNewFile(); +- } catch (IOException e) { +- e.printStackTrace(); +- } +- } ++ private static void initialize() { ++ loadLibrary(); ++ initOpenssl(); ++ } + +- try (BufferedWriter writer = Files.newBufferedWriter(fpath, StandardOpenOption.APPEND)) { +- if (excp != null) { +- writer.write(excp.getMessage()); +- } else { +- writer.write("KAE Engine was found"); ++ // load kae.so ++ private static void loadLibrary() { ++ AccessController.doPrivileged(new PrivilegedAction() { ++ @Override ++ public Object run() { ++ System.loadLibrary("j2kae"); ++ return null; + } +- writer.write(" " + new Date()); +- writer.newLine(); +- } catch (IOException e) { +- e.initCause(excp).printStackTrace(); +- } +- KAEProvider.excp = null; // Exception already logged, clean it. ++ }); + } + +- private Properties getProp() { +- Properties props = new Properties(); +- String sep = File.separator; +- File propFile = new File(System.getProperty("java.home") + sep + "lib" + sep + +- "ext" + sep + "kaeprovider.conf"); +- if (propFile.exists()) { +- try (InputStream is = new BufferedInputStream(new FileInputStream(propFile))) { +- props.load(is); +- } catch (IOException e) { +- e.printStackTrace(); ++ // init openssl ++ private static void initOpenssl() { ++ boolean useGlobalMode = useGlobalMode(); ++ String engineId = getEngineId(); ++ boolean[] algorithmKaeFlags = KAEConfig.getUseKaeEngineFlags(); ++ Throwable throwable = null; ++ try { ++ initOpenssl(useGlobalMode, engineId, algorithmKaeFlags); ++ } catch (Throwable t) { ++ throwable = t; ++ if (kaeDebug != null) { ++ kaeDebug.println("initOpenssl failed : " + throwable.getMessage()); + } + } +- return props; ++ boolean[] engineFlags = getEngineFlags(); ++ boolean[] kaeProviderFlags = KAEConfig.getUseKaeProviderFlags(); ++ KAELog.log(engineId, throwable, engineFlags, kaeProviderFlags); ++ } ++ ++ // get engine id ++ private static String getEngineId() { ++ return KAEConfig.privilegedGetOverridable("kae.engine.id", DEFAULT_ENGINE_ID); ++ } ++ ++ // whether to set libcrypto.so to GLOBAL mode, by default libcrypto.so is LOCAL mode ++ private static boolean useGlobalMode() { ++ String explicitLoad = KAEConfig.privilegedGetOverridable( ++ "kae.libcrypto.useGlobalMode", "false"); ++ return Boolean.parseBoolean(explicitLoad); + } + + public KAEProvider() { + super("KAEProvider", 1.8d, "KAE provider"); +- Properties props = getProp(); +- if (needLog && "true".equalsIgnoreCase(props.getProperty("kae.log"))) { +- logStart(excp); +- needLog = false; // Log only once +- } +- if (!"false".equalsIgnoreCase(props.getProperty("kae.md5"))) { ++ if (KAEConfig.useKaeProvider("kae.md5")) { + putMD5(); + } +- if (!"false".equalsIgnoreCase(props.getProperty("kae.sha256"))) { ++ if (KAEConfig.useKaeProvider("kae.sha256")) { + putSHA256(); + } +- if (!"false".equalsIgnoreCase(props.getProperty("kae.sha384"))) { ++ if (KAEConfig.useKaeProvider("kae.sha384")) { + putSHA384(); + } +- if (!"false".equalsIgnoreCase(props.getProperty("kae.sm3"))) { ++ if (KAEConfig.useKaeProvider("kae.sm3")) { + putSM3(); + } +- if (!"false".equalsIgnoreCase(props.getProperty("kae.aes"))) { ++ if (KAEConfig.useKaeProvider("kae.aes")) { + putAES(); + } +- if (!"false".equalsIgnoreCase(props.getProperty("kae.sm4"))) { ++ if (KAEConfig.useKaeProvider("kae.sm4")) { + putSM4(); + } +- if (!"false".equalsIgnoreCase(props.getProperty("kae.hmac"))) { ++ if (KAEConfig.useKaeProvider("kae.hmac")) { + putHMAC(); + } +- if (!"false".equalsIgnoreCase(props.getProperty("kae.rsa"))) { ++ if (KAEConfig.useKaeProvider("kae.rsa")) { + putRSA(); + putSignatureRSA(); + } +- if (!"false".equalsIgnoreCase(props.getProperty("kae.dh"))) { ++ if (KAEConfig.useKaeProvider("kae.dh")) { + putDH(); + } +- if (!"false".equalsIgnoreCase(props.getProperty("kae.ec"))) { ++ if (KAEConfig.useKaeProvider("kae.ec")) { + putEC(); + } + } +@@ -285,28 +272,28 @@ public class KAEProvider extends Provider { + "org.openeuler.security.openssl.KAERSASignature$SHA512withRSA"); + + // alias +- put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5withRSA"); ++ put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5withRSA"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.4", "MD5withRSA"); + +- put("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1withRSA"); ++ put("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1withRSA"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.5", "SHA1withRSA"); +- put("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1withRSA"); ++ put("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1withRSA"); + +- put("Alg.Alias.Signature.1.2.840.113549.1.1.14", "SHA224withRSA"); ++ put("Alg.Alias.Signature.1.2.840.113549.1.1.14", "SHA224withRSA"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.14", "SHA224withRSA"); + +- put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256withRSA"); ++ put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256withRSA"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.11", "SHA256withRSA"); + +- put("Alg.Alias.Signature.1.2.840.113549.1.1.12", "SHA384withRSA"); ++ put("Alg.Alias.Signature.1.2.840.113549.1.1.12", "SHA384withRSA"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.12", "SHA384withRSA"); + +- put("Alg.Alias.Signature.1.2.840.113549.1.1.13", "SHA512withRSA"); ++ put("Alg.Alias.Signature.1.2.840.113549.1.1.13", "SHA512withRSA"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.13", "SHA512withRSA"); + + put("Signature.RSASSA-PSS", "org.openeuler.security.openssl.KAERSAPSSSignature"); + +- put("Alg.Alias.Signature.1.2.840.113549.1.1.10", "RSASSA-PSS"); ++ put("Alg.Alias.Signature.1.2.840.113549.1.1.10", "RSASSA-PSS"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.10", "RSASSA-PSS"); + + // attributes for supported key classes +@@ -326,6 +313,10 @@ public class KAEProvider extends Provider { + put("Alg.Alias.KeyPairGenerator.EllipticCurve", "EC"); + put("KeyAgreement.ECDH", "org.openeuler.security.openssl.KAEECDHKeyAgreement"); + } ++ + // init openssl +- static native void initOpenssl() throws RuntimeException; ++ static native void initOpenssl(boolean useGlobalMode, String engineId, boolean[] algorithmKaeFlags) ++ throws RuntimeException; ++ ++ static native boolean[] getEngineFlags(); + } +diff --git a/jdk/src/solaris/classes/org/openeuler/security/openssl/KAESM4Cipher.java b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAESM4Cipher.java +index b189bea3a..cca619e1a 100644 +--- a/jdk/src/solaris/classes/org/openeuler/security/openssl/KAESM4Cipher.java ++++ b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAESM4Cipher.java +@@ -26,13 +26,20 @@ + + package org.openeuler.security.openssl; + ++import sun.security.util.Debug; ++ ++import java.nio.ByteBuffer; + import java.security.InvalidAlgorithmParameterException; + import java.security.InvalidKeyException; + import java.security.NoSuchAlgorithmException; + import java.security.Key; ++import java.security.ProviderException; + import java.util.Locale; + ++import javax.crypto.BadPaddingException; ++import javax.crypto.IllegalBlockSizeException; + import javax.crypto.NoSuchPaddingException; ++import javax.crypto.ShortBufferException; + + /* + * This class currently supports: +@@ -46,6 +53,55 @@ import javax.crypto.NoSuchPaddingException; + */ + abstract class KAESM4Cipher extends KAESymmetricCipherBase { + ++ private static final Debug debug = Debug.getInstance("kae"); ++ ++ /* ++ * SM4 max chunk size of each encryption or decryption ++ * when input data does not have an accessible byte[] ++ */ ++ private static final int DEFAULT_KAE_SM4_MAX_CHUNK_SIZE = 4096; ++ private static int KAE_SM4_MAX_CHUNK_SIZE; ++ static { ++ initSM4MaxChunkSize(); ++ } ++ ++ private static void initSM4MaxChunkSize() { ++ String maxChunkSize = KAEConfig.privilegedGetOverridable("kae.sm4.maxChunkSize", ++ DEFAULT_KAE_SM4_MAX_CHUNK_SIZE + ""); ++ try { ++ KAE_SM4_MAX_CHUNK_SIZE = Integer.parseInt(maxChunkSize); ++ } catch (NumberFormatException e) { ++ // When parsing string argument to signed decimal integer fails, uses the default chunk size (4096) ++ KAE_SM4_MAX_CHUNK_SIZE = DEFAULT_KAE_SM4_MAX_CHUNK_SIZE; ++ if (debug != null) { ++ debug.println("The configured block size (" + maxChunkSize + ") cannot be converted to an integer, " + ++ "uses the default chunk size (" + DEFAULT_KAE_SM4_MAX_CHUNK_SIZE + ")"); ++ e.printStackTrace(); ++ } ++ return; ++ } ++ // when the configured chunk size is less than or equal to 0, uses the default chunk size (4096) ++ if (KAE_SM4_MAX_CHUNK_SIZE <= 0) { ++ KAE_SM4_MAX_CHUNK_SIZE = DEFAULT_KAE_SM4_MAX_CHUNK_SIZE; ++ if (debug != null) { ++ debug.println("The configured chunk size (" + KAE_SM4_MAX_CHUNK_SIZE + ") is less than " + ++ "or equal to 0, uses the default chunk size (" + DEFAULT_KAE_SM4_MAX_CHUNK_SIZE + ")"); ++ } ++ return; ++ } ++ if (debug != null) { ++ debug.println("The configured chunk size is " + KAE_SM4_MAX_CHUNK_SIZE); ++ } ++ } ++ ++ /** ++ * Used by the engineUpdate(ByteBuffer, ByteBuffer) and ++ * engineDoFinal(ByteBuffer, ByteBuffer) methods. ++ */ ++ private static int getSM4MaxChunkSize(int totalSize) { ++ return Math.min(KAE_SM4_MAX_CHUNK_SIZE, totalSize); ++ } ++ + public static class Sm4 extends KAESM4Cipher { + public Sm4(Mode mode, Padding padding) { + super(mode, padding, 16); +@@ -170,6 +226,131 @@ abstract class KAESM4Cipher extends KAESymmetricCipherBase { + } + } + ++ @Override ++ protected int engineUpdate(ByteBuffer input, ByteBuffer output) throws ShortBufferException { ++ try { ++ return bufferCrypt(input, output, true); ++ } catch (IllegalBlockSizeException e) { ++ // never thrown for engineUpdate() ++ throw new ProviderException("Internal error in update()"); ++ } catch (BadPaddingException e) { ++ // never thrown for engineUpdate() ++ throw new ProviderException("Internal error in update()"); ++ } ++ } ++ ++ @Override ++ protected int engineDoFinal(ByteBuffer input, ByteBuffer output) ++ throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { ++ return bufferCrypt(input, output, false); ++ } ++ ++ /** ++ * Implementation for encryption using ByteBuffers. Used for both ++ * engineUpdate() and engineDoFinal(). ++ */ ++ private int bufferCrypt(ByteBuffer input, ByteBuffer output, ++ boolean isUpdate) throws ShortBufferException, ++ IllegalBlockSizeException, BadPaddingException { ++ if ((input == null) || (output == null)) { ++ throw new NullPointerException ++ ("Input and output buffers must not be null"); ++ } ++ int inPos = input.position(); ++ int inLimit = input.limit(); ++ int inLen = inLimit - inPos; ++ if (isUpdate && (inLen == 0)) { ++ return 0; ++ } ++ int outLenNeeded = engineGetOutputSize(inLen); ++ ++ if (output.remaining() < outLenNeeded) { ++ throw new ShortBufferException("Need at least " + outLenNeeded ++ + " bytes of space in output buffer"); ++ } ++ ++ // detecting input and output buffer overlap may be tricky ++ // we can only write directly into output buffer when we ++ // are 100% sure it's safe to do so ++ ++ boolean a1 = input.hasArray(); ++ boolean a2 = output.hasArray(); ++ int total = 0; ++ ++ if (a1) { // input has an accessible byte[] ++ byte[] inArray = input.array(); ++ int inOfs = input.arrayOffset() + inPos; ++ ++ byte[] outArray; ++ if (a2) { // output has an accessible byte[] ++ outArray = output.array(); ++ int outPos = output.position(); ++ int outOfs = output.arrayOffset() + outPos; ++ ++ // check array address and offsets and use temp output buffer ++ // if output offset is larger than input offset and ++ // falls within the range of input data ++ boolean useTempOut = false; ++ if (inArray == outArray && ++ ((inOfs < outOfs) && (outOfs < inOfs + inLen))) { ++ useTempOut = true; ++ outArray = new byte[outLenNeeded]; ++ outOfs = 0; ++ } ++ if (isUpdate) { ++ total = engineUpdate(inArray, inOfs, inLen, outArray, outOfs); ++ } else { ++ total = engineDoFinal(inArray, inOfs, inLen, outArray, outOfs); ++ } ++ if (useTempOut) { ++ output.put(outArray, outOfs, total); ++ } else { ++ // adjust output position manually ++ output.position(outPos + total); ++ } ++ } else { // output does not have an accessible byte[] ++ if (isUpdate) { ++ outArray = engineUpdate(inArray, inOfs, inLen); ++ } else { ++ outArray = engineDoFinal(inArray, inOfs, inLen); ++ } ++ if (outArray != null && outArray.length != 0) { ++ output.put(outArray); ++ total = outArray.length; ++ } ++ } ++ // adjust input position manually ++ input.position(inLimit); ++ } else { // input does not have an accessible byte[] ++ // have to assume the worst, since we have no way of determine ++ // if input and output overlaps or not ++ byte[] tempOut = new byte[outLenNeeded]; ++ int outOfs = 0; ++ ++ byte[] tempIn = new byte[getSM4MaxChunkSize(inLen)]; ++ do { ++ int chunk = Math.min(inLen, tempIn.length); ++ if (chunk > 0) { ++ input.get(tempIn, 0, chunk); ++ } ++ int n; ++ if (isUpdate || (inLen > chunk)) { ++ n = engineUpdate(tempIn, 0, chunk, tempOut, outOfs); ++ } else { ++ n = engineDoFinal(tempIn, 0, chunk, tempOut, outOfs); ++ } ++ outOfs += n; ++ total += n; ++ inLen -= chunk; ++ } while (inLen > 0); ++ if (total > 0) { ++ output.put(tempOut, 0, total); ++ } ++ } ++ ++ return total; ++ } ++ + protected void checkIvBytes(byte[] ivBytes) throws InvalidAlgorithmParameterException { + if (ivBytes == null) { + throw new InvalidAlgorithmParameterException("Wrong IV length: iv is null "); +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_cipher_rsa.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_cipher_rsa.c +index 80a0e58b9..d9b16ab9d 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_cipher_rsa.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_cipher_rsa.c +@@ -24,12 +24,11 @@ + #include + #include + #include ++#include "kae_log.h" + #include "kae_util.h" + #include "kae_exception.h" + #include "org_openeuler_security_openssl_KAERSACipher.h" + +-static ENGINE* kaeEngine = NULL; +- + typedef int RSACryptOperation(int, const unsigned char*, unsigned char*, RSA*, int); + + typedef int EvpPkeyCryptOperation(EVP_PKEY_CTX*, unsigned char*, size_t*, const unsigned char*, size_t); +@@ -176,7 +175,9 @@ static int RSACryptOAEPPadding(JNIEnv* env, jlong keyAddress, jint inLen, jbyteA + // outLen type should be size_t + // EVP_PKEY_encrypt takes the outLen address as a parameter, and the parameter type is size_t* + size_t outLen = 0; +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); ++ KAE_TRACE("RSACryptOAEPPadding: kaeEngine => %p", kaeEngine); ++ + + EVP_PKEY* pkey = (EVP_PKEY*) keyAddress; + +@@ -272,7 +273,8 @@ JNIEXPORT jlong JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeC + BIGNUM* bnIQMP = NULL; + RSA* rsa = NULL; + EVP_PKEY* pkey = NULL; +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); ++ KAE_TRACE("KAERSACipher_nativeCreateRSAPrivateCrtKey: kaeEngine => %p", kaeEngine); + + // convert to big num + if ((bnN = KAE_GetBigNumFromByteArray(env, n)) == NULL || +@@ -334,7 +336,8 @@ JNIEXPORT jlong JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeC + BIGNUM* bnE = NULL; + RSA* rsa = NULL; + EVP_PKEY* pkey = NULL; +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); ++ KAE_TRACE("KAERSACipher_nativeCreateRSAPublicKey: kaeEngine => %p", kaeEngine); + + // get public key param n + bnN = KAE_GetBigNumFromByteArray(env, n); +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_digest.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_digest.c +index f0e7b0be4..23b178978 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_digest.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_digest.c +@@ -42,7 +42,7 @@ JNIEXPORT jlong JNICALL + Java_org_openeuler_security_openssl_KAEDigest_nativeInit(JNIEnv *env, jclass cls, jstring algorithmName) + { + EVP_MD_CTX* ctx = NULL; +- static ENGINE* kaeEngine = NULL; ++ ENGINE* kaeEngine = NULL; + + if (algorithmName == NULL) { + KAE_ThrowNullPointerException(env, "algorithm is null"); +@@ -51,11 +51,8 @@ Java_org_openeuler_security_openssl_KAEDigest_nativeInit(JNIEnv *env, jclass cls + + // EVP_get_digestbyname + const char* algo_utf = (*env)->GetStringUTFChars(env, algorithmName, 0); +- if ((strcasecmp(algo_utf, "md5") == 0) || (strcasecmp(algo_utf, "sm3") == 0)) { +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; +- } else { +- kaeEngine = NULL; +- } ++ kaeEngine = GetDigestEngineByAlgorithmName(algo_utf); ++ KAE_TRACE("KAEDigest_nativeInit: kaeEngine => %p", kaeEngine); + EVP_MD* md = (EVP_MD*) EVP_get_digestbyname(algo_utf); + (*env)->ReleaseStringUTFChars(env, algorithmName, algo_utf); + if (md == NULL) { +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_hmac.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_hmac.c +index 554a9750c..1efacbb5b 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_hmac.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_hmac.c +@@ -73,9 +73,14 @@ JNIEXPORT jlong JNICALL Java_org_openeuler_security_openssl_KAEHMac_nativeInit + HMAC_CTX* ctx = NULL; + jbyte* key_buffer = NULL; + const EVP_MD* md = NULL; ++ ENGINE* kaeEngine = NULL; + + const char* algo = (*env)->GetStringUTFChars(env, algoStr, 0); +- md = EVPGetDigestByName(env, algo); ++ md = EVPGetDigestByName(env, algo); ++ ++ kaeEngine = GetHmacEngineByAlgorithmName(algo); ++ KAE_TRACE("KAEHMac_nativeInit: kaeEngine => %p", kaeEngine); ++ + (*env)->ReleaseStringUTFChars(env, algoStr, algo); + if (md == NULL) { + KAE_ThrowRuntimeException(env, "algorithm unsupport"); +@@ -98,7 +103,7 @@ JNIEXPORT jlong JNICALL Java_org_openeuler_security_openssl_KAEHMac_nativeInit + } + + // init hmac context with sc_key and evp_md +- int result_code = HMAC_Init_ex(ctx, key_buffer, key_len, md, NULL); ++ int result_code = HMAC_Init_ex(ctx, key_buffer, key_len, md, kaeEngine); + if (result_code == 0) { + KAE_ThrowRuntimeException(env, "Hmac_Init_ex invoked failed"); + goto cleanup; +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_dh.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_dh.c +index 7cdf790cb..d8d2ee7cb 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_dh.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_dh.c +@@ -51,8 +51,8 @@ JNIEXPORT jbyteArray JNICALL Java_org_openeuler_security_openssl_KAEDHKeyAgreeme + int computekeyLength = 0; + unsigned char* secret = NULL; + jbyteArray retByteArray = NULL; +- static ENGINE* kaeEngine = NULL; +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(DH_INDEX); ++ KAE_TRACE("KAEDHKeyAgreement_nativeComputeKey: kaeEngine => %p", kaeEngine); + + // bits to Bytes + int pSizeInByte = (pSize +7) >> 3; +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keypairgenerator_dh.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keypairgenerator_dh.c +index 54dc07edd..d16b42b41 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keypairgenerator_dh.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keypairgenerator_dh.c +@@ -50,8 +50,8 @@ JNIEXPORT jobjectArray JNICALL Java_org_openeuler_security_openssl_KAEDHKeyPairG + jobjectArray keys = NULL; + jbyteArray pri_key = NULL; + jbyteArray pub_key = NULL; +- static ENGINE* kaeEngine = NULL; +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(DH_INDEX); ++ KAE_TRACE("KAEDHKeyPairGenerator_nativeGenerateKeyPair: kaeEngine => %p", kaeEngine); + + KAE_TRACE("Java_org_openeuler_security_openssl_KAEDHKeyPairGenerator_nativeGenerateKeyPair start !"); + +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keypairgenerator_rsa.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keypairgenerator_rsa.c +index 2ca978bbe..9251b56c4 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keypairgenerator_rsa.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keypairgenerator_rsa.c +@@ -23,6 +23,7 @@ + + #include + #include ++#include "kae_log.h" + #include "kae_util.h" + #include "kae_exception.h" + #include "org_openeuler_security_openssl_KAERSAKeyPairGenerator.h" +@@ -62,8 +63,9 @@ static const BIGNUM* (* GetRSAParamFunctionList[])(const RSA*) = { + * step 3.Generate rsa key, and all key information is stored in RSA + */ + static RSA* NewRSA(JNIEnv* env, jint keySize, jbyteArray publicExponent) { +- static ENGINE* kaeEngine = NULL; +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); ++ KAE_TRACE("NewRSA: kaeEngine => %p", kaeEngine); ++ + // new rsa + RSA* rsa = RSA_new_method(kaeEngine); + if (rsa == NULL) { +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_provider.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_provider.c +index aa46e737e..fca035b04 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_provider.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_provider.c +@@ -24,21 +24,53 @@ + #include + #include + #include ++#include + #include "kae_exception.h" + #include "kae_util.h" + #include "org_openeuler_security_openssl_KAEProvider.h" + ++#define KAE_OPENSSL_LIBRARY "libcrypto.so" ++ + /* + * Class: Java_org_openeuler_security_openssl_KAEProvider + * Method: initOpenssl + * Signature: ()V + */ + JNIEXPORT void JNICALL Java_org_openeuler_security_openssl_KAEProvider_initOpenssl +- (JNIEnv *env, jclass cls) { ++ (JNIEnv *env, jclass cls, jboolean useGlobalMode, jstring engineId, jbooleanArray algorithmKaeFlags) { + SSL_load_error_strings(); + ERR_load_BIO_strings(); + OpenSSL_add_all_algorithms(); + ++ /* ++ * If the same shared object is opened again with dlopen(), the same object handle is returned. ++ * The dynamic linker maintains reference counts for object handles. ++ * An object that was previously opened with RTLD_LOCAL can be promoted to RTLD_GLOBAL in a subsequent dlopen(). ++ * ++ * RTLD_GLOBAL ++ * The symbols defined by this shared object will be made ++ * available for symbol resolution of subsequently loaded ++ * shared objects. ++ * RTLD_LOCAL ++ * This is the converse of RTLD_GLOBAL, and the default if ++ * neither flag is specified. Symbols defined in this shared ++ * object are not made available to resolve references in ++ * subsequently loaded shared objects. ++ * For more information see https://man7.org/linux/man-pages/man3/dlopen.3.html. ++ */ ++ if (useGlobalMode) { ++ char msg[1024]; ++ void *handle = NULL; ++ // Promote the flags of the loaded libcrypto.so library from RTLD_LOCAL to RTLD_GLOBAL ++ handle = dlopen(KAE_OPENSSL_LIBRARY, RTLD_LAZY | RTLD_GLOBAL); ++ if (handle == NULL) { ++ snprintf(msg, sizeof(msg), "Cannot load %s (%s)!", KAE_OPENSSL_LIBRARY, dlerror()); ++ KAE_ThrowByName(env, "java/lang/UnsatisfiedLinkError", msg); ++ return; ++ } ++ dlclose(handle); ++ } ++ + // check if KaeEngine holder is already set + ENGINE* e = GetKaeEngine(); + if (e != NULL) { +@@ -47,11 +79,25 @@ JNIEXPORT void JNICALL Java_org_openeuler_security_openssl_KAEProvider_initOpens + } + + // determine whether KAE is loaded successfully +- e = ENGINE_by_id("kae"); ++ const char* id = (*env)->GetStringUTFChars(env, engineId, 0); ++ e = ENGINE_by_id(id); ++ (*env)->ReleaseStringUTFChars(env, engineId, id); + if (e == NULL) { +- ERR_clear_error(); +- KAE_ThrowRuntimeException(env, "kae engine not found"); ++ KAE_ThrowFromOpenssl(env, "ENGINE_by_id", KAE_ThrowRuntimeException); + return; + } + SetKaeEngine(e); ++ ++ // initialize the engine for each algorithm ++ initEngines(env, algorithmKaeFlags); + } ++ ++/* ++ * Class: Java_org_openeuler_security_openssl_KAEProvider ++ * Method: getEngineFlags ++ * Signature: ()V ++ */ ++JNIEXPORT jbooleanArray JNICALL Java_org_openeuler_security_openssl_KAEProvider_getEngineFlags ++ (JNIEnv *env, jclass cls) { ++ return getEngineFlags(env); ++} +\ No newline at end of file +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_signature_rsa.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_signature_rsa.c +index e81dc1406..6c401356d 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_signature_rsa.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_signature_rsa.c +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include "kae_log.h" + #include "kae_util.h" + #include "kae_exception.h" + +@@ -99,8 +100,9 @@ JNIEXPORT jbyteArray JNICALL Java_org_openeuler_security_openssl_KAERSASignature + jbyte* digestBytes = NULL; + jbyte* sigBytes = NULL; + jbyteArray sigByteArray = NULL; +- static ENGINE* kaeEngine = NULL; +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); ++ KAE_TRACE("KAERSASignatureNative_rsaSign: kaeEngine => %p", kaeEngine); ++ + // new EVP_PKEY_CTX + if ((pkeyCtx = EVP_PKEY_CTX_new(pkey, kaeEngine)) == NULL) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_new", KAE_ThrowSignatureException); +@@ -163,8 +165,9 @@ JNIEXPORT jboolean JNICALL Java_org_openeuler_security_openssl_KAERSASignatureNa + jbyte* digestBytes = NULL; + jbyte* sigBytes = NULL; + jboolean isSuccess = JNI_FALSE; +- static ENGINE* kaeEngine = NULL; +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); ++ KAE_TRACE("KAERSASignatureNative_rsaVerify: kaeEngine => %p", kaeEngine); ++ + // new EVP_PKEY_CTX + if ((pkeyCtx = EVP_PKEY_CTX_new(pkey, kaeEngine)) == NULL) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_new", KAE_ThrowSignatureException); +@@ -255,8 +258,9 @@ JNIEXPORT jbyteArray JNICALL Java_org_openeuler_security_openssl_KAERSASignature + jbyte* digestBytes = NULL; + jbyte* sigBytes = NULL; + jbyteArray sigByteArray = NULL; +- static ENGINE* kaeEngine = NULL; +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); ++ KAE_TRACE("KAERSASignatureNative_pssSign: kaeEngine => %p", kaeEngine); ++ + // new EVP_PKEY_CTX + if ((pkeyCtx = EVP_PKEY_CTX_new(pkey, kaeEngine)) == NULL) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_new", KAE_ThrowSignatureException); +@@ -320,8 +324,9 @@ JNIEXPORT jboolean JNICALL Java_org_openeuler_security_openssl_KAERSASignatureNa + jbyte* digestBytes = NULL; + jbyte* sigBytes = NULL; + jboolean isSuccess = JNI_FALSE; +- static ENGINE* kaeEngine = NULL; +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); ++ KAE_TRACE("KAERSASignatureNative_pssVerify: kaeEngine => %p", kaeEngine); ++ + // new EVP_PKEY_CTX + if ((pkeyCtx = EVP_PKEY_CTX_new(pkey, kaeEngine)) == NULL) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_new", KAE_ThrowSignatureException); +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_symmetric_cipher.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_symmetric_cipher.c +index 71c28bdea..43f6326b2 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_symmetric_cipher.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_symmetric_cipher.c +@@ -142,16 +142,19 @@ Java_org_openeuler_security_openssl_KAESymmetricCipherBase_nativeInit(JNIEnv* en + jbyte* keyBytes = NULL; + jbyte* ivBytes = NULL; + const EVP_CIPHER* cipher = NULL; +- static ENGINE* kaeEngine = NULL; ++ ENGINE* kaeEngine = NULL; + + const char* algo = (*env)->GetStringUTFChars(env, cipherType, 0); + if (StartsWith("aes", algo)) { + cipher = EVPGetAesCipherByName(env, algo); +- kaeEngine = NULL; ++ kaeEngine = GetAesEngineByAlgorithmName(algo); + } else { + cipher = EVPGetSm4CipherByName(env, algo); +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ kaeEngine = GetSm4EngineByAlgorithmName(algo); + } ++ ++ KAE_TRACE("KAESymmetricCipherBase_nativeInit: kaeEngine => %p", kaeEngine); ++ + (*env)->ReleaseStringUTFChars(env, cipherType, algo); + if (cipher == NULL) { + KAE_ThrowOOMException(env, "create EVP_CIPHER fail"); +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_util.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_util.c +index 0e656a834..a16d944c4 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_util.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_util.c +@@ -22,6 +22,7 @@ + */ + + #include ++#include + #include "kae_util.h" + #include "kae_exception.h" + +@@ -55,7 +56,7 @@ BIGNUM* KAE_GetBigNumFromByteArray(JNIEnv* env, jbyteArray byteArray) { + + jbyte* bytes = (*env)->GetByteArrayElements(env, byteArray, NULL); + if (bytes == NULL) { +- KAE_ThrowNullPointerException(env,"GetByteArrayElements failed"); ++ KAE_ThrowNullPointerException(env, "GetByteArrayElements failed"); + goto cleanup; + } + BIGNUM* result = BN_bin2bn((const unsigned char*) bytes, len, bn); +@@ -109,3 +110,138 @@ cleanup: + (*env)->ReleaseByteArrayElements(env, javaBytes, bytes, 0); + return javaBytes; + } ++ ++#define ENGINE_LENGTH (EC_INDEX + 1) ++static ENGINE* engines[ENGINE_LENGTH] = {NULL}; ++static jboolean engineFlags[ENGINE_LENGTH] = {JNI_FALSE}; ++static KAEAlgorithm kaeAlgorithms[ENGINE_LENGTH] = { ++ {MD5_INDEX, "md5"}, ++ {SHA256_INDEX, "sha256"}, ++ {SHA384_INDEX, "sha384"}, ++ {SM3_INDEX, "sm3"}, ++ {AES_128_ECB_INDEX, "aes-128-ecb"}, ++ {AES_128_CBC_INDEX, "aes-128-cbc"}, ++ {AES_128_CTR_INDEX, "aes-128-ctr"}, ++ {AES_128_GCM_INDEX, "aes-128-gcm"}, ++ {AES_192_ECB_INDEX, "aes-192-ecb"}, ++ {AES_192_CBC_INDEX, "aes-192-cbc"}, ++ {AES_192_CTR_INDEX, "aes-192-ctr"}, ++ {AES_192_GCM_INDEX, "aes-192-gcm"}, ++ {AES_256_ECB_INDEX, "aes-256-ecb"}, ++ {AES_256_CBC_INDEX, "aes-256-cbc"}, ++ {AES_256_CTR_INDEX, "aes-256-ctr"}, ++ {AES_256_GCM_INDEX, "aes-256-gcm"}, ++ {SM4_ECB_INDEX, "sm4-ecb"}, ++ {SM4_CBC_INDEX, "sm4-cbc"}, ++ {SM4_CTR_INDEX, "sm4-ctr"}, ++ {SM4_OFB_INDEX, "sm4-ofb"}, ++ {HMAC_MD5_INDEX, "hmac-md5"}, ++ {HMAC_SHA1_INDEX, "hmac-sha1"}, ++ {HMAC_SHA224_INDEX, "hmac-sha224"}, ++ {HMAC_SHA256_INDEX, "hmac-sha256"}, ++ {HMAC_SHA384_INDEX, "hmac-sha384"}, ++ {HMAC_SHA512_INDEX, "hmac-sha512"}, ++ {RSA_INDEX, "rsa"}, ++ {DH_INDEX, "dh"}, ++ {EC_INDEX, "ec"} ++}; ++ ++void initEngines(JNIEnv* env, jbooleanArray algorithmKaeFlags) { ++ if (algorithmKaeFlags == NULL) { ++ return; ++ } ++ ++ // get jTemp ++ jboolean* jTemp = NULL; ++ int length = (*env)->GetArrayLength(env, algorithmKaeFlags); ++ jTemp = (jboolean*) malloc(length); ++ if (jTemp == NULL) { ++ KAE_ThrowOOMException(env, "initEngines GetArrayLength error"); ++ return; ++ } ++ (*env)->GetBooleanArrayRegion(env, algorithmKaeFlags, 0, length, jTemp); ++ ++ // assign engines ++ int minLen = length < ENGINE_LENGTH ? length : ENGINE_LENGTH; ++ int i; ++ for (i = 0; i < minLen; i++) { ++ if (jTemp[i]) { ++ engines[i] = kaeEngine; ++ engineFlags[i] = JNI_TRUE; ++ } ++ } ++ if (length < ENGINE_LENGTH) { ++ for (i = minLen; i < ENGINE_LENGTH; i++) { ++ engines[i] = kaeEngine; ++ engineFlags[i] = JNI_TRUE; ++ } ++ } ++ ++ // free jTemp ++ if (jTemp != NULL) { ++ free(jTemp); ++ } ++} ++ ++jbooleanArray getEngineFlags(JNIEnv* env) { ++ jbooleanArray array = (*env)->NewBooleanArray(env, ENGINE_LENGTH); ++ (*env)->SetBooleanArrayRegion(env, array, 0, ENGINE_LENGTH, engineFlags); ++ return array; ++} ++ ++ENGINE* GetEngineByAlgorithmIndex(AlgorithmIndex algorithmIndex) { ++ return engines[algorithmIndex]; ++} ++ ++/* ++ * Get the engine used by the specified algorithm. ++ * @param beginIndex the beginning index, inclusive. ++ * @param endIndex the ending index, exclusive. ++ * @param algorithmName algorithm name ++ * @return engine ++ */ ++ENGINE* GetEngineByBeginIndexAndEndIndex(int beginIndex, int endIndex, ++ const char* algorithmName) { ++ if (beginIndex < 0 || endIndex > ENGINE_LENGTH) { ++ return NULL; ++ } ++ ++ int i; ++ for (i = beginIndex; i < endIndex; i++) { ++ if (strcasecmp(kaeAlgorithms[i].algorithmName, algorithmName) == 0) { ++ return engines[kaeAlgorithms[i].algorithmIndex]; ++ } ++ } ++ return NULL; ++} ++ ++ENGINE* GetHmacEngineByAlgorithmName(const char* algorithmName) { ++ char prefix[] = {"hmac-"}; ++ int len = strlen(algorithmName); ++ int newLen = strlen(algorithmName) + strlen(prefix) + 1; ++ char* newAlgorithmName = NULL; ++ newAlgorithmName = malloc(newLen); ++ if (newAlgorithmName == NULL) { ++ return NULL; ++ } ++ strcpy(newAlgorithmName, prefix); ++ strcat(newAlgorithmName, algorithmName); ++ ENGINE* engine = GetEngineByBeginIndexAndEndIndex(HMAC_MD5_INDEX, HMAC_SHA512_INDEX + 1, newAlgorithmName); ++ if (newAlgorithmName != NULL) { ++ free(newAlgorithmName); ++ } ++ return engine; ++} ++ ++ENGINE* GetDigestEngineByAlgorithmName(const char* algorithmName) { ++ return GetEngineByBeginIndexAndEndIndex(MD5_INDEX, SM3_INDEX + 1, algorithmName); ++} ++ ++ENGINE* GetAesEngineByAlgorithmName(const char* algorithmName) { ++ return GetEngineByBeginIndexAndEndIndex(AES_128_ECB_INDEX, AES_256_GCM_INDEX + 1, algorithmName); ++} ++ ++ENGINE* GetSm4EngineByAlgorithmName(const char* algorithmName) { ++ return GetEngineByBeginIndexAndEndIndex(SM4_ECB_INDEX, SM4_OFB_INDEX + 1, algorithmName); ++} ++ +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_util.h b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_util.h +index 13bd5976d..347337509 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_util.h ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_util.h +@@ -27,6 +27,43 @@ + #include + #include + ++typedef enum { ++ MD5_INDEX, ++ SHA256_INDEX, ++ SHA384_INDEX, ++ SM3_INDEX, ++ AES_128_ECB_INDEX, ++ AES_128_CBC_INDEX, ++ AES_128_CTR_INDEX, ++ AES_128_GCM_INDEX, ++ AES_192_ECB_INDEX, ++ AES_192_CBC_INDEX, ++ AES_192_CTR_INDEX, ++ AES_192_GCM_INDEX, ++ AES_256_ECB_INDEX, ++ AES_256_CBC_INDEX, ++ AES_256_CTR_INDEX, ++ AES_256_GCM_INDEX, ++ SM4_ECB_INDEX, ++ SM4_CBC_INDEX, ++ SM4_CTR_INDEX, ++ SM4_OFB_INDEX, ++ HMAC_MD5_INDEX, ++ HMAC_SHA1_INDEX, ++ HMAC_SHA224_INDEX, ++ HMAC_SHA256_INDEX, ++ HMAC_SHA384_INDEX, ++ HMAC_SHA512_INDEX, ++ RSA_INDEX, ++ DH_INDEX, ++ EC_INDEX ++} AlgorithmIndex; ++ ++typedef struct { ++ AlgorithmIndex algorithmIndex; ++ const char* algorithmName; ++} KAEAlgorithm; ++ + /* jbyteArray convert to BIGNUM */ + BIGNUM* KAE_GetBigNumFromByteArray(JNIEnv* env, jbyteArray byteArray); + +@@ -40,8 +77,18 @@ void SetKaeEngine(ENGINE* engine); + + ENGINE* GetKaeEngine(); + +-void SetKaeEngine(ENGINE* engine); ++void initEngines(JNIEnv* env, jbooleanArray algorithmKaeFlags); + +-ENGINE* GetKaeEngine(); ++jbooleanArray getEngineFlags(JNIEnv* env); ++ ++ENGINE* GetEngineByAlgorithmIndex(AlgorithmIndex algorithmIndex); ++ ++ENGINE* GetHmacEngineByAlgorithmName(const char* algorithmName); ++ ++ENGINE* GetDigestEngineByAlgorithmName(const char* algorithmName); ++ ++ENGINE* GetAesEngineByAlgorithmName(const char* algorithmName); ++ ++ENGINE* GetSm4EngineByAlgorithmName(const char* algorithmName); + + #endif +diff --git a/jdk/test/org/openeuler/security/openssl/AESTest.java b/jdk/test/org/openeuler/security/openssl/AESTest.java +new file mode 100644 +index 000000000..77da5ecc2 +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/AESTest.java +@@ -0,0 +1,114 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import org.openeuler.security.openssl.KAEProvider; ++ ++import java.nio.charset.StandardCharsets; ++import java.security.Security; ++import java.security.spec.AlgorithmParameterSpec; ++import javax.crypto.Cipher; ++import javax.crypto.KeyGenerator; ++import javax.crypto.SecretKey; ++import javax.crypto.spec.IvParameterSpec; ++ ++/** ++ * @test ++ * @summary Basic test for AES ++ * @requires os.arch=="aarch64" ++ * @run main AESTest ++ */ ++ ++public class AESTest { ++ private static final String[] ALGORITHM = {"AES", "AES_128", "AES_192", "AES_256"}; ++ private static final String[] MODES = {"ECB", "CBC", "CTR", "GCM"}; ++ private static final String[] PADDING = {"NoPadding", "PKCS5Padding"}; ++ private static final int AES_128_KEY_LENGTH = 128; ++ private static final int AES_192_KEY_LENGTH = 192; ++ private static final int AES_256_KEY_LENGTH = 256; ++ private static String plainText = "helloworldhellow"; // 16bytes for NoPadding ++ private static String shortPlainText = "helloworld"; // 5 bytes for padding ++ ++ public static void main(String[] args) throws Exception { ++ Security.insertProviderAt(new KAEProvider(), 1); ++ for (String algo : ALGORITHM) { ++ for (String mode : MODES) { ++ int padKinds = 2; ++ if (mode.equalsIgnoreCase("CTR")) { ++ padKinds = 1; ++ } ++ for (int k = 0; k < padKinds; k++) { ++ test(algo, mode, PADDING[k]); ++ } ++ } ++ } ++ } ++ ++ public static void test(String algo, String mo, String pad) throws Exception { ++ AlgorithmParameterSpec aps = null; ++ ++ Cipher cipher = Cipher.getInstance(algo + "/" + mo + "/" + pad); ++ ++ KeyGenerator kg = KeyGenerator.getInstance("AES"); ++ if (algo.equalsIgnoreCase("AES_192")) { ++ kg.init(AES_192_KEY_LENGTH); ++ } else if (algo.equalsIgnoreCase("AES_256")) { ++ kg.init(AES_256_KEY_LENGTH); ++ } else { ++ kg.init(AES_128_KEY_LENGTH); ++ } ++ ++ SecretKey key = kg.generateKey(); ++ ++ // encrypt ++ if (!mo.equalsIgnoreCase("GCM")) { ++ cipher.init(Cipher.ENCRYPT_MODE, key, aps); ++ } else { ++ cipher.init(Cipher.ENCRYPT_MODE, key); ++ } ++ ++ String cipherString = null; ++ if (!pad.equalsIgnoreCase("NoPadding")) { ++ cipherString = shortPlainText; ++ } else { ++ cipherString = plainText; ++ } ++ byte[] cipherText = cipher.doFinal(cipherString.getBytes(StandardCharsets.UTF_8)); ++ if (!mo.equalsIgnoreCase("ECB")) { ++ aps = new IvParameterSpec(cipher.getIV()); ++ } else { ++ aps = null; ++ } ++ ++ if (!mo.equalsIgnoreCase("GCM")) { ++ cipher.init(Cipher.DECRYPT_MODE, key, aps); ++ } else { ++ cipher.init(Cipher.DECRYPT_MODE, key, cipher.getParameters()); ++ } ++ ++ String decryptPlainText = new String(cipher.doFinal(cipherText)); ++ ++ if (!cipherString.equals(decryptPlainText)) { ++ throw new RuntimeException("aes decryption failed, algo = " + algo + ", mo = " + mo + ", pad = " + pad); ++ } ++ } ++} +diff --git a/jdk/test/org/openeuler/security/openssl/DHTest.java b/jdk/test/org/openeuler/security/openssl/DHTest.java +index 6eb5e7c96..ee5d63684 100644 +--- a/jdk/test/org/openeuler/security/openssl/DHTest.java ++++ b/jdk/test/org/openeuler/security/openssl/DHTest.java +@@ -28,7 +28,6 @@ import java.io.Serializable; + import java.math.BigInteger; + import java.security.*; + import java.util.Arrays; +-import java.util.Date; + import javax.crypto.KeyAgreement; + import javax.crypto.spec.*; + import org.openeuler.security.openssl.KAEProvider; +@@ -75,18 +74,16 @@ import org.openeuler.security.openssl.KAEProvider; + /** + * @test + * @summary Basic test for DH ++ * @requires os.arch=="aarch64" + * @run main DHTest + */ + +-final class DHTest implements Serializable { +- private static int bitLength = 8192; ++public class DHTest implements Serializable { + private static BigInteger g512; + private static BigInteger p512; +- Throwable t = null; + + private static volatile Provider sunJceProvider; + private static volatile Provider kaeProvider; +- Date d = new Date(); + + public static void main(String[] args) throws Exception { + Security.addProvider(new KAEProvider()); +@@ -97,8 +94,6 @@ final class DHTest implements Serializable { + + p512 = new BigInteger("27672987386729926592037876826877634387173876890702920770064392919138769821035856568775311919542560094764667151024449425954917954337048895981297730855891532066350935045229294626339548842381843985759061682551900379979643117695834175891578650111093016914264824311693147701566019122696621248493126219217339690346346921463135605151471303957324058301097079967414639146647429422884520134312590056632178576758580657240245655739869017244657144448267757255018625514803292549109401806336918448001843022629625467069714240279603204909633404992842479161100500474744098408277938070656334892106100534117209709263785505019003765693651"); + +- DHTest.bitLength = 0; +- + DHParameterSpec dhParams = new DHParameterSpec(p512, g512); + KeyPairGenerator SunJCEkeyGen = KeyPairGenerator.getInstance("DH", sunJceProvider); + KeyPairGenerator KAEkeyGen = KeyPairGenerator.getInstance("DH", kaeProvider); +diff --git a/jdk/test/org/openeuler/security/openssl/DigestTest.java b/jdk/test/org/openeuler/security/openssl/DigestTest.java +new file mode 100644 +index 000000000..a293f7268 +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/DigestTest.java +@@ -0,0 +1,60 @@ ++import org.openeuler.security.openssl.KAEProvider; ++ ++import java.nio.charset.StandardCharsets; ++import java.security.MessageDigest; ++import java.security.Security; ++import java.util.Arrays; ++import java.util.HashMap; ++import java.util.Map; ++ ++/** ++ * @test ++ * @summary Basic test for MD5 SHA256 SHA384 ++ * @requires os.arch=="aarch64" ++ * @run main/othervm DigestTest ++ */ ++public class DigestTest { ++ private static String PLAIN_TEXT = "hello world"; ++ ++ private static Map alg = new HashMap(); ++ ++ static { ++ alg.put("MD5", new byte[] {94, -74, 59, -69, -32, 30, -18, -48, -109, -53, 34, -69, -113, 90, -51, -61}); ++ alg.put( ++ "SHA-256", ++ new byte[] { ++ -71, 77, 39, -71, -109, 77, 62, 8, -91, 46, 82, -41, -38, 125, -85, -6, ++ -60, -124, -17, -29, 122, 83, -128, -18, -112, -120, -9, -84, -30, -17, -51, -23 ++ }); ++ alg.put( ++ "SHA-384", ++ new byte[] { ++ -3, -67, -114, 117, -90, 127, 41, -9, 1, -92, -32, 64, 56, 94, 46, 35, ++ -104, 99, 3, -22, 16, 35, -110, 17, -81, -112, 127, -53, -72, 53, 120, -77, ++ -28, 23, -53, 113, -50, 100, 110, -3, 8, 25, -35, -116, 8, -115, -31, -67 ++ }); ++ alg.put( ++ "SM3", ++ new byte[] { ++ 68, -16, 6, 30, 105, -6, 111, -33, -62, -112, -60, -108, 101, 74, 5, ++ -36, 12, 5, 61, -89, -27, -59, 43, -124, -17, -109, -87, -42, 125, 63, ++ -1, -120 ++ }); ++ } ++ ++ public static void main(String[] args) throws Exception { ++ Security.insertProviderAt(new KAEProvider(), 1); ++ for (String key : alg.keySet()) { ++ test(PLAIN_TEXT, key, alg.get(key)); ++ } ++ } ++ ++ public static void test(String plainText, String algo, byte[] expectRes) throws Exception { ++ MessageDigest md = MessageDigest.getInstance(algo); ++ md.update(plainText.getBytes(StandardCharsets.UTF_8)); ++ byte[] res = md.digest(); ++ if (!Arrays.equals(res, expectRes)) { ++ throw new RuntimeException(algo + " failed"); ++ } ++ } ++} +diff --git a/jdk/test/org/openeuler/security/openssl/ECDHTest.java b/jdk/test/org/openeuler/security/openssl/ECDHTest.java +index 590c31154..069c32295 100644 +--- a/jdk/test/org/openeuler/security/openssl/ECDHTest.java ++++ b/jdk/test/org/openeuler/security/openssl/ECDHTest.java +@@ -41,6 +41,7 @@ import java.nio.charset.StandardCharsets; + /** + * @test + * @summary Basic test for ECDH ++ * @requires os.arch=="aarch64" + * @run main ECDHTest + */ + +diff --git a/jdk/test/org/openeuler/security/openssl/HmacTest.java b/jdk/test/org/openeuler/security/openssl/HmacTest.java +new file mode 100644 +index 000000000..9ff328629 +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/HmacTest.java +@@ -0,0 +1,88 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import org.openeuler.security.openssl.KAEProvider; ++ ++import javax.crypto.Mac; ++import javax.crypto.spec.SecretKeySpec; ++import java.security.Key; ++import java.security.Security; ++import java.util.Arrays; ++ ++/** ++ * @test ++ * @summary test for Hmac ++ * @requires os.arch=="aarch64" ++ * @run main/othervm HmacTest ++ */ ++public class HmacTest { ++ private static final byte[] PLAIN_BYTES = "hello world".getBytes(); ++ private static final String[] ALGORITHMS = new String[]{ ++ "HmacMD5", ++ "HmacSHA1", ++ "HmacSHA224", ++ "HmacSHA256", ++ "HmacSHA384", ++ "HmacSHA512", ++ }; ++ private static final byte[][] EXPECTED_BYTES = { ++ {-40, 63, -96, 13, 107, -33, -1, -53, -116, 117, 75, -6, 85, -88, -112, -90}, ++ {-68, 104, 112, -36, 123, 123, -92, 104, 89, -90, 63, 56, 84, 45, 12, -7, 41, 103, -105, -27}, ++ {-31, 0, 103, 51, -119, -61, 2, -76, -83, -113, 95, 86, 8, 46, 91, 20, ++ -15, -23, -71, 62, -50, 86, -54, 71, -94, -47, -103, 43}, ++ {-69, -83, -3, 7, 61, 38, -122, -59, 7, -53, 106, 114, 58, 102, 65, -118, ++ 54, -50, 116, -56, 110, 54, -71, 36, 60, 84, 14, 97, 78, 18, -119, -24}, ++ {100, -58, 106, 64, -96, 91, 99, -33, 36, -78, -53, -50, -78, 116, -110, 85, ++ 84, -5, -63, 17, 51, -69, -39, -122, 65, 8, -122, -43, 39, 13, -41, -52, ++ 45, -38, -59, 70, 17, -87, -63, -126, 4, 120, -77, 71, 119, 96, -2, -68}, ++ {-89, 47, -98, -12, 110, -88, 23, 2, 28, 26, -71, 53, -108, 54, -52, 1, ++ -121, -121, 87, 6, -78, 123, -14, -86, 127, 114, 124, -73, -98, 79, -122, 69, ++ -32, 50, 48, -79, -110, 66, 38, 70, -3, -76, 95, 55, 74, 48, 57, -121, ++ 22, 60, -83, -109, 59, 79, 0, -49, 107, 88, -82, -35, 87, -36, 49, -54} ++ }; ++ private static final Key key = new SecretKeySpec("mac".getBytes(), ""); ++ ++ public static void main(String[] args) throws Exception { ++ Security.insertProviderAt(new KAEProvider(), 1); ++ for (int i = 0; i < ALGORITHMS.length; i++) { ++ test(ALGORITHMS[i], key, PLAIN_BYTES, EXPECTED_BYTES[i]); ++ } ++ } ++ ++ private static void test(String algorithm, Key key, byte[] inputBytes, byte[] expectedBytes) throws Exception { ++ Mac mac = Mac.getInstance(algorithm); ++ mac.init(key); ++ mac.update(inputBytes); ++ byte[] bytes = mac.doFinal(); ++ if (!(mac.getProvider() instanceof KAEProvider)) { ++ throw new RuntimeException(algorithm + " failed," + ++ "provider=" + mac.getProvider().getClass() + "," + ++ "expectedProvider=" + KAEProvider.class); ++ } ++ if (!Arrays.equals(bytes, expectedBytes)) { ++ throw new RuntimeException(algorithm + " failed," + ++ "bytes=" + Arrays.toString(bytes) + "," + ++ "expectedBytes=" + Arrays.toString(expectedBytes)); ++ } ++ } ++} +diff --git a/jdk/test/org/openeuler/security/openssl/KAEConfTest.java b/jdk/test/org/openeuler/security/openssl/KAEConfTest.java +new file mode 100644 +index 000000000..9028d28b5 +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/KAEConfTest.java +@@ -0,0 +1,121 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import org.openeuler.security.openssl.KAEConfig; ++import org.openeuler.security.openssl.KAEProvider; ++ ++import java.io.File; ++import java.io.FileWriter; ++import java.io.IOException; ++import java.nio.file.Files; ++import java.util.ArrayList; ++import java.util.List; ++ ++/* ++ * @test ++ * @summary Test KAE Conf ++ * @requires os.arch=="aarch64" ++ * @run main/othervm KAEConfTest DEFAULT ++ * @run main/othervm KAEConfTest SPECIFY ++ */ ++public class KAEConfTest { ++ private static final String DEFAULT_CONF = System.getProperty("java.home") + ++ File.separator + "lib" + File.separator + "kaeprovider.conf"; ++ ++ private static final String SPECIFY_CONF = System.getProperty("user.dir") + ++ File.separator + "kaeprovider.conf"; ++ ++ private static final String SPECIFY_LOG_PATH = System.getProperty("user.dir") + File.separator + "kae.log"; ++ private static final List files = new ArrayList<>(); ++ ++ enum Mode { ++ DEFAULT, ++ SPECIFY ++ } ++ ++ public static void main(String[] args) throws IOException { ++ Mode mode = getMode(args); ++ try { ++ init(mode); ++ new KAEProvider(); ++ test(mode); ++ } finally { ++ KAETestHelper.cleanUp(files); ++ } ++ } ++ ++ private static Mode getMode(String[] args) { ++ if (args.length <= 0) { ++ return Mode.DEFAULT; ++ } ++ return Mode.valueOf(args[0]); ++ } ++ ++ private static void init(Mode mode) throws IOException { ++ if (Mode.SPECIFY.equals(mode)) { ++ System.setProperty("kae.conf", SPECIFY_CONF); ++ File file = new File(SPECIFY_CONF); ++ if (!file.exists()) { ++ Files.createFile(file.toPath()); ++ } ++ files.add(file); ++ try (FileWriter fileWriter = new FileWriter(file)) { ++ fileWriter.write("kae.log=true"); ++ fileWriter.flush(); ++ } ++ } ++ } ++ ++ private static void testDefault() { ++ File file = new File(DEFAULT_CONF); ++ if (!file.exists()) { ++ throw new RuntimeException("test failed"); ++ } ++ } ++ ++ private static void testSpecify() { ++ String value = KAEConfig.privilegedGetOverridable("kae.log"); ++ if (!"true".equals(value)) { ++ throw new RuntimeException("test failed : kae.log=" + value); ++ } ++ File file = new File(SPECIFY_LOG_PATH); ++ if (!file.exists()) { ++ throw new RuntimeException(SPECIFY_LOG_PATH + "does not exist"); ++ } ++ // kae log file ++ files.add(file); ++ } ++ ++ private static void test(Mode mode) { ++ switch (mode) { ++ case DEFAULT: ++ testDefault(); ++ break; ++ case SPECIFY: ++ testSpecify(); ++ break; ++ default: ++ throw new IllegalArgumentException("invalid mode"); ++ } ++ } ++} +diff --git a/jdk/test/org/openeuler/security/openssl/KAEDisabledAlgorithmsTest.java b/jdk/test/org/openeuler/security/openssl/KAEDisabledAlgorithmsTest.java +new file mode 100644 +index 000000000..6301b6d76 +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/KAEDisabledAlgorithmsTest.java +@@ -0,0 +1,164 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import org.openeuler.security.openssl.KAEConfig; ++import org.openeuler.security.openssl.KAEProvider; ++ ++import java.util.HashSet; ++import java.util.Set; ++ ++/* ++ * @test ++ * @summary Test property kae.engine.disableAlgorithms ++ * @requires os.arch=="aarch64" ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=md5 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=sha256 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=sha384 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=sm3 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-128-ecb KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-128-cbc KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-128-ctr KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-128-gcm KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-192-ecb KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-192-cbc KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-192-ctr KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-192-gcm KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-256-ecb KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-256-cbc KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-256-ctr KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-256-gcm KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=sm4-ecb KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=sm4-cbc KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=sm4-ctr KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=sm4-ofb KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=hmac-md5 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=hmac-sha1 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=hmac-sha224 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=hmac-sha256 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=hmac-sha384 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=hmac-sha512 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=rsa KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=dh KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=ec KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-128-gcm,aes-192-gcm,aes-256-gcm KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=md5,aes-128-ecb,sm4-ecb,hmac-sha1,rsa,dh,ec KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=md5 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=sha256 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=sha384 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=sm3 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-128-ecb KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-128-cbc KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-128-ctr KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-128-gcm KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-192-ecb KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-192-cbc KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-192-ctr KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-192-gcm KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-256-ecb KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-256-cbc KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-256-ctr KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-256-gcm KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=sm4-ecb KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=sm4-cbc KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=sm4-ctr KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=sm4-ofb KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=hmac-md5 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=hmac-sha1 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=hmac-sha224 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=hmac-sha256 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=hmac-sha384 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=hmac-sha512 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=rsa KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=dh KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=ec KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-128-gcm,aes-192-gcm,aes-256-gcm KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=md5,aes-128-ecb,sm4-ecb,hmac-sha1,rsa,dh,ec KAEDisabledAlgorithmsTest ++ */ ++public class KAEDisabledAlgorithmsTest { ++ ++ public static void main(String[] args) { ++ KAETestHelper.Engine engine = KAETestHelper.getEngine(); ++ if (!engine.isValid()) { ++ System.out.println("Skip test, engine " + engine.getEngineId() + " does not exist."); ++ return; ++ } ++ String[] disabledAlgorithms = getDisabledAlgorithms(); ++ init(); ++ new KAEProvider(); ++ test(disabledAlgorithms); ++ } ++ ++ private static final String[] PROPERTY_NAMES = new String[]{ ++ "kae.digest.useKaeEngine", ++ "kae.aes.useKaeEngine", ++ "kae.sm4.useKaeEngine", ++ "kae.hmac.useKaeEngine", ++ "kae.rsa.useKaeEngine", ++ "kae.dh.useKaeEngine", ++ "kae.ec.useKaeEngine" ++ }; ++ ++ private static String[] getDisabledAlgorithms() { ++ String value = System.getProperty("kae.engine.disabledAlgorithms"); ++ if (value == null) { ++ return new String[0]; ++ } ++ return value.split(","); ++ } ++ ++ private static void init() { ++ for (String propertyName : PROPERTY_NAMES) { ++ System.setProperty(propertyName, "true"); ++ } ++ } ++ ++ private static void test(String[] disabledAlgorithms) { ++ boolean[] useKaeEngineFlags = KAEConfig.getUseKaeEngineFlags(); ++ Set disabledAlgorithmIndexSet = new HashSet<>(); ++ ++ // test disabled algorithms ++ for (String disabledAlgorithm : disabledAlgorithms) { ++ Integer index = KAETestHelper.getAlgorithmIndex(disabledAlgorithm); ++ if (index == null || index < 0 || index >= useKaeEngineFlags.length) { ++ continue; ++ } ++ if (useKaeEngineFlags[index]) { ++ throw new RuntimeException("test failed"); ++ } ++ disabledAlgorithmIndexSet.add(index); ++ } ++ ++ // test other algorithms that are not disabled (except ec) ++ for (int i = 0; i < useKaeEngineFlags.length - 1; i++) { ++ if (!disabledAlgorithmIndexSet.contains(i) && !useKaeEngineFlags[i]) { ++ throw new RuntimeException(KAETestHelper.getAlgorithmName(i) + " algorithm is not disabled"); ++ } ++ } ++ ++ // test whether the ec algorithm is disabled by default ++ if (useKaeEngineFlags[useKaeEngineFlags.length - 1]) { ++ throw new RuntimeException(KAETestHelper.getAlgorithmName(useKaeEngineFlags.length - 1) ++ + " algorithm is disabled by default"); ++ } ++ } ++} +diff --git a/jdk/test/org/openeuler/security/openssl/KAEEngineIdTest.java b/jdk/test/org/openeuler/security/openssl/KAEEngineIdTest.java +new file mode 100644 +index 000000000..2ddaf6712 +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/KAEEngineIdTest.java +@@ -0,0 +1,76 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import org.openeuler.security.openssl.KAEProvider; ++ ++import java.io.BufferedReader; ++import java.io.File; ++import java.io.FileReader; ++import java.io.IOException; ++import java.util.ArrayList; ++import java.util.List; ++ ++/* ++ * @test ++ * @summary Test KAE property kae.engine.id and kae.libcrypto.useGlobalMode ++ * @requires os.arch=="aarch64" ++ * @run main/othervm -Dkae.log=true KAEEngineIdTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=kae KAEEngineIdTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true KAEEngineIdTest ++ */ ++public class KAEEngineIdTest { ++ ++ private static final String LOG_PATH = System.getProperty("user.dir") + ++ File.separator + "kae.log"; ++ ++ private static final List files = new ArrayList<>(); ++ ++ public static void main(String[] args) throws IOException { ++ KAETestHelper.Engine engine = KAETestHelper.getEngine(); ++ if (!engine.isValid()) { ++ System.out.println("Skip test, engine " + engine.getEngineId() + " does not exist."); ++ return; ++ } ++ ++ try { ++ new KAEProvider(); ++ test(engine); ++ } finally { ++ KAETestHelper.cleanUp(files); ++ } ++ } ++ ++ private static void test(KAETestHelper.Engine engine) throws IOException { ++ File file = new File(LOG_PATH); ++ if (!file.exists()) { ++ throw new RuntimeException(LOG_PATH + " does not exist"); ++ } ++ files.add(file); ++ try (BufferedReader bufferedReader = new BufferedReader(new FileReader(file))) { ++ String s = bufferedReader.readLine(); ++ if (!s.contains(engine.getEngineId() + " engine was found")) { ++ throw new RuntimeException("test failed"); ++ } ++ } ++ } ++} +diff --git a/jdk/test/org/openeuler/security/openssl/KAELogTest.java b/jdk/test/org/openeuler/security/openssl/KAELogTest.java +new file mode 100644 +index 000000000..31c8f5d99 +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/KAELogTest.java +@@ -0,0 +1,126 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import org.openeuler.security.openssl.KAEProvider; ++ ++import java.io.File; ++import java.util.ArrayList; ++import java.util.List; ++ ++/* ++ * @test ++ * @summary Test KAE log ++ * @requires os.arch=="aarch64" ++ * @run main/othervm KAELogTest ++ * @run main/othervm -Dkae.log=false KAELogTest ++ * @run main/othervm -Dkae.log=true KAELogTest ++ * @run main/othervm -Dkae.log=true -Dkae.log.file=./KAELogTest/kae.log KAELogTest ++ */ ++public class KAELogTest { ++ private static final String DEFAULT_LOG_PATH = System.getProperty("user.dir") + ++ File.separator + "kae.log"; ++ ++ private static final String SPECIFY_LOG_PATH = System.getProperty("user.dir") + ++ File.separator + "KAELogTest" + File.separator + "kae.log"; ++ ++ private static final List files = new ArrayList<>(); ++ ++ enum Mode { ++ DEFAULT, ++ DISABLE, ++ ENABLE, ++ SPECIFY ++ } ++ ++ public static void main(String[] args) { ++ Mode mode = getMode(); ++ try { ++ new KAEProvider(); ++ test(mode); ++ } finally { ++ KAETestHelper.cleanUp(files); ++ } ++ } ++ ++ private static Mode getMode() { ++ String enableKaeLog = System.getProperty("kae.log"); ++ if (enableKaeLog == null) { ++ return Mode.DEFAULT; ++ } else if ("false".equals(enableKaeLog)) { ++ return Mode.DISABLE; ++ } else { ++ String logPath = System.getProperty("kae.log.file"); ++ if (logPath == null) { ++ return Mode.ENABLE; ++ } ++ return Mode.SPECIFY; ++ } ++ } ++ ++ private static void testDefault() { ++ testDisable(); ++ } ++ ++ private static void testDisable() { ++ File file = new File(DEFAULT_LOG_PATH); ++ if (file.exists()) { ++ throw new RuntimeException("test failed"); ++ } ++ } ++ ++ private static void testEnable() { ++ File file = new File(DEFAULT_LOG_PATH); ++ if (!file.exists()) { ++ throw new RuntimeException("test failed"); ++ } ++ files.add(file); ++ } ++ ++ private static void testSpecify() { ++ File file = new File(KAELogTest.SPECIFY_LOG_PATH); ++ if (!file.exists()) { ++ throw new RuntimeException("test failed"); ++ } ++ files.add(file); ++ files.add(file.getParentFile()); ++ } ++ ++ private static void test(Mode mode) { ++ switch (mode) { ++ case DEFAULT: ++ testDefault(); ++ break; ++ case DISABLE: ++ testDisable(); ++ break; ++ case ENABLE: ++ testEnable(); ++ break; ++ case SPECIFY: ++ testSpecify(); ++ break; ++ default: ++ throw new IllegalArgumentException("invalid mode"); ++ } ++ } ++} +diff --git a/jdk/test/org/openeuler/security/openssl/KAETestHelper.java b/jdk/test/org/openeuler/security/openssl/KAETestHelper.java +new file mode 100644 +index 000000000..31e22493a +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/KAETestHelper.java +@@ -0,0 +1,209 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import java.io.BufferedReader; ++import java.io.File; ++import java.io.FileReader; ++import java.io.IOException; ++import java.util.HashMap; ++import java.util.List; ++import java.util.Map; ++ ++class KAETestHelper { ++ private static final String KAE_ENGINE_ID = "kae"; ++ private static final String UADK_ENGINE_ID = "uadk_engine"; ++ private static boolean hasKaeEngine; ++ private static boolean hasUadkEngine; ++ ++ private static String engineRootPath; ++ ++ // algorithm names ++ private static final String[] ALGORITHM_NAMES = new String[]{ ++ "md5", ++ "sha256", ++ "sha384", ++ "sm3", ++ "aes-128-ecb", ++ "aes-128-cbc", ++ "aes-128-ctr", ++ "aes-128-gcm", ++ "aes-192-ecb", ++ "aes-192-cbc", ++ "aes-192-ctr", ++ "aes-192-gcm", ++ "aes-256-ecb", ++ "aes-256-cbc", ++ "aes-256-ctr", ++ "aes-256-gcm", ++ "sm4-ecb", ++ "sm4-cbc", ++ "sm4-ctr", ++ "sm4-ofb", ++ "hmac-md5", ++ "hmac-sha1", ++ "hmac-sha224", ++ "hmac-sha256", ++ "hmac-sha384", ++ "hmac-sha512", ++ "rsa", ++ "dh", ++ "ec" ++ }; ++ private static final Map ALGORITHM_NAME_MAP = new HashMap<>(); ++ ++ private static final String PROVIDER_NAME = "KAEProvider"; ++ private static final String USE_OPENSSL_MSG = "Use openssl soft calculation"; ++ private static final String USE_KAE_HARDWARE_MSG = "enable KAE hardware acceleration"; ++ private static final Map ALGORITHM_MSG_MAP = new HashMap<>(); ++ ++ static { ++ init(); ++ } ++ ++ enum Engine { ++ default_engine(hasKaeEngine, KAE_ENGINE_ID), ++ kae(hasKaeEngine, KAE_ENGINE_ID), ++ uadk_engine(hasUadkEngine, UADK_ENGINE_ID); ++ private final boolean isValid; ++ private final String engineId; ++ ++ Engine(boolean isValid, String engineId) { ++ this.isValid = isValid; ++ this.engineId = engineId; ++ } ++ ++ public boolean isValid() { ++ return isValid; ++ } ++ ++ public String getEngineId() { ++ return engineId; ++ } ++ } ++ ++ private static void init() { ++ engineRootPath = System.getenv("OPENSSL_ENGINES"); ++ if (engineRootPath == null || engineRootPath.equals("")) { ++ System.out.println("Environment variable OPENSSL_ENGINES is not configured"); ++ } ++ hasKaeEngine = hasEngine(KAE_ENGINE_ID); ++ hasUadkEngine = hasEngine(UADK_ENGINE_ID); ++ ++ for (int i = 0; i < ALGORITHM_NAMES.length; i++) { ++ ALGORITHM_NAME_MAP.put(ALGORITHM_NAMES[i], i); ++ } ++ ++ ALGORITHM_MSG_MAP.put(USE_OPENSSL_MSG, false); ++ ALGORITHM_MSG_MAP.put(USE_KAE_HARDWARE_MSG, true); ++ } ++ ++ static Integer getAlgorithmIndex(String algorithmName) { ++ return ALGORITHM_NAME_MAP.get(algorithmName); ++ } ++ ++ static String getAlgorithmName(Integer algorithmIndex) { ++ return ALGORITHM_NAMES[algorithmIndex]; ++ } ++ ++ private static boolean hasEngine(String engineId) { ++ String filePath = engineRootPath + File.separator + engineId + ".so"; ++ File file = new File(filePath); ++ return file.exists(); ++ } ++ ++ static boolean hasKaeEngine() { ++ return hasKaeEngine; ++ } ++ ++ static boolean hasUadkEngine() { ++ return hasUadkEngine; ++ } ++ ++ static void cleanUp(List files) { ++ for (File file : files) { ++ System.out.println("delete file : " + file); ++ file.delete(); ++ } ++ } ++ ++ static boolean[] parseLog(Engine engine, File file) throws IOException { ++ boolean[] kaeUseEngineFlags; ++ String expectedEngineMsg = engine.getEngineId() + " engine was found"; ++ try (BufferedReader reader = new BufferedReader(new FileReader(file))) { ++ // load engine message ++ String engineMsg = reader.readLine(); ++ if (engineMsg == null || !engineMsg.contains(expectedEngineMsg)) { ++ throw new RuntimeException("test failed : actual message :" + engineMsg); ++ } ++ ++ // summary message ++ String summaryMessage = reader.readLine(); ++ if (summaryMessage == null) { ++ throw new RuntimeException("test failed : summary message is null"); ++ } ++ ++ kaeUseEngineFlags = new boolean[ALGORITHM_NAMES.length]; ++ // strategy of each algorithm ++ String strategy; ++ while ((strategy = reader.readLine()) != null) { ++ String[] splitArray = strategy.split("=>"); ++ if (splitArray.length < 2) { ++ throw new RuntimeException("test failed : strategy = " + strategy); ++ } ++ ++ // algorithm Index ++ String algorithm = splitArray[0].replace(" ", ""); ++ Integer algorithmIndex = ALGORITHM_NAME_MAP.get(algorithm); ++ if (algorithmIndex == null) { ++ throw new RuntimeException("test failed : illegal algorithm " + algorithm); ++ } ++ ++ // provider and algorithm value ++ String detail = splitArray[1]; ++ String[] detailArray = detail.split(":"); ++ if (detailArray.length < 2) { ++ throw new RuntimeException("test failed : detail=" + strategy); ++ } ++ String provider = detailArray[0].replace(" ", ""); ++ if (!PROVIDER_NAME.equals(provider)) { ++ throw new RuntimeException("test failed : provider= " + provider); ++ } ++ String algorithmMsg = detailArray[1].trim(); ++ Boolean algorithmValue = ALGORITHM_MSG_MAP.get(algorithmMsg); ++ if (algorithmValue == null) { ++ throw new RuntimeException("test failed : algorithmMsg= " + algorithmMsg); ++ } ++ kaeUseEngineFlags[algorithmIndex] = algorithmValue; ++ } ++ } ++ return kaeUseEngineFlags; ++ } ++ ++ static KAETestHelper.Engine getEngine() { ++ String engineId = System.getProperty("kae.engine.id"); ++ if (engineId == null) { ++ return KAETestHelper.Engine.default_engine; ++ } ++ return KAETestHelper.Engine.valueOf(engineId); ++ } ++} +diff --git a/jdk/test/org/openeuler/security/openssl/KAEUseEngineTest.java b/jdk/test/org/openeuler/security/openssl/KAEUseEngineTest.java +new file mode 100644 +index 000000000..4e57f775e +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/KAEUseEngineTest.java +@@ -0,0 +1,262 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import org.openeuler.security.openssl.KAEProvider; ++ ++import java.io.File; ++import java.io.IOException; ++import java.util.ArrayList; ++import java.util.Arrays; ++import java.util.HashMap; ++import java.util.List; ++import java.util.Map; ++ ++/* ++ * @test ++ * @summary Test KAE property kae..useKaeEngine ++ * @requires os.arch=="aarch64" ++ * @run main/othervm -Dkae.log=true -Dall.test=default KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.digest.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.aes.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.sm4.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.hmac.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.rsa.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.dh.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.ec.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dall.test=enable -Dkae.digest.useKaeEngine=true -Dkae.aes.useKaeEngine=true -Dkae.sm4.useKaeEngine=true -Dkae.hmac.useKaeEngine=true -Dkae.rsa.useKaeEngine=true -Dkae.dh.useKaeEngine=true -Dkae.ec.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.digest.useKaeEngine=false KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.aes.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.sm4.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.hmac.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.rsa.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.dh.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.ec.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dall.test=disable -Dkae.digest.useKaeEngine=false -Dkae.aes.useKaeEngine=false -Dkae.sm4.useKaeEngine=false -Dkae.hmac.useKaeEngine=false -Dkae.rsa.useKaeEngine=false -Dkae.dh.useKaeEngine=false -Dkae.ec.useKaeEngine=false KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dall.test=default -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.digest.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.aes.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.sm4.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.hmac.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.rsa.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.dh.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.ec.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dall.test=enable -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.digest.useKaeEngine=true -Dkae.aes.useKaeEngine=true -Dkae.sm4.useKaeEngine=true -Dkae.hmac.useKaeEngine=true -Dkae.rsa.useKaeEngine=true -Dkae.dh.useKaeEngine=true -Dkae.ec.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.digest.useKaeEngine=false KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.aes.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.sm4.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.hmac.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.rsa.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.dh.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.ec.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dall.test=disable -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.digest.useKaeEngine=false -Dkae.aes.useKaeEngine=false -Dkae.sm4.useKaeEngine=false -Dkae.hmac.useKaeEngine=false -Dkae.rsa.useKaeEngine=false -Dkae.dh.useKaeEngine=false -Dkae.ec.useKaeEngine=false KAEUseEngineTest ++ */ ++public class KAEUseEngineTest { ++ enum Mode { ++ DEFAULT(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, true, true, false ++ }), ++ DIGEST_ENABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, true, true, false ++ }, 0, true), ++ AES_ENABLE(new boolean[]{ ++ true, false, false, true, true, true, true, true, true, true, ++ true, true, true, true, true, true, true, true, true, true, ++ false, false, false, false, false, false, true, true, false ++ }, 1, true), ++ SM4_ENABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, true, true, false ++ }, 2, true), ++ HMAC_ENABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ true, true, true, true, true, true, true, true, false ++ }, 3, true), ++ RSA_ENABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, true, true, false ++ }, 4, true), ++ DH_ENABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, true, true, false ++ }, 5, true), ++ EC_ENABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, true, true, false ++ }, 6, true), ++ ALL_ENABLE(new boolean[]{ ++ true, false, false, true, true, true, true, true, true, true, ++ true, true, true, true, true, true, true, true, true, true, ++ true, true, true, true, true, true, true, true, false ++ }, true), ++ DIGEST_DISABLE(new boolean[]{ ++ false, false, false, false, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, true, true, false ++ }, 0, false), ++ AES_DISABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, true, true, false ++ }, 1, false), ++ SM4_DISABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, false ++ }, 2, false), ++ HMAC_DISABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, true, true, false ++ }, 3, false), ++ RSA_DISABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, false, true, false ++ }, 4, false), ++ DH_DISABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, true, false, false ++ }, 5, false), ++ EC_DISABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, true, true, false ++ }, 6, false), ++ ALL_DISABLE(new boolean[]{ ++ false, false, false, false, false, false, false, false, false, false, ++ false, false, false, false, false, false, false, false, false, false, ++ false, false, false, false, false, false, false, false, false ++ }, false); ++ private final boolean[] expectedResult; ++ private final Integer propertyNameIndex; ++ private final boolean enable; ++ private static final Map modeMap = new HashMap<>(); ++ ++ static { ++ Mode[] modes = Mode.values(); ++ for (Mode mode : modes) { ++ if (mode.propertyNameIndex != null) { ++ modeMap.put(PROPERTY_NAMES[mode.propertyNameIndex] + ":" + mode.enable, mode); ++ } ++ } ++ modeMap.put("default", DEFAULT); ++ modeMap.put("disable", ALL_DISABLE); ++ modeMap.put("enable", ALL_ENABLE); ++ } ++ ++ Mode(boolean[] expectedResult) { ++ this(expectedResult, false); ++ } ++ ++ Mode(boolean[] expectedResult, boolean enable) { ++ this(expectedResult, null, enable); ++ } ++ ++ Mode(boolean[] expectedResult, Integer propertyNameIndex, boolean enable) { ++ this.expectedResult = expectedResult; ++ this.propertyNameIndex = propertyNameIndex; ++ this.enable = enable; ++ } ++ ++ static Mode getMode(String name, Boolean enable) { ++ return modeMap.get(name + ":" + enable); ++ } ++ ++ static Mode getMode(String key) { ++ return modeMap.get(key); ++ } ++ } ++ ++ private static final String KAE_LOG_PATH = System.getProperty("user.dir") + ++ File.separator + "kae.log"; ++ ++ private static final String[] PROPERTY_NAMES = new String[]{ ++ "kae.digest.useKaeEngine", ++ "kae.aes.useKaeEngine", ++ "kae.sm4.useKaeEngine", ++ "kae.hmac.useKaeEngine", ++ "kae.rsa.useKaeEngine", ++ "kae.dh.useKaeEngine", ++ "kae.ec.useKaeEngine" ++ }; ++ ++ private static final List files = new ArrayList<>(); ++ ++ public static void main(String[] args) throws IOException { ++ KAETestHelper.Engine engine = KAETestHelper.getEngine(); ++ if (!engine.isValid()) { ++ System.out.println("Skip test, engine " + engine.getEngineId() + " does not exist."); ++ return; ++ } ++ Mode mode = getMode(); ++ if (mode == null) { ++ throw new RuntimeException("test failed: mode is null"); ++ } ++ ++ try { ++ new KAEProvider(); ++ test(mode, engine); ++ } finally { ++ KAETestHelper.cleanUp(files); ++ } ++ } ++ ++ private static Mode getMode() { ++ String value = System.getProperty("all.test"); ++ if (value != null) { ++ return Mode.getMode(value); ++ } ++ for (String propertyName : PROPERTY_NAMES) { ++ String property = System.getProperty(propertyName); ++ Boolean enable = null; ++ if (property != null) { ++ enable = Boolean.valueOf(property); ++ } ++ Mode mode = Mode.getMode(propertyName, enable); ++ if (mode != null) { ++ return mode; ++ } ++ } ++ return null; ++ } ++ ++ private static void test(Mode mode, KAETestHelper.Engine engine) throws IOException { ++ File file = new File(KAE_LOG_PATH); ++ files.add(file); ++ boolean[] kaeUseEngineFlags = KAETestHelper.parseLog(engine, file); ++ if (!Arrays.equals(mode.expectedResult, kaeUseEngineFlags)) { ++ throw new RuntimeException("test failed : expected : " + Arrays.toString(mode.expectedResult) + "," + ++ "actual:" + Arrays.toString(kaeUseEngineFlags)); ++ } ++ } ++} +diff --git a/jdk/test/org/openeuler/security/openssl/KaeDebugLogTest.java b/jdk/test/org/openeuler/security/openssl/KaeDebugLogTest.java +new file mode 100644 +index 000000000..bcce9cb8b +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/KaeDebugLogTest.java +@@ -0,0 +1,88 @@ ++/* ++ * Copyright (c) 2021, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import org.openeuler.security.openssl.KAEProvider; ++ ++import javax.crypto.Cipher; ++import javax.crypto.spec.SecretKeySpec; ++import java.io.PrintStream; ++import java.nio.charset.StandardCharsets; ++import java.nio.file.Files; ++import java.nio.file.Paths; ++import java.security.Security; ++import java.util.Objects; ++import java.util.stream.Collectors; ++import java.util.stream.Stream; ++ ++/** ++ * @test ++ * @summary test for KaeDebugLogTest ++ * @requires os.arch=="aarch64" ++ * @run main/othervm -Djava.security.debug=kae -Dkae.sm4.maxChunkSize=65536 KaeDebugLogTest ++ * @run main/othervm -Djava.security.debug=kae KaeDebugLogTest ++ * @run main/othervm -Djava.security.auth.debug=kae KaeDebugLogTest ++ * @run main/othervm KaeDebugLogTest ++ */ ++ ++public class KaeDebugLogTest { ++ ++ private static final PrintStream err = System.err; ++ ++ public static void main(String[] args) throws Exception { ++ PrintStream printStream = new PrintStream("kaetest.out"); ++ System.setErr(printStream); ++ testDebugLog(); ++ System.setErr(printStream); ++ testSm4ChunkSize(); ++ } ++ ++ public static void testDebugLog() throws Exception { ++ new KAEProvider(); ++ Stream lines = Files.lines(Paths.get("kaetest.out")); ++ System.setErr(err); ++ String content = lines.collect(Collectors.joining(System.lineSeparator())); ++ if(("kae".equals(System.getProperty("java.security.debug")) ++ || "kae".equals(System.getProperty("java.security..auth.debug"))) ++ && !content.contains("reading kae properties file:")){ ++ throw new RuntimeException("KaeDebugLogTest Failed! Failed to set the debug log."); ++ } ++ lines.close(); ++ } ++ ++ public static void testSm4ChunkSize() throws Exception { ++ Security.insertProviderAt(new KAEProvider(), 1); ++ Cipher cipher = Cipher.getInstance("SM4"); ++ cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec("sm4EncryptionKey".getBytes(StandardCharsets.UTF_8), "SM4")); ++ Stream lines = Files.lines(Paths.get("kaetest.out")); ++ System.setErr(err); ++ String content = lines.collect(Collectors.joining(System.lineSeparator())); ++ String log = "The configured chunk size is " + System.getProperty("kae.sm4.maxChunkSize"); ++ if(("kae".equals(System.getProperty("java.security.debug")) ++ || "kae".equals(System.getProperty("java.security..auth.debug"))) ++ && Objects.nonNull(System.getProperty("kae.sm4.maxChunkSize")) &&!content.contains(log)){ ++ throw new RuntimeException("KaeDebugLogTest Failed! Failed to set the kae.sm4.maxChunkSize = " + System.getProperty("kae.sm4.maxChunkSize")); ++ } ++ lines.close(); ++ } ++ ++} +diff --git a/jdk/test/org/openeuler/security/openssl/KaeProviderTest.java b/jdk/test/org/openeuler/security/openssl/KaeProviderTest.java +new file mode 100644 +index 000000000..d8587891b +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/KaeProviderTest.java +@@ -0,0 +1,170 @@ ++/* ++ * Copyright (c) 2021, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import org.openeuler.security.openssl.KAEProvider; ++ ++import javax.crypto.Cipher; ++import javax.crypto.Mac; ++import javax.crypto.NoSuchPaddingException; ++import java.security.KeyPairGenerator; ++import java.security.MessageDigest; ++import java.security.NoSuchAlgorithmException; ++import java.security.Security; ++ ++/** ++ * @test ++ * @requires os.arch=="aarch64" ++ * @summary test for KaeProviderTest ++ * @run main/othervm KaeProviderTest ++ * @run main/othervm KaeProviderTest true ++ * @run main/othervm KaeProviderTest false ++ * @run main/othervm KaeProviderTest wrong ++ */ ++ ++public class KaeProviderTest { ++ ++ private static final String[] algorithmKaeProviderPropertyNames = new String[]{ ++ "kae.md5", ++ "kae.sha256", ++ "kae.sha384", ++ "kae.sm3", ++ "kae.aes", ++ "kae.sm4", ++ "kae.hmac", ++ "kae.rsa", ++ "kae.dh", ++ "kae.ec" ++ }; ++ ++ private static final String KAE = "KAEProvider"; ++ ++ public static void main(String[] args) throws Exception { ++ initProperty(args); ++ Security.insertProviderAt(new KAEProvider(), 1); ++ testALL(); ++ } ++ ++ private static void initProperty(String[] args) { ++ if (args.length <= 0) { ++ return; ++ } ++ String value = args[0]; ++ for (String name : algorithmKaeProviderPropertyNames){ ++ System.setProperty(name,value); ++ } ++ } ++ ++ public static void testALL() throws Exception { ++ testMd5(); ++ testSha256(); ++ testSha384(); ++ testSm3(); ++ testAes(); ++ testSm4(); ++ testHmac(); ++ testRsa(); ++ testDh(); ++ testEc(); ++ } ++ ++ public static void testMd5() throws NoSuchAlgorithmException { ++ MessageDigest messageDigest = MessageDigest.getInstance("MD5"); ++ judge("kae.md5",messageDigest.getProvider().getName()); ++ ++ } ++ ++ public static void testSha256() throws NoSuchAlgorithmException { ++ MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); ++ judge("kae.sha256",messageDigest.getProvider().getName()); ++ } ++ ++ public static void testSha384() throws NoSuchAlgorithmException { ++ MessageDigest messageDigest = MessageDigest.getInstance("SHA-384"); ++ judge("kae.sha384",messageDigest.getProvider().getName()); ++ } ++ ++ public static void testSm3() throws NoSuchAlgorithmException { ++ try{ ++ MessageDigest messageDigest = MessageDigest.getInstance("SM3"); ++ judge("kae.sm3",messageDigest.getProvider().getName()); ++ }catch (NoSuchAlgorithmException e){ ++ if(Boolean.parseBoolean(System.getProperty("kae.sm3"))){ ++ throw e; ++ } ++ } ++ } ++ ++ public static void testAes() throws NoSuchAlgorithmException, NoSuchPaddingException { ++ Cipher cipher = Cipher.getInstance("AES"); ++ judge("kae.aes",cipher.getProvider().getName()); ++ } ++ ++ public static void testSm4() throws NoSuchAlgorithmException, NoSuchPaddingException { ++ try{ ++ Cipher cipher = Cipher.getInstance("SM4"); ++ judge("kae.sm4",cipher.getProvider().getName()); ++ }catch (NoSuchAlgorithmException e){ ++ if(Boolean.parseBoolean(System.getProperty("kae.sm4"))){ ++ throw e; ++ } ++ } ++ } ++ ++ public static void testHmac() throws NoSuchAlgorithmException { ++ Mac mac = Mac.getInstance("HmacMD5"); ++ judge("kae.hmac",mac.getProvider().getName()); ++ } ++ ++ public static void testRsa() throws NoSuchAlgorithmException, NoSuchPaddingException { ++ Cipher cipher = Cipher.getInstance("RSA"); ++ judge("kae.rsa",cipher.getProvider().getName()); ++ } ++ ++ public static void testDh() throws NoSuchAlgorithmException { ++ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DH"); ++ judge("kae.dh",keyPairGenerator.getProvider().getName()); ++ } ++ ++ public static void testEc() throws NoSuchAlgorithmException { ++ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC"); ++ judge("kae.ec",keyPairGenerator.getProvider().getName()); ++ } ++ ++ private static void judge(String algorithm , String providerName){ ++ String value = System.getProperty(algorithm); ++ if (value == null) { ++ if (!KAE.equals(providerName)) { ++ throw new RuntimeException("KaeProviderTest Failed! default Provider.name is not right!"); ++ } ++ } else { ++ if (Boolean.parseBoolean(value) && !KAE.equals(providerName)) { ++ throw new RuntimeException("KaeProviderTest Failed! " + algorithm + " is " + value + "," + ++ " Provider.name is not right!"); ++ } ++ if (!Boolean.parseBoolean(value) && KAE.equals(providerName)) { ++ throw new RuntimeException("KaeProviderTest Failed! " + algorithm + " is " + value + ", " + ++ " Provider.name is not right!"); ++ } ++ } ++ } ++} +diff --git a/jdk/test/org/openeuler/security/openssl/RSATest.java b/jdk/test/org/openeuler/security/openssl/RSATest.java +new file mode 100644 +index 000000000..1f740af0b +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/RSATest.java +@@ -0,0 +1,137 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import org.openeuler.security.openssl.KAEProvider; ++ ++import java.nio.charset.StandardCharsets; ++import java.security.*; ++import java.security.spec.*; ++import javax.crypto.Cipher; ++ ++/** ++ * @test ++ * @summary Basic test for RSA ++ * @run main RSATest ++ */ ++ ++public class RSATest { ++ private static final String algorithm = "RSA"; ++ private static KeyPairGenerator keyPairGenerator; ++ private static byte[] privateKey; ++ private static byte[] publicKey; ++ private static String plainText = "helloworld"; ++ // 512, 768, ++ private static int[] keySizes = {1024, 2048, 4096, 5120, 6144}; ++ private static String[] signAlgorithms = { ++ "MD2withRSA", "MD5withRSA", "SHA1withRSA", "SHA224withRSA", "SHA256withRSA", "SHA384withRSA", "SHA512withRSA" ++ }; ++ private static String[] signAlgorithmsPSS = {"SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512"}; ++ ++ public static void main(String[] args) throws Exception { ++ Security.insertProviderAt(new KAEProvider(), 1); ++ ++ for (int keySize : keySizes) { ++ testKeyPairByKeySize(keySize); ++ testRSACipher(keySize); ++ testSignature(); ++ testPSSSignature(keySize); ++ } ++ } ++ ++ public static void testKeyPairByKeySize(int keySize) throws Exception { ++ keyPairGenerator = KeyPairGenerator.getInstance(algorithm); ++ keyPairGenerator.initialize(keySize); ++ KeyPair keyPair = keyPairGenerator.generateKeyPair(); ++ ++ PrivateKey pairPrivate = keyPair.getPrivate(); ++ PublicKey pairPublic = keyPair.getPublic(); ++ ++ privateKey = pairPrivate.getEncoded(); ++ publicKey = pairPublic.getEncoded(); ++ } ++ ++ public static void testRSACipher(int keySize) throws Exception { ++ PublicKey pubKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKey)); ++ Cipher cipher = Cipher.getInstance("RSA"); ++ cipher.init(Cipher.ENCRYPT_MODE, pubKey); ++ ++ byte[] cipherText = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)); ++ ++ PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKey)); ++ ++ cipher.init(Cipher.DECRYPT_MODE, priKey); ++ ++ String decryptText = new String(cipher.doFinal(cipherText)); ++ ++ if (!plainText.equals(decryptText)) { ++ throw new RuntimeException("rsa decryption failed. keySize = " + keySize); ++ } ++ } ++ ++ public static void testSignature() throws Exception { ++ PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKey)); ++ PublicKey pubKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKey)); ++ ++ for (String algorithm : signAlgorithms) { ++ Signature sign = Signature.getInstance(algorithm); ++ sign.initSign(priKey); ++ sign.update(plainText.getBytes()); ++ byte[] signInfo = sign.sign(); ++ ++ sign.initVerify(pubKey); ++ sign.update(plainText.getBytes()); ++ if (!sign.verify(signInfo)) { ++ throw new RuntimeException("rsa testSignature failed. digest algorithm = " + algorithm); ++ } ++ } ++ } ++ ++ public static void testPSSSignature(int keySize) throws Exception { ++ PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKey)); ++ PublicKey pubKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKey)); ++ ++ Signature sign = Signature.getInstance("RSASSA-PSS"); ++ ++ for (String algorithm : signAlgorithmsPSS) { ++ if (algorithm.equals(signAlgorithmsPSS[4]) && keySize <= 1024) { ++ continue; ++ } ++ sign.initSign(priKey); ++ ++ MessageDigest digest = MessageDigest.getInstance(algorithm); ++ byte[] digestByte = digest.digest(plainText.getBytes()); ++ sign.setParameter( ++ new PSSParameterSpec(algorithm, "MGF1", new MGF1ParameterSpec(algorithm), digestByte.length, 1)); ++ ++ sign.update(plainText.getBytes()); ++ byte[] signInfo = sign.sign(); ++ ++ sign.initVerify(pubKey); ++ ++ sign.update(plainText.getBytes()); ++ if (!sign.verify(signInfo)) { ++ throw new RuntimeException("rsa testPSSSignature failed. digest algorithm = " + algorithm); ++ } ++ } ++ } ++} +diff --git a/jdk/test/org/openeuler/security/openssl/SM3Test.java b/jdk/test/org/openeuler/security/openssl/SM3Test.java +deleted file mode 100644 +index 181f708ff..000000000 +--- a/jdk/test/org/openeuler/security/openssl/SM3Test.java ++++ /dev/null +@@ -1,54 +0,0 @@ +-/* +- * Copyright (c) 2021, Huawei Technologies Co., Ltd. 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 +- * under the terms of the GNU General Public License version 2 only, as +- * published by the Free Software Foundation. +- * +- * This code is distributed in the hope that it will be useful, but WITHOUT +- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +- * version 2 for more details (a copy is included in the LICENSE file that +- * accompanied this code). +- * +- * You should have received a copy of the GNU General Public License version +- * 2 along with this work; if not, write to the Free Software Foundation, +- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +- * +- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +- * or visit www.oracle.com if you need additional information or have any +- * questions. +- */ +- +-import org.openeuler.security.openssl.KAEProvider; +-import java.nio.charset.StandardCharsets; +-import java.util.Arrays; +-import java.security.MessageDigest; +-import java.security.Security; +- +-/** +- * @test +- * @summary Basic test for sm3 +- * @run main SM3Test +- */ +- +-public class SM3Test { +- +- private static String plainText = "helloworldhellow"; +- +- public static void main(String[] args) throws Exception { +- Security.insertProviderAt(new KAEProvider(), 1); +- test(plainText, "SM3", new byte[]{40, -103, -71, 4, -80, -49, 94, 112, 11, -75, -66, 121, 63, 80, 62, -14, -45, -75, -34, 66, -77, -34, -26, 26, 33, -23, 45, 52, -74, 67, -18, 118}); +- } +- +- public static void test(String plainText, String algo, byte[] expectRes) throws Exception { +- MessageDigest md = MessageDigest.getInstance(algo); +- md.update(plainText.getBytes(StandardCharsets.UTF_8)); +- byte[] res = md.digest(); +- if (!Arrays.equals(res, expectRes)) { +- throw new RuntimeException("sm3 failed"); +- } +- } +- +-} +diff --git a/jdk/test/org/openeuler/security/openssl/SM4Test.java b/jdk/test/org/openeuler/security/openssl/SM4Test.java +index 4c28dc5b6..1029fe897 100644 +--- a/jdk/test/org/openeuler/security/openssl/SM4Test.java ++++ b/jdk/test/org/openeuler/security/openssl/SM4Test.java +@@ -22,9 +22,10 @@ + */ + + import org.openeuler.security.openssl.KAEProvider; ++ ++import java.nio.ByteBuffer; + import java.nio.charset.StandardCharsets; + import java.util.Arrays; +-import java.security.NoSuchAlgorithmException; + import java.security.Security; + import javax.crypto.Cipher; + import javax.crypto.spec.IvParameterSpec; +@@ -55,6 +56,25 @@ public class SM4Test { + test(shortPlainText, "SM4/OFB/PKCS5Padding", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110}); + + testCtrShortIv(plainText, "SM4/CTR/NOPADDING", new byte[]{-13, 73, 40, -36, -64, -67, 75, -72, 90, 58, 73, -4, -36, 115, 126, -48}); ++ ++ testByteBuffer(plainText, "SM4/CBC/NOPADDING", new byte[]{86, 69, 47, -115, -63, 54, 35, 24, -2, 114, 113, 102, 82, 20, 69, 59}); ++ testByteBuffer(shortPlainText, "SM4/CBC/PKCS5Padding", new byte[]{10, 105, 75, -80, -85, -68, 13, -53, 42, 91, -64, 99, 104, 35, -85, 8}); ++ testByteBuffer(plainText, "SM4/ECB/NOPADDING", new byte[]{103, 36, -31, -53, -109, -12, -71, -79, -54, 106, 10, -3, -35, -22, -122, -67}); ++ testByteBuffer(shortPlainText, "SM4/ECB/PKCS5Padding", new byte[]{-10, 99, -9, 90, 58, -36, -109, 54, -55, -52, 7, -49, 110, -88, 72, 40}); ++ testByteBuffer(plainText, "SM4/CTR/NOPADDING", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110, -100, -113, -46, -29, -11, 71}); ++ testByteBuffer(plainText, "SM4/OFB/NOPADDING", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110, -100, -113, -46, -29, -11, 71}); ++ testByteBuffer(shortPlainText, "SM4/OFB/PKCS5Padding", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110}); ++ ++ System.setProperty("kae.sm4.maxChunkSize", "65536"); ++ testByteBuffer(plainText, "SM4/CBC/NOPADDING", new byte[]{86, 69, 47, -115, -63, 54, 35, 24, -2, 114, 113, 102, 82, 20, 69, 59}); ++ testByteBuffer(shortPlainText, "SM4/CBC/PKCS5Padding", new byte[]{10, 105, 75, -80, -85, -68, 13, -53, 42, 91, -64, 99, 104, 35, -85, 8}); ++ testByteBuffer(plainText, "SM4/ECB/NOPADDING", new byte[]{103, 36, -31, -53, -109, -12, -71, -79, -54, 106, 10, -3, -35, -22, -122, -67}); ++ testByteBuffer(shortPlainText, "SM4/ECB/PKCS5Padding", new byte[]{-10, 99, -9, 90, 58, -36, -109, 54, -55, -52, 7, -49, 110, -88, 72, 40}); ++ testByteBuffer(plainText, "SM4/CTR/NOPADDING", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110, -100, -113, -46, -29, -11, 71}); ++ testByteBuffer(plainText, "SM4/OFB/NOPADDING", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110, -100, -113, -46, -29, -11, 71}); ++ testByteBuffer(shortPlainText, "SM4/OFB/PKCS5Padding", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110}); ++ ++ + } + + public static void test(String plainText, String algo, byte[] expectRes) throws Exception { +@@ -92,4 +112,44 @@ public class SM4Test { + throw new RuntimeException("sm4 decryption failed, algo = " + algo); + } + } ++ ++ public static void testByteBuffer(String plainText, String algo, byte[] expectRes) throws Exception { ++ // encrypt ++ Cipher encryptCipher = Cipher.getInstance(algo); ++ if (algo.contains("ECB")) { ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks); ++ } else { ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks, iv); ++ } ++ int inputLen = plainText.length(); ++ ByteBuffer sourceByteBuffer = ByteBuffer.allocateDirect(inputLen); ++ sourceByteBuffer.put(plainText.getBytes()); ++ sourceByteBuffer.flip(); ++ int outputLen = encryptCipher.getOutputSize(inputLen); ++ ByteBuffer encryptedByteBuffer = ByteBuffer.allocate(outputLen); ++ encryptCipher.doFinal(sourceByteBuffer,encryptedByteBuffer); ++ encryptedByteBuffer.flip(); ++ byte[] encryptedBytes = new byte[encryptedByteBuffer.limit()]; ++ encryptedByteBuffer.get(encryptedBytes); ++ if (!Arrays.equals(encryptedBytes, expectRes)) { ++ throw new RuntimeException("sm4 encryption failed, algo = " + algo); ++ } ++ sourceByteBuffer.clear(); ++ encryptedByteBuffer.flip(); ++ ++ // decrypt ++ Cipher decryptCipher = Cipher.getInstance(algo); ++ decryptCipher.init(Cipher.DECRYPT_MODE, ks, encryptCipher.getParameters()); ++ outputLen = decryptCipher.getOutputSize(encryptedBytes.length); ++ ByteBuffer decryptedByteBuffer = ByteBuffer.allocate(outputLen); ++ decryptCipher.doFinal(encryptedByteBuffer, decryptedByteBuffer); ++ decryptedByteBuffer.flip(); ++ byte[] decryptedBytes = new byte[decryptedByteBuffer.limit()]; ++ decryptedByteBuffer.get(decryptedBytes); ++ if (!Arrays.equals(plainText.getBytes(), decryptedBytes)) { ++ throw new RuntimeException("sm4 decryption failed, algo = " + algo); ++ } ++ encryptedByteBuffer.clear(); ++ decryptedByteBuffer.clear(); ++ } + } +-- +2.17.1 + diff --git a/modify_coding_style_and_describe_error.patch b/modify_coding_style_and_describe_error.patch new file mode 100644 index 0000000000000000000000000000000000000000..9464ab21dde76df0e439597cd55f1c8f7caa22cf --- /dev/null +++ b/modify_coding_style_and_describe_error.patch @@ -0,0 +1,56 @@ +From 9d32c786ff6886bcd4b76e0a80eb19ce602dbe42 Mon Sep 17 00:00:00 2001 +From: wangkun +Date: Thu, 28 Jul 2022 17:24:52 +0800 +Subject: [PATCH 3/3] fix xx + +--- + .../classes/org/openeuler/security/openssl/KAEDigest.java | 6 +++--- + .../classes/org/openeuler/security/openssl/KAEProvider.java | 2 -- + jdk/src/solaris/native/java/io/path_util.c | 1 - + 3 files changed, 3 insertions(+), 6 deletions(-) + +diff --git a/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEDigest.java b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEDigest.java +index bb5c8681..6ff03241 100644 +--- a/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEDigest.java ++++ b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEDigest.java +@@ -88,9 +88,9 @@ abstract class KAEDigest extends MessageDigestSpi implements Cloneable { + private static class DigestContextRef extends PhantomReference + implements Comparable { + +- private static ReferenceQueue referenceQueue = new ReferenceQueue<>(); +- private static Set referenceList = new ConcurrentSkipListSet<>(); +- private static boolean disableKaeDispose = Boolean.getBoolean("kae.disableKaeDispose"); ++ private static final ReferenceQueue referenceQueue = new ReferenceQueue<>(); ++ private static final Set referenceList = new ConcurrentSkipListSet<>(); ++ private static final boolean disableKaeDispose = Boolean.getBoolean("kae.disableKaeDispose"); + + private final long ctxAddress; + +diff --git a/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEProvider.java b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEProvider.java +index 8ba70200..83ed8649 100644 +--- a/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEProvider.java ++++ b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEProvider.java +@@ -104,8 +104,6 @@ public class KAEProvider extends Provider { + if (needLog && "true".equalsIgnoreCase(props.getProperty("kae.log"))) { + logStart(excp); + needLog = false; // Log only once +- } else { +- KAEProvider.excp = null; // Ignore exception. + } + if (!"false".equalsIgnoreCase(props.getProperty("kae.md5"))) { + putMD5(); +diff --git a/jdk/src/solaris/native/java/io/path_util.c b/jdk/src/solaris/native/java/io/path_util.c +index 8a533f81..4b978206 100644 +--- a/jdk/src/solaris/native/java/io/path_util.c ++++ b/jdk/src/solaris/native/java/io/path_util.c +@@ -116,7 +116,6 @@ collapse(char *path) + int nc; + char **ix; + int i, j; +- char *p, *q; + + nc = collapsible(names); + if (nc < 2) return; /* Nothing to do */ +-- +2.22.0 + diff --git a/openjdk-1.8.0.spec b/openjdk-1.8.0.spec index 6269875b65455cb4386428bec80ef30e8a6df015..838ce778b52e77cacbe82f5a466a0dc8a89eadc6 100644 --- a/openjdk-1.8.0.spec +++ b/openjdk-1.8.0.spec @@ -146,13 +146,13 @@ %global origin_nice OpenJDK %global top_level_dir_name %{origin} %global repo jdk8u -%global revision jdk8u342-b07 +%global revision jdk8u352-b08 %global full_revision %{repo}-%{revision} # Define IcedTea version used for SystemTap tapsets and desktop files %global icedteaver 3.15.0 -%global updatever 342 -%global buildver b07 +%global updatever 352 +%global buildver b08 # priority must be 7 digits in total. The expression is workarounding tip %global priority 1800%{updatever} @@ -583,7 +583,7 @@ exit 0 %{_jvmdir}/%{jredir -- %{?1}}/lib/%{archinstall}/libnpt.so %ifarch %{aarch64} %{_jvmdir}/%{jredir -- %{?1}}/lib/%{archinstall}/libj2kae.so -%{_jvmdir}/%{jredir -- %{?1}}/lib/ext/kaeprovider.conf +%{_jvmdir}/%{jredir -- %{?1}}/lib/kaeprovider.conf %endif %ifarch %{sa_arches} %{_jvmdir}/%{jredir -- %{?1}}/lib/%{archinstall}/libsaproc.so @@ -916,7 +916,7 @@ Provides: java-%{javaver}-%{origin}-accessibility%{?1} = %{epoch}:%{version}-%{r Name: java-%{javaver}-%{origin} Version: %{javaver}.%{updatever}.%{buildver} -Release: 0 +Release: 1 # java-1.5.0-ibm from jpackage.org set Epoch to 1 for unknown reasons # and this change was brought into RHEL-4. java-1.5.0-ibm packages # also included the epoch in their virtual provides. This created a @@ -1079,7 +1079,6 @@ Patch185: update-cacerts-and-VerifyCACerts.java-test.patch Patch186: update-to-keep-same-with-master.patch Patch188: 8247691_incorrect_handling_of_VM_exceptions_in_C1_deopt_stub.patch Patch192: add_kae_implementation_add_default_conf_file.patch -Patch193: improve_algorithmConstraints_checkAlgorithm_performance.patch Patch194: modify_the_default_iteration_time_and_forks_in_the_JMH_of_KAEProvider.patch Patch195: support_CMS_parallel_inspection.patch Patch196: g1gc-numa-aware-Implementation.patch @@ -1116,7 +1115,6 @@ Patch229: downgrade-the-symver-of-fcntl64.patch # 8u322 Patch230: add-system-property-swing.JComboBox.useLegacyMode.patch -Patch232: 8173361-various-crashes-in-JvmtiExport-post_compiled.patch Patch233: fix-TestUseCompressedOopsErgo-run-failed.patch Patch235: fix-testme-Test6929067-run-faild.patch Patch236: penetration_testing_vulnerability_fix.patch @@ -1131,10 +1129,25 @@ Patch243: Fix-compile-and-runtime-failures-for-minimal1-versio.patch Patch244: fix_X509TrustManagerImpl_symantec_distrust.patch Patch245: change-sa-jdi.jar-make-file-for-BEP.PATCH Patch246: 7092821-java.security.Provider.getService-is-synchro.patch -Patch247: 8173339-AArch64-Fix-minimum-stack-size-computations.patch Patch248: 8067941-TESTBUG-Fix-tests-for-OS-with-64K-page-size.patch # 8u342 +Patch249: Improve_AlgorithmConstraints_checkAlgorithm_performance.patch +Patch250: modify_coding_style_and_describe_error.patch +Patch251: fix_wrap_memcpy_undefined_gcc10_3.patch +Patch252: 8290705_fix_StringConcat_validate_mem_flow_asserts_with_unexpected_userStoreI.patch +Patch253: 8143925-enhancing-CounterMode.crypt-for-AESCrypt.patch +Patch254: kae-usability-enhancement.patch +Patch255: Dynamic-CDS-Archive.patch +Patch256: 8202951-Support-default-jsa.patch +Patch257: 8200332-Improve-GCM-counting.patch +Patch258: dynamic-cds-_header-and-_fd-handles-are-not-free.patch +Patch259: fix-dumped-heap-using-jhat-parsing-to-appear-failed-to-resolve-object-id-warning-message.patch +Patch260: 8159720-Failure-of-C2-compilation-with-tiered-preven.patch +Patch261: revert-fPIC-and-security-compilation-flag-on.patch +Patch262: add-configuration-option-of-huawei-internal-version-shown-in-release-file.patch +Patch263: The-code-style-is-fixed-and-test-cases-are-added.patch +Patch264: 8287109-Distrust-failed-with-CertificateExpired.patch ############################################# # @@ -1196,6 +1209,7 @@ BuildRequires: cups-devel BuildRequires: desktop-file-utils # elfutils only are OK for build without AOT BuildRequires: elfutils-devel +BuildRequires: elfutils-extra BuildRequires: fontconfig-devel BuildRequires: freetype-devel BuildRequires: giflib-devel @@ -1595,7 +1609,6 @@ pushd %{top_level_dir_name} %patch228 -p1 %patch229 -p1 %patch230 -p1 -%patch232 -p1 %patch233 -p1 %patch235 -p1 %patch236 -p1 @@ -1608,8 +1621,23 @@ pushd %{top_level_dir_name} %patch244 -p1 %patch245 -p1 %patch246 -p1 -%patch247 -p1 %patch248 -p1 +%patch249 -p1 +%patch250 -p1 +%patch251 -p1 +%patch252 -p1 +%patch253 -p1 +%patch254 -p1 +%patch255 -p1 +%patch256 -p1 +%patch257 -p1 +%patch258 -p1 +%patch259 -p1 +%patch260 -p1 +%patch261 -p1 +%patch262 -p1 +%patch263 -p1 +%patch264 -p1 popd # System library fixes @@ -2234,6 +2262,73 @@ cjc.mainProgram(arg) %endif %changelog +* Mon Oct 24 2022 kuenking111 - 1:1.8.0.352-b08.1 +- remove gitattributes gitignore jcheck files + +* Wed Oct 19 2022 kuenking111 - 1:1.8.0.352-b08.0 +- modified add-missing-test-case.patch +- upgrade to jdk8u352-b08 + +* Thu Sep 29 2022 DXwangg - 1:1.8.0.352-b07.0 +- upgrade to jdk8u352-b07 +- deleted Improve_AlgorithmConstraints_checkAlgorithm_performance.patch +- deleted 8173361-various-crashes-in-JvmtiExport-post_compiled.patch +- modified Fix-compile-and-runtime-failures-for-minimal1-versio.patch +- deleted 8173339-AArch64-Fix-minimum-stack-size-computations.patch +- modified add-appcds-file-lock.patch +- modified add-DumpSharedSpace-guarantee-when-create-anonymous-classes.patch +- modified fix-appcds-s-option-AppCDSLockFile.patch +- modified fix-windows-compile-fail.patch + +* Sat Sep 24 2022 kuenking111 - 1:1.8.0.342-b07.15 +- add 8287109-Distrust-failed-with-CertificateExpired.patch + +* Fri Sep 23 2022 kuenking111 - 1:1.8.0.342-b07.14 +- add The-code-style-is-fixed-and-test-cases-are-added.patch + +* Thu Sep 22 2022 kuenking111 - 1:1.8.0.342-b07.13 +- add add-configuration-option-of-huawei-internal-version-shown-in-release-file.patch + +* Wed Sep 21 2022 kuenking111 - 1:1.8.0.342-b07.12 +- add revert-fPIC-and-security-compilation-flag-on.patch + +* Mon Sep 19 2022 zhoulei - 1:1.8.0.342-b07.11 +- add 8159720-Failure-of-C2-compilation-with-tiered-preven.patch + +* Fri Sep 16 2022 kuenking111 - 1:1.8.0.342-b07.10 +- add fix-dumped-heap-using-jhat-parsing-to-appear-failed-to-resolve-object-id-warning-message.patch + +* Fri Sep 16 2022 kuenking111 - 1:1.8.0.342-b07.9 +- add dynamic-cds-_header-and-_fd-handles-are-not-free.patch + +* Fri Sep 16 2022 kuenking111 - 1:1.8.0.342-b07.8 +- add 8200332-Improve-GCM-counting.patch + +* Fri Sep 16 2022 kuenking111 - 1:1.8.0.342-b07.7 +- add 8202951-Support-default-jsa.patch + +* Thu Sep 15 2022 kuenking111 - 1:1.8.0.342-b07.6 +- add Dynamic-CDS-Archive.patch + +* Thu Sep 15 2022 kuenking111 - 1:1.8.0.342-b07.5 +- add kae-usability-enhancement.patch + +* Thu Sep 15 2022 kuenking111 - 1:1.8.0.342-b07.4 +- add 8143925-enhancing-CounterMode.crypt-for-AESCrypt.patch + +* Fri Aug 5 2022 kuenking111 - 1:1.8.0.342-b07.3 +- add 8290705_fix_StringConcat_validate_mem_flow_asserts_with_unexpected_userStoreI.patch +- modified version.txt + +* Thu Jul 28 2022 kuenking111 - 1:1.8.0.342-b07.2 +- add modify_coding_style_and_describe_error.patch +- add Improve_AlgorithmConstraints_checkAlgorithm_performance.patch +- add fix_wrap_memcpy_undefined_gcc10_3.patch +- modified implementation_of_Blas_hotspot_function_in_Intrinsics.patch + +* Thu Jul 28 2022 kuenking111 - 1:1.8.0.342-b07.1 +- del hg git files + * Fri Jul 22 2022 kuenking111 - 1:1.8.0.342-b07.0 - del 8168926.patch - del 8194154.patch diff --git a/revert-fPIC-and-security-compilation-flag-on.patch b/revert-fPIC-and-security-compilation-flag-on.patch new file mode 100644 index 0000000000000000000000000000000000000000..a785c84154e7daba6a202a27a2e6f57dae0cc68f --- /dev/null +++ b/revert-fPIC-and-security-compilation-flag-on.patch @@ -0,0 +1,60 @@ +From 9ffc530e0d34086e68c87306ca0410e5847812d6 Mon Sep 17 00:00:00 2001 +Date: Wed, 21 Sep 2022 09:53:14 +0800 +Subject: revert fPIC and security compilation flag on + +--- + common/autoconf/flags.m4 | 6 +----- + common/autoconf/generated-configure.sh | 6 +----- + hotspot/make/pic.make | 2 +- + 3 files changed, 3 insertions(+), 11 deletions(-) + +diff --git a/common/autoconf/flags.m4 b/common/autoconf/flags.m4 +index 69bea78d..71703a15 100644 +--- a/common/autoconf/flags.m4 ++++ b/common/autoconf/flags.m4 +@@ -807,11 +807,7 @@ AC_DEFUN_ONCE([FLAGS_SETUP_COMPILER_FLAGS_FOR_JDK], + LDFLAGS_JDKEXE="$LDFLAGS_JDKEXE -Xlinker --allow-shlib-undefined" + fi + if test "x$TOOLCHAIN_TYPE" = xgcc; then +- # Enabling pie on 32 bit builds prevents the JVM from allocating a continuous +- # java heap. +- if test "x$OPENJDK_TARGET_CPU_BITS" != "x32"; then +- LDFLAGS_JDKEXE="$LDFLAGS_JDKEXE -pie" +- fi ++ LDFLAGS_JDKEXE="$LDFLAGS_JDKEXE -pie" + fi + fi + AC_SUBST(LDFLAGS_JDKLIB) +diff --git a/common/autoconf/generated-configure.sh b/common/autoconf/generated-configure.sh +index f0e49f50..53e6cf18 100644 +--- a/common/autoconf/generated-configure.sh ++++ b/common/autoconf/generated-configure.sh +@@ -43068,11 +43068,7 @@ $as_echo "$supports" >&6; } + LDFLAGS_JDKEXE="$LDFLAGS_JDKEXE -Xlinker --allow-shlib-undefined" + fi + if test "x$TOOLCHAIN_TYPE" = xgcc; then +- # Enabling pie on 32 bit builds prevents the JVM from allocating a continuous +- # java heap. +- if test "x$OPENJDK_TARGET_CPU_BITS" != "x32"; then +- LDFLAGS_JDKEXE="$LDFLAGS_JDKEXE -pie" +- fi ++ LDFLAGS_JDKEXE="$LDFLAGS_JDKEXE -pie" + fi + fi + +diff --git a/hotspot/make/pic.make b/hotspot/make/pic.make +index 0e61ad93..3d85546c 100644 +--- a/hotspot/make/pic.make ++++ b/hotspot/make/pic.make +@@ -30,7 +30,7 @@ include $(GAMMADIR)/make/scm.make + + ifneq ($(OSNAME), windows) + ifndef LP64 +- PARTIAL_NONPIC=1 ++ PARTIAL_NONPIC=0 + endif + PIC_ARCH = ppc arm + ifneq ("$(filter $(PIC_ARCH),$(BUILDARCH))","") +-- +2.22.0 +