diff --git a/8352716-tz-Update-Timezone-Data-to-2025b.patch b/8352716-tz-Update-Timezone-Data-to-2025b.patch new file mode 100644 index 0000000000000000000000000000000000000000..06f702c08e9a3d8e7380a3cc520bf8b7db4517e7 --- /dev/null +++ b/8352716-tz-Update-Timezone-Data-to-2025b.patch @@ -0,0 +1,241 @@ +From ffbb19474b77e5892735ebaf0f6f734982f48ab7 Mon Sep 17 00:00:00 2001 +Date: Tue, 29 Apr 2025 14:23:11 +0800 +Subject: [PATCH] 8352716: (tz) Update Timezone Data to 2025b + +--- + src/java.base/share/data/tzdata/VERSION | 2 +- + src/java.base/share/data/tzdata/asia | 12 ++- + src/java.base/share/data/tzdata/northamerica | 9 ++ + src/java.base/share/data/tzdata/southamerica | 86 +++++++++++++++---- + src/java.base/share/data/tzdata/zone.tab | 3 +- + .../java/util/TimeZone/TimeZoneData/VERSION | 2 +- + 6 files changed, 94 insertions(+), 20 deletions(-) + +diff --git a/src/java.base/share/data/tzdata/VERSION b/src/java.base/share/data/tzdata/VERSION +index 9c056fac3..4bd54efbc 100644 +--- a/src/java.base/share/data/tzdata/VERSION ++++ b/src/java.base/share/data/tzdata/VERSION +@@ -21,4 +21,4 @@ + # or visit www.oracle.com if you need additional information or have any + # questions. + # +-tzdata2025a ++tzdata2025b +diff --git a/src/java.base/share/data/tzdata/asia b/src/java.base/share/data/tzdata/asia +index b0a6fa01d..55b13134f 100644 +--- a/src/java.base/share/data/tzdata/asia ++++ b/src/java.base/share/data/tzdata/asia +@@ -1523,6 +1523,16 @@ Zone Asia/Jayapura 9:22:48 - LMT 1932 Nov + # (UIT No. 143 17.XI.1977) and not 23 September (UIT No. 141 13.IX.1977). + # UIT is the Operational Bulletin of International Telecommunication Union. + ++# From Roozbeh Pournader (2025-03-18): ++# ... the exact time of Iran's transition from +0400 to +0330 ... was Friday ++# 1357/8/19 AP=1978-11-10. Here's a newspaper clip from the Ettela'at ++# newspaper, dated 1357/8/14 AP=1978-11-05, translated from Persian ++# (at https://w.wiki/DUEY): ++# Following the government's decision about returning the official time ++# to the previous status, the spokesperson for the Ministry of Energy ++# announced today: At the hour 24 of Friday 19th of Aban (=1978-11-10), ++# the country's time will be pulled back half an hour. ++# + # From Roozbeh Pournader (2003-03-15): + # This is an English translation of what I just found (originally in Persian). + # The Gregorian dates in brackets are mine: +@@ -1650,7 +1660,7 @@ Rule Iran 2021 2022 - Sep 21 24:00 0 - + Zone Asia/Tehran 3:25:44 - LMT 1916 + 3:25:44 - TMT 1935 Jun 13 # Tehran Mean Time + 3:30 Iran %z 1977 Oct 20 24:00 +- 4:00 Iran %z 1979 ++ 4:00 Iran %z 1978 Nov 10 24:00 + 3:30 Iran %z + + +diff --git a/src/java.base/share/data/tzdata/northamerica b/src/java.base/share/data/tzdata/northamerica +index 0a54e63be..21f178ee8 100644 +--- a/src/java.base/share/data/tzdata/northamerica ++++ b/src/java.base/share/data/tzdata/northamerica +@@ -1634,6 +1634,15 @@ Zone America/Moncton -4:19:08 - LMT 1883 Dec 9 + # For more on Orillia, see: Daubs K. Bold attempt at daylight saving + # time became a comic failure in Orillia. Toronto Star 2017-07-08. + # https://www.thestar.com/news/insight/2017/07/08/bold-attempt-at-daylight-saving-time-became-a-comic-failure-in-orillia.html ++# From Paul Eggert (2025-03-20): ++# Also see the 1912-06-17 front page of The Evening Sunbeam, ++# reproduced in: Richardson M. "Daylight saving was a confusing ++# time in Orillia" in the 2025-03-15 Orillia Matters. Richardson writes, ++# "The first Sunday after the switch was made, [DST proponent and ++# Orillia mayor William Sword] Frost walked into church an hour late. ++# This became a symbol of the downfall of daylight saving in Orillia." ++# The mayor became known as "Daylight Bill". ++# https://www.orilliamatters.com/local-news/column-daylight-saving-was-a-confusing-time-in-orillia-10377529 + + # From Mark Brader (2010-03-06): + # +diff --git a/src/java.base/share/data/tzdata/southamerica b/src/java.base/share/data/tzdata/southamerica +index 0a5859600..ca3c33859 100644 +--- a/src/java.base/share/data/tzdata/southamerica ++++ b/src/java.base/share/data/tzdata/southamerica +@@ -1269,35 +1269,45 @@ Zone America/Rio_Branco -4:31:12 - LMT 1914 + # dates to 2014. + # DST End: last Saturday of April 2014 (Sun 27 Apr 2014 03:00 UTC) + # DST Start: first Saturday of September 2014 (Sun 07 Sep 2014 04:00 UTC) +-# http://www.diariooficial.interior.gob.cl//media/2014/02/19/do-20140219.pdf ++# From Tim Parenti (2025-03-22): ++# Decreto 307 of 2014 of the Ministry of the Interior and Public Security, ++# promulgated 2014-01-30 and published 2014-02-19: ++# https://www.diariooficial.interior.gob.cl/media/2014/02/19/do-20140219.pdf#page=1 ++# https://www.bcn.cl/leychile/navegar?idNorma=1059557 + + # From Eduardo Romero Urra (2015-03-03): + # Today has been published officially that Chile will use the DST time + # permanently until March 25 of 2017 +-# http://www.diariooficial.interior.gob.cl/media/2015/03/03/1-large.jpg +-# +-# From Paul Eggert (2015-03-03): +-# For now, assume that the extension will persist indefinitely. ++# From Tim Parenti (2025-03-22): ++# Decreto 106 of 2015 of the Ministry of the Interior and Public Security, ++# promulgated 2015-01-27 and published 2015-03-03: ++# https://www.diariooficial.interior.gob.cl/media/2015/03/03/do-20150303.pdf#page=1 ++# https://www.bcn.cl/leychile/navegar?idNorma=1075157 + + # From Juan Correa (2016-03-18): +-# The decree regarding DST has been published in today's Official Gazette: +-# http://www.diariooficial.interior.gob.cl/versiones-anteriores/do/20160318/ +-# http://www.leychile.cl/Navegar?idNorma=1088502 ++# The decree regarding DST has been published in today's Official Gazette... + # It does consider the second Saturday of May and August as the dates + # for the transition; and it lists DST dates until 2019, but I think + # this scheme will stick. +-# + # From Paul Eggert (2016-03-18): +-# For now, assume the pattern holds for the indefinite future. + # The decree says transitions occur at 24:00; in practice this appears + # to mean 24:00 mainland time, not 24:00 local time, so that Easter + # Island is always two hours behind the mainland. ++# From Tim Parenti (2025-03-22): ++# Decreto 253 of 2016 of the Ministry of the Interior and Public Security, ++# promulgated 2016-03-16 and published 2016-03-18. ++# https://www.diariooficial.interior.gob.cl/media/2016/03/18/do-20160318.pdf#page=1 ++# https://www.bcn.cl/leychile/navegar?idNorma=1088502 + + # From Juan Correa (2016-12-04): + # Magallanes region ... will keep DST (UTC -3) all year round.... + # http://www.soychile.cl/Santiago/Sociedad/2016/12/04/433428/Bachelet-firmo-el-decreto-para-establecer-un-horario-unico-para-la-Region-de-Magallanes.aspx +-# From Deborah Goldsmith (2017-01-19): +-# http://www.diariooficial.interior.gob.cl/publicaciones/2017/01/17/41660/01/1169626.pdf ++# From Tim Parenti (2025-03-22), via Deborah Goldsmith (2017-01-19): ++# Decreto 1820 of 2016 of the Ministry of the Interior and Public Security, ++# promulgated 2016-12-02 and published 2017-01-17: ++# https://www.diariooficial.interior.gob.cl/publicaciones/2017/01/17/41660/01/1169626.pdf ++# https://www.bcn.cl/leychile/Navegar?idNorma=1099217 ++# Model this as a change to standard offset effective 2016-12-04. + + # From Juan Correa (2018-08-13): + # As of moments ago, the Ministry of Energy in Chile has announced the new +@@ -1316,13 +1326,20 @@ Zone America/Rio_Branco -4:31:12 - LMT 1914 + # https://twitter.com/MinEnergia/status/1029009354001973248 + # "We will keep the new time policy unchanged for at least the next 4 years." + # So we extend the new rules on Saturdays at 24:00 mainland time indefinitely. +-# From Juan Correa (2019-02-04): +-# http://www.diariooficial.interior.gob.cl/publicaciones/2018/11/23/42212/01/1498738.pdf ++# From Tim Parenti (2025-03-22), via Juan Correa (2019-02-04): ++# Decreto 1286 of 2018 of the Ministry of the Interior and Public Security, ++# promulgated 2018-09-21 and published 2018-11-23: ++# https://www.diariooficial.interior.gob.cl/publicaciones/2018/11/23/42212/01/1498738.pdf ++# https://www.bcn.cl/leychile/Navegar?idNorma=1125760 + + # From Juan Correa (2022-04-02): + # I found there was a decree published last Thursday that will keep +-# Magallanes region to UTC -3 "indefinitely". The decree is available at ++# Magallanes region to UTC -3 "indefinitely". ++# From Tim Parenti (2025-03-22): ++# Decreto 143 of 2022 of the Ministry of the Interior and Public Security, ++# promulgated 2022-03-29 and published 2022-03-31: + # https://www.diariooficial.interior.gob.cl/publicaciones/2022/03/31/43217-B/01/2108910.pdf ++# https://www.bcn.cl/leychile/Navegar?idNorma=1174342 + + # From Juan Correa (2022-08-09): + # the Internal Affairs Ministry (Ministerio del Interior) informed DST +@@ -1331,13 +1348,36 @@ Zone America/Rio_Branco -4:31:12 - LMT 1914 + # will keep UTC -3 "indefinitely"... This is because on September 4th + # we will have a voting whether to approve a new Constitution. + # +-# From Eduardo Romero Urra (2022-08-17): ++# From Tim Parenti (2025-03-22), via Eduardo Romero Urra (2022-08-17): ++# Decreto 224 of 2022 of the Ministry of the Interior and Public Security, ++# promulgated 2022-07-14 and published 2022-08-13: + # https://www.diariooficial.interior.gob.cl/publicaciones/2022/08/13/43327/01/2172567.pdf ++# https://www.bcn.cl/leychile/navegar?idNorma=1179983 + # + # From Paul Eggert (2022-08-17): + # Although the presidential decree stops at fall 2026, assume that + # similar DST rules will continue thereafter. + ++# From Paul Eggert (2025-01-15): ++# Diario Regional Aysén's Sebastián Martel reports that 94% of Aysén ++# citizens polled in November favored changing the rules from ++# -04/-03-with-DST to -03 all year... ++# https://www.diarioregionalaysen.cl/noticia/actualidad/2024/12/presentan-decision-que-gano-la-votacion-sobre-el-cambio-del-huso-horario-en-aysen ++# ++# From Yonathan Dossow (2025-03-20): ++# [T]oday we have more confirmation of the change. [Aysén] region will keep ++# UTC-3 all year... ++# https://www.cnnchile.com/pais/region-de-aysen-mantendra-horario-de-verano-todo-el-ano_20250320/ ++# https://www.latercera.com/nacional/noticia/tras-consulta-ciudadana-region-de-aysen-mantendra-el-horario-de-verano-durante-todo-el-ano/ ++# https://x.com/min_interior/status/1902692504270672098 ++# ++# From Tim Parenti (2025-03-22), via Eduardo Romero Urra (2025-03-20): ++# Decreto 93 of 2025 of the Ministry of the Interior and Public Security, ++# promulgated 2025-03-11 and published 2025-03-20: ++# https://www.diariooficial.interior.gob.cl/publicaciones/2025/03/20/44104/01/2624263.pdf ++# https://www.bcn.cl/leychile/Navegar?idNorma=1211955 ++# Model this as a change to standard offset effective 2025-03-20. ++ + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S + Rule Chile 1927 1931 - Sep 1 0:00 1:00 - + Rule Chile 1928 1932 - Apr 1 0:00 0 - +@@ -1394,6 +1434,20 @@ Zone America/Santiago -4:42:45 - LMT 1890 + -5:00 1:00 %z 1947 Mar 31 24:00 + -5:00 - %z 1947 May 21 23:00 + -4:00 Chile %z ++Zone America/Coyhaique -4:48:16 - LMT 1890 ++ -4:42:45 - SMT 1910 Jan 10 ++ -5:00 - %z 1916 Jul 1 ++ -4:42:45 - SMT 1918 Sep 10 ++ -4:00 - %z 1919 Jul 1 ++ -4:42:45 - SMT 1927 Sep 1 ++ -5:00 Chile %z 1932 Sep 1 ++ -4:00 - %z 1942 Jun 1 ++ -5:00 - %z 1942 Aug 1 ++ -4:00 - %z 1946 Aug 28 24:00 ++ -5:00 1:00 %z 1947 Mar 31 24:00 ++ -5:00 - %z 1947 May 21 23:00 ++ -4:00 Chile %z 2025 Mar 20 ++ -3:00 - %z + Zone America/Punta_Arenas -4:43:40 - LMT 1890 + -4:42:45 - SMT 1910 Jan 10 + -5:00 - %z 1916 Jul 1 +diff --git a/src/java.base/share/data/tzdata/zone.tab b/src/java.base/share/data/tzdata/zone.tab +index e7a4868c3..c8fc60104 100644 +--- a/src/java.base/share/data/tzdata/zone.tab ++++ b/src/java.base/share/data/tzdata/zone.tab +@@ -162,7 +162,8 @@ CH +4723+00832 Europe/Zurich + CI +0519-00402 Africa/Abidjan + CK -2114-15946 Pacific/Rarotonga + CL -3327-07040 America/Santiago most of Chile +-CL -5309-07055 America/Punta_Arenas Region of Magallanes ++CL -4534-07204 America/Coyhaique Aysen Region ++CL -5309-07055 America/Punta_Arenas Magallanes Region + CL -2709-10926 Pacific/Easter Easter Island + CM +0403+00942 Africa/Douala + CN +3114+12128 Asia/Shanghai Beijing Time +diff --git a/test/jdk/java/util/TimeZone/TimeZoneData/VERSION b/test/jdk/java/util/TimeZone/TimeZoneData/VERSION +index 5159b3786..750d9fae2 100644 +--- a/test/jdk/java/util/TimeZone/TimeZoneData/VERSION ++++ b/test/jdk/java/util/TimeZone/TimeZoneData/VERSION +@@ -1 +1 @@ +-tzdata2025a ++tzdata2025b +-- + diff --git a/Backport-JDK-8315338-RISC-V-Change-flags-for-stable-.patch b/Backport-JDK-8315338-RISC-V-Change-flags-for-stable-.patch new file mode 100644 index 0000000000000000000000000000000000000000..29dfaaf6f5fc13fb6f1a079983444b5f7ad147b5 --- /dev/null +++ b/Backport-JDK-8315338-RISC-V-Change-flags-for-stable-.patch @@ -0,0 +1,29 @@ +From 845c2409581d1c2435b1f9b0eab1169649042236 Mon Sep 17 00:00:00 2001 +From: Dingli Zhang +Date: Mon, 17 Mar 2025 16:24:36 +0800 +Subject: [PATCH] Backport JDK-8315338: RISC-V: Change flags for stable + extensions to non-experimental + + +diff --git a/src/hotspot/cpu/riscv/globals_riscv.hpp b/src/hotspot/cpu/riscv/globals_riscv.hpp +index ce572fd9a4d..53c4f2c2842 100644 +--- a/src/hotspot/cpu/riscv/globals_riscv.hpp ++++ b/src/hotspot/cpu/riscv/globals_riscv.hpp +@@ -101,10 +101,10 @@ define_pd_global(intx, InlineSmallCode, 1000); + product(bool, UseRVA20U64, true, "Use RVA20U64 profile") \ + product(bool, UseRVC, false, "Use RVC instructions") \ + product(bool, UseRVA22U64, false, EXPERIMENTAL, "Use RVA22U64 profile") \ +- product(bool, UseRVV, false, EXPERIMENTAL, "Use RVV instructions") \ +- product(bool, UseZba, false, EXPERIMENTAL, "Use Zba instructions") \ +- product(bool, UseZbb, false, EXPERIMENTAL, "Use Zbb instructions") \ +- product(bool, UseZbs, false, EXPERIMENTAL, "Use Zbs instructions") \ ++ product(bool, UseRVV, false, "Use RVV instructions") \ ++ product(bool, UseZba, false, "Use Zba instructions") \ ++ product(bool, UseZbb, false, "Use Zbb instructions") \ ++ product(bool, UseZbs, false, "Use Zbs instructions") \ + product(bool, UseZic64b, false, EXPERIMENTAL, "Use Zic64b instructions") \ + product(bool, UseZicbom, false, EXPERIMENTAL, "Use Zicbom instructions") \ + product(bool, UseZicbop, false, EXPERIMENTAL, "Use Zicbop instructions") \ +-- +2.34.1 + diff --git a/Backport-JDK-8328553-Get-rid-of-JApplet-in-test-jdk-.patch b/Backport-JDK-8328553-Get-rid-of-JApplet-in-test-jdk-.patch deleted file mode 100644 index 009f689cfc3874ab904c3726f521c6c2646d7843..0000000000000000000000000000000000000000 --- a/Backport-JDK-8328553-Get-rid-of-JApplet-in-test-jdk-.patch +++ /dev/null @@ -1,54 +0,0 @@ -Subject: Backport JDK-8328553 Get rid of JApplet in test/jdk/sanity/client/lib/SwingSet2/src/DemoModule.java - ---- - .../client/lib/SwingSet2/src/DemoModule.java | 15 ++++----------- - 1 file changed, 4 insertions(+), 11 deletions(-) - -diff --git a/test/jdk/sanity/client/lib/SwingSet2/src/DemoModule.java b/test/jdk/sanity/client/lib/SwingSet2/src/DemoModule.java -index 3511583e9..0e7c71083 100644 ---- a/test/jdk/sanity/client/lib/SwingSet2/src/DemoModule.java -+++ b/test/jdk/sanity/client/lib/SwingSet2/src/DemoModule.java -@@ -1,5 +1,5 @@ - /* -- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. -+ * Copyright (c) 2018, 2024, 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 -@@ -31,7 +31,6 @@ import java.net.URL; - import javax.swing.BoxLayout; - import javax.swing.Icon; - import javax.swing.ImageIcon; --import javax.swing.JApplet; - import javax.swing.JFrame; - import javax.swing.JPanel; - import javax.swing.UIManager; -@@ -42,10 +41,8 @@ import javax.swing.border.SoftBevelBorder; - - /** - * A generic SwingSet2 demo module -- * -- * @author Jeff Dinkins - */ --public class DemoModule extends JApplet { -+public class DemoModule extends JPanel { - - // The preferred size of the demo - private int PREFERRED_WIDTH = 680; -@@ -214,10 +211,6 @@ public class DemoModule extends JApplet { - demo.mainImpl(); - } - -- public void init() { -- getContentPane().setLayout(new BorderLayout()); -- getContentPane().add(getDemoPanel(), BorderLayout.CENTER); -- } -- - void updateDragEnabled(boolean dragEnabled) {} --} -\ No newline at end of file -+} -+ --- -2.33.0 - diff --git a/Backport-JDK-8329083-RISC-V-Update-profiles-supporte.patch b/Backport-JDK-8329083-RISC-V-Update-profiles-supporte.patch new file mode 100644 index 0000000000000000000000000000000000000000..f55980f9207e1e38cc70544a7cfaca0dd68c4c15 --- /dev/null +++ b/Backport-JDK-8329083-RISC-V-Update-profiles-supporte.patch @@ -0,0 +1,151 @@ +From 0d7ae782a1460d07b4c421539d6270b75e42f7eb Mon Sep 17 00:00:00 2001 +From: Dingli Zhang +Date: Mon, 17 Mar 2025 16:27:36 +0800 +Subject: [PATCH] Backport JDK-8329083: RISC-V: Update profiles supported on + riscv + + +diff --git a/src/hotspot/cpu/riscv/globals_riscv.hpp b/src/hotspot/cpu/riscv/globals_riscv.hpp +index 53c4f2c2842..85aaa3d239d 100644 +--- a/src/hotspot/cpu/riscv/globals_riscv.hpp ++++ b/src/hotspot/cpu/riscv/globals_riscv.hpp +@@ -99,8 +99,9 @@ define_pd_global(intx, InlineSmallCode, 1000); + product(bool, AvoidUnalignedAccesses, true, \ + "Avoid generating unaligned memory accesses") \ + product(bool, UseRVA20U64, true, "Use RVA20U64 profile") \ +- product(bool, UseRVC, false, "Use RVC instructions") \ + product(bool, UseRVA22U64, false, EXPERIMENTAL, "Use RVA22U64 profile") \ ++ product(bool, UseRVA23U64, false, EXPERIMENTAL, "Use RVA23U64 profile") \ ++ product(bool, UseRVC, false, "Use RVC instructions") \ + product(bool, UseRVV, false, "Use RVV instructions") \ + product(bool, UseZba, false, "Use Zba instructions") \ + product(bool, UseZbb, false, "Use Zbb instructions") \ +diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.cpp b/src/hotspot/cpu/riscv/vm_version_riscv.cpp +index 4ad0b16b623..febe7cc0d73 100644 +--- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp ++++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp +@@ -45,6 +45,18 @@ VM_Version::RVFeatureValue* VM_Version::_feature_list[] = { + RV_FEATURE_FLAGS(ADD_RV_FEATURE_IN_LIST) + nullptr}; + ++void VM_Version::useRVA20U64Profile() { ++ RV_USE_RVA20U64; ++} ++ ++void VM_Version::useRVA22U64Profile() { ++ RV_USE_RVA22U64; ++} ++ ++void VM_Version::useRVA23U64Profile() { ++ RV_USE_RVA23U64; ++} ++ + void VM_Version::initialize() { + _supports_cx8 = true; + _supports_atomic_getset4 = true; +@@ -62,41 +74,14 @@ void VM_Version::initialize() { + (int)satp_mode.value())); + } + +- // https://github.com/riscv/riscv-profiles/blob/main/profiles.adoc#rva20-profiles + if (UseRVA20U64) { +- if (FLAG_IS_DEFAULT(UseRVC)) { +- FLAG_SET_DEFAULT(UseRVC, true); +- } ++ useRVA20U64Profile(); + } +- // https://github.com/riscv/riscv-profiles/blob/main/profiles.adoc#rva22-profiles + if (UseRVA22U64) { +- if (FLAG_IS_DEFAULT(UseRVC)) { +- FLAG_SET_DEFAULT(UseRVC, true); +- } +- if (FLAG_IS_DEFAULT(UseZba)) { +- FLAG_SET_DEFAULT(UseZba, true); +- } +- if (FLAG_IS_DEFAULT(UseZbb)) { +- FLAG_SET_DEFAULT(UseZbb, true); +- } +- if (FLAG_IS_DEFAULT(UseZbs)) { +- FLAG_SET_DEFAULT(UseZbs, true); +- } +- if (FLAG_IS_DEFAULT(UseZic64b)) { +- FLAG_SET_DEFAULT(UseZic64b, true); +- } +- if (FLAG_IS_DEFAULT(UseZicbom)) { +- FLAG_SET_DEFAULT(UseZicbom, true); +- } +- if (FLAG_IS_DEFAULT(UseZicbop)) { +- FLAG_SET_DEFAULT(UseZicbop, true); +- } +- if (FLAG_IS_DEFAULT(UseZicboz)) { +- FLAG_SET_DEFAULT(UseZicboz, true); +- } +- if (FLAG_IS_DEFAULT(UseZihintpause)) { +- FLAG_SET_DEFAULT(UseZihintpause, true); +- } ++ useRVA22U64Profile(); ++ } ++ if (UseRVA23U64) { ++ useRVA23U64Profile(); + } + + if (UseZic64b) { +diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.hpp b/src/hotspot/cpu/riscv/vm_version_riscv.hpp +index 0de6e9a19e1..01c5cf0c600 100644 +--- a/src/hotspot/cpu/riscv/vm_version_riscv.hpp ++++ b/src/hotspot/cpu/riscv/vm_version_riscv.hpp +@@ -161,6 +161,51 @@ class VM_Version : public Abstract_VM_Version { + RV_FEATURE_FLAGS(DECLARE_RV_FEATURE) + #undef DECLARE_RV_FEATURE + ++ // enable extensions based on profile, current supported profiles: ++ // RVA20U64 ++ // RVA22U64 ++ // RVA23U64 ++ // NOTE: we only enable the mandatory extensions, not optional extension. ++ #define RV_ENABLE_EXTENSION(UseExtension) \ ++ if (FLAG_IS_DEFAULT(UseExtension)) { \ ++ FLAG_SET_DEFAULT(UseExtension, true); \ ++ } \ ++ ++ // https://github.com/riscv/riscv-profiles/blob/main/profiles.adoc#rva20-profiles ++ #define RV_USE_RVA20U64 \ ++ RV_ENABLE_EXTENSION(UseRVC) \ ++ ++ static void useRVA20U64Profile(); ++ ++ // https://github.com/riscv/riscv-profiles/blob/main/profiles.adoc#rva22-profiles ++ #define RV_USE_RVA22U64 \ ++ RV_ENABLE_EXTENSION(UseRVC) \ ++ RV_ENABLE_EXTENSION(UseZba) \ ++ RV_ENABLE_EXTENSION(UseZbb) \ ++ RV_ENABLE_EXTENSION(UseZbs) \ ++ RV_ENABLE_EXTENSION(UseZic64b) \ ++ RV_ENABLE_EXTENSION(UseZicbom) \ ++ RV_ENABLE_EXTENSION(UseZicbop) \ ++ RV_ENABLE_EXTENSION(UseZicboz) \ ++ RV_ENABLE_EXTENSION(UseZihintpause) \ ++ ++ static void useRVA22U64Profile(); ++ ++ // https://github.com/riscv/riscv-profiles/blob/main/rva23-profile.adoc#rva23u64-profile ++ #define RV_USE_RVA23U64 \ ++ RV_ENABLE_EXTENSION(UseRVC) \ ++ RV_ENABLE_EXTENSION(UseRVV) \ ++ RV_ENABLE_EXTENSION(UseZba) \ ++ RV_ENABLE_EXTENSION(UseZbb) \ ++ RV_ENABLE_EXTENSION(UseZbs) \ ++ RV_ENABLE_EXTENSION(UseZic64b) \ ++ RV_ENABLE_EXTENSION(UseZicbom) \ ++ RV_ENABLE_EXTENSION(UseZicbop) \ ++ RV_ENABLE_EXTENSION(UseZicboz) \ ++ RV_ENABLE_EXTENSION(UseZihintpause) \ ++ ++ static void useRVA23U64Profile(); ++ + // VM modes (satp.mode) privileged ISA 1.10 + enum VM_MODE : int { + VM_NOTSET = -1, +-- +2.34.1 + diff --git a/Backport-JDK-8336012-Fix-usages-of-jtreg-reserved-pr.patch b/Backport-JDK-8336012-Fix-usages-of-jtreg-reserved-pr.patch deleted file mode 100644 index 33b3f5e4aaf4f8d255cab2d54e5c0e8e5d132e0b..0000000000000000000000000000000000000000 --- a/Backport-JDK-8336012-Fix-usages-of-jtreg-reserved-pr.patch +++ /dev/null @@ -1,29 +0,0 @@ -Subject: Backport JDK-8336012 Fix usages of jtreg-reserved properties - ---- - test/jdk/java/lang/invoke/PrivateInvokeTest.java | 4 +--- - 1 file changed, 1 insertion(+), 3 deletions(-) - -diff --git a/test/jdk/java/lang/invoke/PrivateInvokeTest.java b/test/jdk/java/lang/invoke/PrivateInvokeTest.java -index 12edf8e32..8ae78d967 100644 ---- a/test/jdk/java/lang/invoke/PrivateInvokeTest.java -+++ b/test/jdk/java/lang/invoke/PrivateInvokeTest.java -@@ -1,5 +1,5 @@ - /* -- * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved. -+ * Copyright (c) 2009, 2024, 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 -@@ -67,8 +67,6 @@ public class PrivateInvokeTest { - String vstr = System.getProperty(THIS_CLASS.getSimpleName()+".verbose"); - if (vstr == null) - vstr = System.getProperty(THIS_CLASS.getName()+".verbose"); -- if (vstr == null) -- vstr = System.getProperty("test.verbose"); - if (vstr != null) verbose = Integer.parseInt(vstr); - } - private static int referenceKind(Method m) { --- -2.33.0 - diff --git a/Backport-JDK-8343555-RISC-V-make-some-verified-on-ha.patch b/Backport-JDK-8343555-RISC-V-make-some-verified-on-ha.patch new file mode 100644 index 0000000000000000000000000000000000000000..95b0e8f31e691edba2d5f5020a6b1d63024b6408 --- /dev/null +++ b/Backport-JDK-8343555-RISC-V-make-some-verified-on-ha.patch @@ -0,0 +1,31 @@ +From 2966967606532deac3dde80fcdbce301eecc19d1 Mon Sep 17 00:00:00 2001 +From: Dingli Zhang +Date: Mon, 17 Mar 2025 16:28:35 +0800 +Subject: [PATCH] Backport JDK-8343555: RISC-V: make some verified (on + hardware) extension options diagnostic + + +diff --git a/src/hotspot/cpu/riscv/globals_riscv.hpp b/src/hotspot/cpu/riscv/globals_riscv.hpp +index 85aaa3d239d..2ee0b4b9421 100644 +--- a/src/hotspot/cpu/riscv/globals_riscv.hpp ++++ b/src/hotspot/cpu/riscv/globals_riscv.hpp +@@ -101,11 +101,11 @@ define_pd_global(intx, InlineSmallCode, 1000); + product(bool, UseRVA20U64, true, "Use RVA20U64 profile") \ + product(bool, UseRVA22U64, false, EXPERIMENTAL, "Use RVA22U64 profile") \ + product(bool, UseRVA23U64, false, EXPERIMENTAL, "Use RVA23U64 profile") \ +- product(bool, UseRVC, false, "Use RVC instructions") \ +- product(bool, UseRVV, false, "Use RVV instructions") \ +- product(bool, UseZba, false, "Use Zba instructions") \ +- product(bool, UseZbb, false, "Use Zbb instructions") \ +- product(bool, UseZbs, false, "Use Zbs instructions") \ ++ product(bool, UseRVC, false, DIAGNOSTIC, "Use RVC instructions") \ ++ product(bool, UseRVV, false, DIAGNOSTIC, "Use RVV instructions") \ ++ product(bool, UseZba, false, DIAGNOSTIC, "Use Zba instructions") \ ++ product(bool, UseZbb, false, DIAGNOSTIC, "Use Zbb instructions") \ ++ product(bool, UseZbs, false, DIAGNOSTIC, "Use Zbs instructions") \ + product(bool, UseZic64b, false, EXPERIMENTAL, "Use Zic64b instructions") \ + product(bool, UseZicbom, false, EXPERIMENTAL, "Use Zicbom instructions") \ + product(bool, UseZicbop, false, EXPERIMENTAL, "Use Zicbop instructions") \ +-- +2.34.1 + diff --git a/Backport-JDK-8348384-RISC-V-Disable-auto-enable-Vect.patch b/Backport-JDK-8348384-RISC-V-Disable-auto-enable-Vect.patch new file mode 100644 index 0000000000000000000000000000000000000000..222968ccda3d0e4a40844da7ad50218046416ca0 --- /dev/null +++ b/Backport-JDK-8348384-RISC-V-Disable-auto-enable-Vect.patch @@ -0,0 +1,43 @@ +From 0310b533d409704f349b9e9bf861ee6afb9d67ee Mon Sep 17 00:00:00 2001 +From: Dingli Zhang +Date: Mon, 17 Mar 2025 16:31:52 +0800 +Subject: [PATCH] Backport JDK-8348384: RISC-V: Disable auto-enable Vector on + buggy kernels + + +diff --git a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp +index 243c4b850ee..991ce07bb7c 100644 +--- a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp ++++ b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp +@@ -25,6 +25,8 @@ + + #include "precompiled.hpp" + #include "logging/log.hpp" ++#include "logging/logMessage.hpp" ++#include "os_linux.hpp" + #include "riscv_hwprobe.hpp" + #include "runtime/os.hpp" + #include "runtime/vm_version.hpp" +@@ -134,7 +136,18 @@ void RiscvHwprobe::add_features_from_query_result() { + VM_Version::ext_C.enable_feature(); + } + if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_IMA_V)) { +- VM_Version::ext_V.enable_feature(); ++ // Linux signal return bug when using vector with vlen > 128b in pre 6.8.5. ++ long major, minor, patch; ++ os::Linux::kernel_version(&major, &minor, &patch); ++ if (os::Linux::kernel_version_compare(major, minor, patch, 6, 8, 5) == -1) { ++ LogMessage(os) log; ++ if (log.is_info()) { ++ log.info("Linux kernels before 6.8.5 (current %ld.%ld.%ld) have a known bug when using Vector and signals.", major, minor, patch); ++ log.info("Vector not enabled automatically via hwprobe, but can be turned on with -XX:+UseRVV."); ++ } ++ } else { ++ VM_Version::ext_V.enable_feature(); ++ } + } + if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZBA)) { + VM_Version::ext_Zba.enable_feature(); +-- +2.34.1 + diff --git a/Backport-JDK-8348554-Enhance-Linux-kernel-version-ch.patch b/Backport-JDK-8348554-Enhance-Linux-kernel-version-ch.patch new file mode 100644 index 0000000000000000000000000000000000000000..0e74d63883d714b350a9d8c2d4fe87c28cb76ec5 --- /dev/null +++ b/Backport-JDK-8348554-Enhance-Linux-kernel-version-ch.patch @@ -0,0 +1,100 @@ +From 2565e265b0381ed00428d33fc8040d9367ee394d Mon Sep 17 00:00:00 2001 +From: Dingli Zhang +Date: Mon, 17 Mar 2025 16:31:03 +0800 +Subject: [PATCH] Backport JDK-8348554: Enhance Linux kernel version checks + + +diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp +index 7cb862d7c3e..22216a568bc 100644 +--- a/src/hotspot/os/linux/os_linux.cpp ++++ b/src/hotspot/os/linux/os_linux.cpp +@@ -311,9 +311,10 @@ static void next_line(FILE *f) { + } while (c != '\n' && c != EOF); + } + +-void os::Linux::kernel_version(long* major, long* minor) { +- *major = -1; +- *minor = -1; ++void os::Linux::kernel_version(long* major, long* minor, long* patch) { ++ *major = 0; ++ *minor = 0; ++ *patch = 0; + + struct utsname buffer; + int ret = uname(&buffer); +@@ -321,12 +322,29 @@ void os::Linux::kernel_version(long* major, long* minor) { + log_warning(os)("uname(2) failed to get kernel version: %s", os::errno_name(ret)); + return; + } +- int nr_matched = sscanf(buffer.release, "%ld.%ld", major, minor); +- if (nr_matched != 2) { +- log_warning(os)("Parsing kernel version failed, expected 2 version numbers, only matched %d", nr_matched); ++ int nr_matched = sscanf(buffer.release, "%ld.%ld.%ld", major, minor, patch); ++ if (nr_matched != 3) { ++ log_warning(os)("Parsing kernel version failed, expected 3 version numbers, only matched %d", nr_matched); + } + } + ++int os::Linux::kernel_version_compare(long major1, long minor1, long patch1, ++ long major2, long minor2, long patch2) { ++ // Compare major versions ++ if (major1 > major2) return 1; ++ if (major1 < major2) return -1; ++ ++ // Compare minor versions ++ if (minor1 > minor2) return 1; ++ if (minor1 < minor2) return -1; ++ ++ // Compare patchlevel versions ++ if (patch1 > patch2) return 1; ++ if (patch1 < patch2) return -1; ++ ++ return 0; ++} ++ + bool os::Linux::get_tick_information(CPUPerfTicks* pticks, int which_logical_cpu) { + FILE* fh; + uint64_t userTicks, niceTicks, systemTicks, idleTicks; +diff --git a/src/hotspot/os/linux/os_linux.hpp b/src/hotspot/os/linux/os_linux.hpp +index 029f2aa7a52..f2901480d29 100644 +--- a/src/hotspot/os/linux/os_linux.hpp ++++ b/src/hotspot/os/linux/os_linux.hpp +@@ -109,7 +109,13 @@ class os::Linux { + bool has_steal_ticks; + }; + +- static void kernel_version(long* major, long* minor); ++ static void kernel_version(long* major, long* minor, long* patch); ++ ++ // If kernel1 > kernel2 return 1 ++ // If kernel1 < kernel2 return -1 ++ // If kernel1 = kernel2 return 0 ++ static int kernel_version_compare(long major1, long minor1, long patch1, ++ long major2, long minor2, long patch2); + + // which_logical_cpu=-1 returns accumulated ticks for all cpus. + static bool get_tick_information(CPUPerfTicks* pticks, int which_logical_cpu); +diff --git a/src/hotspot/os/linux/systemMemoryBarrier_linux.cpp b/src/hotspot/os/linux/systemMemoryBarrier_linux.cpp +index 51397b139d8..43122a58bce 100644 +--- a/src/hotspot/os/linux/systemMemoryBarrier_linux.cpp ++++ b/src/hotspot/os/linux/systemMemoryBarrier_linux.cpp +@@ -76,11 +76,11 @@ bool LinuxSystemMemoryBarrier::initialize() { + // RISCV port was introduced in kernel 4.4. + // 4.4 also made membar private expedited mandatory. + // But RISCV actually don't support it until 6.9. +- long major, minor; +- os::Linux::kernel_version(&major, &minor); +- if (!(major > 6 || (major == 6 && minor >= 9))) { +- log_info(os)("Linux kernel %ld.%ld does not support MEMBARRIER PRIVATE_EXPEDITED on RISC-V.", +- major, minor); ++ long major, minor, patch; ++ os::Linux::kernel_version(&major, &minor, &patch); ++ if (os::Linux::kernel_version_compare(major, minor, patch, 6, 9, 0) == -1) { ++ log_info(os)("Linux kernel %ld.%ld.%ld does not support MEMBARRIER PRIVATE_EXPEDITED on RISC-V.", ++ major, minor, patch); + return false; + } + #endif +-- +2.34.1 + diff --git a/Backport-JDK-8352673-RISC-V-Vector-can-t-be-turned-o.patch b/Backport-JDK-8352673-RISC-V-Vector-can-t-be-turned-o.patch new file mode 100644 index 0000000000000000000000000000000000000000..437e724ca9e21e1586e1c74c7a87aa2d18b6b663 --- /dev/null +++ b/Backport-JDK-8352673-RISC-V-Vector-can-t-be-turned-o.patch @@ -0,0 +1,23 @@ +From fc255c7c11af6a3e60e3cdd74ae0986579adbaae Mon Sep 17 00:00:00 2001 +From: Dingli Zhang +Date: Tue, 29 Apr 2025 22:41:45 +0800 +Subject: [PATCH] Backport JDK-8352673: RISC-V: Vector can't be turned on with + -XX:+UseRVV + + +diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.cpp b/src/hotspot/cpu/riscv/vm_version_riscv.cpp +index febe7cc0d73..97ac3a2e1d4 100644 +--- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp ++++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp +@@ -166,7 +166,7 @@ void VM_Version::initialize() { + } + + if (UseRVV) { +- if (!ext_V.enabled()) { ++ if (!ext_V.enabled() && FLAG_IS_DEFAULT(UseRVV)) { + warning("RVV is not supported on this CPU"); + FLAG_SET_DEFAULT(UseRVV, false); + } else { +-- +2.34.1 + diff --git a/Backport-JDK-8355878-RISC-V-jdk-incubator-vector-Dou.patch b/Backport-JDK-8355878-RISC-V-jdk-incubator-vector-Dou.patch new file mode 100644 index 0000000000000000000000000000000000000000..a898ee969ade23a5ca2ff7dcb9e118d7f8962485 --- /dev/null +++ b/Backport-JDK-8355878-RISC-V-jdk-incubator-vector-Dou.patch @@ -0,0 +1,34 @@ +From 5724de06f17cae3db89c911674d0b1906403af45 Mon Sep 17 00:00:00 2001 +From: Dingli Zhang +Date: Tue, 29 Apr 2025 22:42:53 +0800 +Subject: [PATCH] Backport JDK-8355878: RISC-V: + jdk/incubator/vector/DoubleMaxVectorTests.java fails when using RVV + + +diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad +index 148cf8dd3ae..d47b3a0502f 100644 +--- a/src/hotspot/cpu/riscv/riscv.ad ++++ b/src/hotspot/cpu/riscv/riscv.ad +@@ -1609,7 +1609,8 @@ uint MachSpillCopyNode::implementation(CodeBuffer *cbuf, PhaseRegAlloc *ra_, boo + __ unspill(as_VectorRegister(Matcher::_regEncode[dst_lo]), ra_->reg2offset(src_lo)); + } else if (src_lo_rc == rc_vector && dst_lo_rc == rc_vector) { + // vpr to vpr +- __ vmv1r_v(as_VectorRegister(Matcher::_regEncode[dst_lo]), as_VectorRegister(Matcher::_regEncode[src_lo])); ++ __ vsetvli_helper(T_BYTE, MaxVectorSize); ++ __ vmv_v_v(as_VectorRegister(Matcher::_regEncode[dst_lo]), as_VectorRegister(Matcher::_regEncode[src_lo])); + } else { + ShouldNotReachHere(); + } +@@ -1628,7 +1629,8 @@ uint MachSpillCopyNode::implementation(CodeBuffer *cbuf, PhaseRegAlloc *ra_, boo + __ unspill_vmask(as_VectorRegister(Matcher::_regEncode[dst_lo]), ra_->reg2offset(src_lo)); + } else if (src_lo_rc == rc_vector && dst_lo_rc == rc_vector) { + // vmask to vmask +- __ vmv1r_v(as_VectorRegister(Matcher::_regEncode[dst_lo]), as_VectorRegister(Matcher::_regEncode[src_lo])); ++ __ vsetvli_helper(T_BYTE, MaxVectorSize >> 3); ++ __ vmv_v_v(as_VectorRegister(Matcher::_regEncode[dst_lo]), as_VectorRegister(Matcher::_regEncode[src_lo])); + } else { + ShouldNotReachHere(); + } +-- +2.34.1 + diff --git a/LoongArch64-support.patch b/LoongArch64-support.patch index 4f8b7a6674d02c23983228ff094a7c56faffbd1b..8b5d303a676c059ff04048a72719b703782e3c47 100644 --- a/LoongArch64-support.patch +++ b/LoongArch64-support.patch @@ -1,8 +1,483 @@ -commit c35d5dd5296952bdcc213ea4e5768085a8d9e35b -Author: panxuefeng -Date: Tue Feb 11 20:20:18 2025 +0800 +From 790b520135eeeb11a6269bb16402d3160998363f Mon Sep 17 00:00:00 2001 +From: songliyang +Date: Wed, 30 Apr 2025 11:20:52 +0800 +Subject: [PATCH] LoongArch64 support - LoongArch Support +--- + make/autoconf/jvm-features.m4 | 18 +- + make/autoconf/platform.m4 | 17 + + .../abstractInterpreter_loongarch.cpp | 155 + + .../cpu/loongarch/assembler_loongarch.cpp | 820 + + .../cpu/loongarch/assembler_loongarch.hpp | 3213 ++++ + .../loongarch/assembler_loongarch.inline.hpp | 33 + + src/hotspot/cpu/loongarch/bytes_loongarch.hpp | 64 + + .../loongarch/c1_CodeStubs_loongarch_64.cpp | 338 + + .../cpu/loongarch/c1_Defs_loongarch.hpp | 88 + + .../loongarch/c1_FpuStackSim_loongarch.hpp | 32 + + .../loongarch/c1_FpuStackSim_loongarch_64.cpp | 31 + + .../cpu/loongarch/c1_FrameMap_loongarch.hpp | 143 + + .../loongarch/c1_FrameMap_loongarch_64.cpp | 345 + + .../loongarch/c1_LIRAssembler_loongarch.hpp | 84 + + .../c1_LIRAssembler_loongarch_64.cpp | 3383 ++++ + .../c1_LIRGenerator_loongarch_64.cpp | 1393 ++ + .../cpu/loongarch/c1_LIR_loongarch_64.cpp | 57 + + .../cpu/loongarch/c1_LinearScan_loongarch.hpp | 70 + + .../loongarch/c1_LinearScan_loongarch_64.cpp | 33 + + .../loongarch/c1_MacroAssembler_loongarch.hpp | 111 + + .../c1_MacroAssembler_loongarch_64.cpp | 350 + + .../loongarch/c1_Runtime1_loongarch_64.cpp | 1049 + + .../cpu/loongarch/c1_globals_loongarch.hpp | 64 + + .../cpu/loongarch/c2_CodeStubs_loongarch.cpp | 91 + + .../loongarch/c2_MacroAssembler_loongarch.cpp | 1903 ++ + .../loongarch/c2_MacroAssembler_loongarch.hpp | 141 + + .../cpu/loongarch/c2_globals_loongarch.hpp | 85 + + .../cpu/loongarch/c2_init_loongarch.cpp | 37 + + .../cpu/loongarch/codeBuffer_loongarch.cpp | 32 + + .../cpu/loongarch/codeBuffer_loongarch.hpp | 37 + + .../cpu/loongarch/compiledIC_loongarch.cpp | 138 + + .../loongarch/continuationEntry_loongarch.hpp | 33 + + .../continuationEntry_loongarch.inline.hpp | 52 + + ...ontinuationFreezeThaw_loongarch.inline.hpp | 284 + + .../continuationHelper_loongarch.inline.hpp | 145 + + src/hotspot/cpu/loongarch/copy_loongarch.cpp | 147 + + src/hotspot/cpu/loongarch/copy_loongarch.hpp | 65 + + .../cpu/loongarch/disassembler_loongarch.hpp | 49 + + .../loongarch/downcallLinker_loongarch_64.cpp | 360 + + .../loongarch/foreignGlobals_loongarch.cpp | 195 + + .../loongarch/foreignGlobals_loongarch.hpp | 50 + + src/hotspot/cpu/loongarch/frame_loongarch.cpp | 629 + + src/hotspot/cpu/loongarch/frame_loongarch.hpp | 200 + + .../cpu/loongarch/frame_loongarch.inline.hpp | 472 + + .../gc/g1/g1BarrierSetAssembler_loongarch.cpp | 491 + + .../gc/g1/g1BarrierSetAssembler_loongarch.hpp | 72 + + .../loongarch/gc/g1/g1Globals_loongarch.hpp | 30 + + .../shared/barrierSetAssembler_loongarch.cpp | 453 + + .../shared/barrierSetAssembler_loongarch.hpp | 147 + + .../gc/shared/barrierSetNMethod_loongarch.cpp | 222 + + ...cardTableBarrierSetAssembler_loongarch.cpp | 117 + + ...cardTableBarrierSetAssembler_loongarch.hpp | 44 + + .../modRefBarrierSetAssembler_loongarch.cpp | 53 + + .../modRefBarrierSetAssembler_loongarch.hpp | 54 + + .../c1/shenandoahBarrierSetC1_loongarch.cpp | 130 + + ...henandoahBarrierSetAssembler_loongarch.cpp | 783 + + ...henandoahBarrierSetAssembler_loongarch.hpp | 88 + + .../gc/shenandoah/shenandoah_loongarch_64.ad | 232 + + .../gc/x/xBarrierSetAssembler_loongarch.cpp | 471 + + .../gc/x/xBarrierSetAssembler_loongarch.hpp | 105 + + .../cpu/loongarch/gc/x/xGlobals_loongarch.cpp | 211 + + .../cpu/loongarch/gc/x/xGlobals_loongarch.hpp | 34 + + .../cpu/loongarch/gc/x/x_loongarch_64.ad | 256 + + .../cpu/loongarch/gc/z/zAddress_loongarch.cpp | 109 + + .../cpu/loongarch/gc/z/zAddress_loongarch.hpp | 35 + + .../gc/z/zAddress_loongarch.inline.hpp | 38 + + .../gc/z/zBarrierSetAssembler_loongarch.cpp | 1226 ++ + .../gc/z/zBarrierSetAssembler_loongarch.hpp | 192 + + .../cpu/loongarch/gc/z/zGlobals_loongarch.hpp | 30 + + .../cpu/loongarch/gc/z/z_loongarch_64.ad | 250 + + .../loongarch/globalDefinitions_loongarch.hpp | 58 + + .../cpu/loongarch/globals_loongarch.hpp | 125 + + .../cpu/loongarch/icBuffer_loongarch.cpp | 82 + + .../cpu/loongarch/icache_loongarch.cpp | 42 + + .../cpu/loongarch/icache_loongarch.hpp | 41 + + .../cpu/loongarch/interp_masm_loongarch.hpp | 272 + + .../loongarch/interp_masm_loongarch_64.cpp | 1965 ++ + .../cpu/loongarch/interpreterRT_loongarch.hpp | 62 + + .../loongarch/interpreterRT_loongarch_64.cpp | 264 + + .../loongarch/javaFrameAnchor_loongarch.hpp | 86 + + .../jniFastGetField_loongarch_64.cpp | 179 + + .../cpu/loongarch/jniTypes_loongarch.hpp | 143 + + .../jvmciCodeInstaller_loongarch.cpp | 194 + + src/hotspot/cpu/loongarch/loongarch.ad | 25 + + src/hotspot/cpu/loongarch/loongarch_64.ad | 15888 ++++++++++++++++ + .../loongarch/macroAssembler_loongarch.cpp | 4242 +++++ + .../loongarch/macroAssembler_loongarch.hpp | 809 + + .../macroAssembler_loongarch.inline.hpp | 937 + + .../macroAssembler_loongarch_chacha.cpp | 86 + + .../macroAssembler_loongarch_trig.cpp | 1626 ++ + .../cpu/loongarch/matcher_loongarch.hpp | 181 + + .../cpu/loongarch/methodHandles_loongarch.cpp | 568 + + .../cpu/loongarch/methodHandles_loongarch.hpp | 65 + + .../cpu/loongarch/nativeInst_loongarch.cpp | 537 + + .../cpu/loongarch/nativeInst_loongarch.hpp | 595 + + .../cpu/loongarch/registerMap_loongarch.hpp | 59 + + .../cpu/loongarch/register_loongarch.cpp | 61 + + .../cpu/loongarch/register_loongarch.hpp | 479 + + .../cpu/loongarch/relocInfo_loongarch.cpp | 133 + + .../cpu/loongarch/relocInfo_loongarch.hpp | 44 + + .../loongarch/sharedRuntime_loongarch_64.cpp | 2979 +++ + .../smallRegisterMap_loongarch.inline.hpp | 88 + + ...stackChunkFrameStream_loongarch.inline.hpp | 142 + + .../stackChunkOop_loongarch.inline.hpp | 43 + + .../loongarch/stubGenerator_loongarch_64.cpp | 5721 ++++++ + .../cpu/loongarch/stubRoutines_loongarch.hpp | 118 + + .../loongarch/stubRoutines_loongarch_64.cpp | 196 + + ...templateInterpreterGenerator_loongarch.cpp | 2106 ++ + .../cpu/loongarch/templateTable_loongarch.hpp | 43 + + .../loongarch/templateTable_loongarch_64.cpp | 4007 ++++ + .../loongarch/upcallLinker_loongarch_64.cpp | 351 + + .../cpu/loongarch/vmStructs_loongarch.hpp | 61 + + .../cpu/loongarch/vm_version_loongarch.cpp | 511 + + .../cpu/loongarch/vm_version_loongarch.hpp | 308 + + src/hotspot/cpu/loongarch/vmreg_loongarch.cpp | 54 + + src/hotspot/cpu/loongarch/vmreg_loongarch.hpp | 58 + + .../cpu/loongarch/vmreg_loongarch.inline.hpp | 38 + + .../cpu/loongarch/vmstorage_loongarch.hpp | 87 + + .../loongarch/vtableStubs_loongarch_64.cpp | 312 + + src/hotspot/os/linux/os_linux.cpp | 54 +- + src/hotspot/os/linux/os_linux.hpp | 8 + + .../os/linux/systemMemoryBarrier_linux.cpp | 8 + + .../assembler_linux_loongarch.cpp | 24 + + .../atomic_linux_loongarch.hpp | 499 + + .../copy_linux_loongarch.inline.hpp | 145 + + .../gc/x/xSyscall_linux_loongarch.hpp | 41 + + .../gc/z/zSyscall_linux_loongarch.hpp | 41 + + .../globals_linux_loongarch.hpp | 43 + + .../javaThread_linux_loongarch.cpp | 105 + + .../javaThread_linux_loongarch.hpp | 66 + + .../os_cpu/linux_loongarch/linux_loongarch.s | 25 + + .../orderAccess_linux_loongarch.hpp | 50 + + .../linux_loongarch/os_linux_loongarch.cpp | 474 + + .../os_linux_loongarch.inline.hpp | 29 + + .../prefetch_linux_loongarch.inline.hpp | 56 + + .../safefetch_linux_loongarch64.S | 56 + + .../vmStructs_linux_loongarch.hpp | 55 + + .../vm_version_linux_loongarch.cpp | 95 + + src/hotspot/share/adlc/formssel.cpp | 7 + + src/hotspot/share/c1/c1_Compiler.cpp | 8 +- + src/hotspot/share/c1/c1_LIR.cpp | 21 +- + src/hotspot/share/c1/c1_LIR.hpp | 25 +- + src/hotspot/share/c1/c1_LinearScan.cpp | 30 +- + src/hotspot/share/code/vtableStubs.cpp | 10 + + src/hotspot/share/gc/g1/g1Arguments.cpp | 20 + + .../gc/g1/g1ParScanThreadState.inline.hpp | 9 + + .../share/gc/parallel/parallelArguments.cpp | 12 + + .../share/gc/shared/barrierSetNMethod.cpp | 8 +- + .../share/gc/shared/c2/barrierSetC2.cpp | 15 + + .../gc/shenandoah/shenandoahArguments.cpp | 8 +- + src/hotspot/share/gc/z/c1/zBarrierSetC1.cpp | 17 + + .../share/gc/z/zStoreBarrierBuffer.hpp | 10 +- + .../share/interpreter/interpreterRuntime.cpp | 8 +- + .../share/interpreter/interpreterRuntime.hpp | 8 +- + .../templateInterpreterGenerator.hpp | 10 +- + .../share/jfr/utilities/jfrBigEndian.hpp | 8 +- + src/hotspot/share/jvmci/vmStructs_jvmci.cpp | 17 + + src/hotspot/share/memory/metaspace.cpp | 15 +- + .../share/oops/stackChunkOop.inline.hpp | 10 +- + src/hotspot/share/opto/classes.hpp | 7 + + src/hotspot/share/opto/compile.cpp | 7 + + src/hotspot/share/opto/memnode.cpp | 7 + + src/hotspot/share/opto/memnode.hpp | 14 + + src/hotspot/share/opto/output.cpp | 22 + + src/hotspot/share/runtime/arguments.cpp | 10 +- + src/hotspot/share/runtime/continuation.cpp | 8 +- + .../share/runtime/continuationFreezeThaw.cpp | 16 +- + .../share/runtime/javaThread.inline.hpp | 10 +- + src/hotspot/share/runtime/objectMonitor.cpp | 10 + + src/hotspot/share/runtime/os.cpp | 9 +- + src/hotspot/share/runtime/sharedRuntime.cpp | 8 +- + .../share/runtime/sharedRuntimeTrig.cpp | 16 + + src/hotspot/share/runtime/synchronizer.cpp | 8 +- + src/hotspot/share/runtime/vmStructs.cpp | 7 + + src/hotspot/share/utilities/macros.hpp | 18 + + .../classes/jdk/internal/foreign/CABI.java | 13 +- + .../internal/foreign/abi/AbstractLinker.java | 8 + + .../jdk/internal/foreign/abi/SharedUtils.java | 8 + + .../loongarch64/LoongArch64Architecture.java | 179 + + .../linux/LinuxLoongArch64CallArranger.java | 472 + + .../linux/LinuxLoongArch64Linker.java | 65 + + .../abi/loongarch64/linux/TypeClass.java | 217 + + .../jdk/internal/util/Architecture.java | 8 + + .../internal/util/PlatformProps.java.template | 8 + + .../native/libsaproc/LinuxDebuggerLocal.cpp | 28 +- + .../linux/native/libsaproc/libproc.h | 9 +- + .../linux/native/libsaproc/ps_proc.c | 8 +- + .../classes/sun/jvm/hotspot/HotSpotAgent.java | 9 + + .../MachineDescriptionLOONGARCH64.java | 41 + + .../debugger/linux/LinuxCDebugger.java | 17 +- + .../linux/LinuxThreadContextFactory.java | 11 +- + .../loongarch64/LinuxLOONGARCH64CFrame.java | 92 + + .../LinuxLOONGARCH64ThreadContext.java | 47 + + .../loongarch64/LOONGARCH64ThreadContext.java | 128 + + .../hotspot/debugger/posix/elf/ELFHeader.java | 8 + + .../debugger/remote/RemoteDebuggerClient.java | 11 + + .../loongarch64/RemoteLOONGARCH64Thread.java | 54 + + .../RemoteLOONGARCH64ThreadContext.java | 51 + + .../RemoteLOONGARCH64ThreadFactory.java | 45 + + .../sun/jvm/hotspot/runtime/Threads.java | 9 + + .../LinuxLOONGARCH64JavaThreadPDAccess.java | 139 + + .../LOONGARCH64CurrentFrameGuess.java | 250 + + .../runtime/loongarch64/LOONGARCH64Frame.java | 540 + + .../LOONGARCH64JavaCallWrapper.java | 59 + + .../loongarch64/LOONGARCH64RegisterMap.java | 52 + + .../jvm/hotspot/utilities/PlatformInfo.java | 12 +- + ...LoongArch64HotSpotJVMCIBackendFactory.java | 142 + + .../LoongArch64HotSpotRegisterConfig.java | 297 + + .../LoongArch64HotSpotVMConfig.java | 79 + + .../ci/hotspot/loongarch64/package-info.java | 28 + + .../jdk/vm/ci/loongarch64/LoongArch64.java | 251 + + .../vm/ci/loongarch64/LoongArch64Kind.java | 163 + + .../jdk/vm/ci/loongarch64/package-info.java | 28 + + .../share/classes/module-info.java | 7 + + src/utils/hsdis/binutils/hsdis-binutils.c | 9 + + .../arguments/TestCodeEntryAlignment.java | 8 +- + .../arraycopy/stress/TestStressArrayCopy.java | 14 + + test/hotspot/jtreg/compiler/c2/TestBit.java | 8 +- + .../compiler/c2/irTests/CmpUWithZero.java | 8 +- + .../irTests/TestAutoVecCountingDownLoop.java | 8 +- + .../jtreg/compiler/c2/irTests/TestIRAbs.java | 8 +- + .../c2/irTests/TestVectorizationNotRun.java | 8 +- + .../irTests/TestVectorizeTypeConversion.java | 8 +- + .../irTests/TestVectorizeURShiftSubword.java | 8 +- + .../TestAESIntrinsicsOnSupportedConfig.java | 8 +- + .../TestAESIntrinsicsOnUnsupportedConfig.java | 8 +- + .../intrinsics/TestCompareUnsigned.java | 9 +- + .../intrinsics/TestDoubleIsFinite.java | 8 +- + .../intrinsics/TestDoubleIsInfinite.java | 8 +- + .../intrinsics/TestFloatIsFinite.java | 8 +- + .../intrinsics/TestFloatIsInfinite.java | 8 +- + .../intrinsics/TestIntegerUnsignedDivMod.java | 8 +- + .../intrinsics/TestLongUnsignedDivMod.java | 8 +- + .../intrinsics/chacha/TestChaCha20.java | 12 + + .../float16/Binary16Conversion.java | 8 +- + .../float16/Binary16ConversionNaN.java | 8 +- + .../float16/TestAllFloat16ToFloat.java | 8 +- + .../float16/TestConstFloat16ToFloat.java | 8 +- + .../testcases/GenericTestCaseForOtherCPU.java | 13 +- + .../intrinsics/string/TestCountPositives.java | 12 +- + .../TestStringCompareToDifferentLength.java | 8 +- + .../vm/ci/code/test/CodeInstallationTest.java | 11 + + .../jdk/vm/ci/code/test/DataPatchTest.java | 11 +- + .../code/test/InterpreterFrameSizeTest.java | 11 +- + .../code/test/MaxOopMapStackOffsetTest.java | 11 +- + .../jdk/vm/ci/code/test/NativeCallTest.java | 11 +- + .../code/test/SimpleCodeInstallationTest.java | 11 +- + .../vm/ci/code/test/SimpleDebugInfoTest.java | 11 +- + .../code/test/VirtualObjectDebugInfoTest.java | 11 +- + .../loongarch64/LoongArch64TestAssembler.java | 568 + + .../compiler/lib/ir_framework/IRNode.java | 10 +- + .../loopopts/superword/ReductionPerf.java | 8 +- + .../TestRangeCheckHoistingScaledIV.java | 8 +- + .../runtime/TestConstantsInError.java | 14 +- + .../sharedstubs/SharedStubToInterpTest.java | 8 +- + .../sha/predicate/IntrinsicPredicates.java | 17 +- + .../compiler/vectorapi/TestVectorTest.java | 9 +- + .../VectorLogicalOpIdentityTest.java | 8 +- + .../vectorapi/VectorReverseBytesTest.java | 8 +- + .../vectorization/TestAutoVecIntMinMax.java | 8 +- + .../TestBufferVectorization.java | 8 +- + .../TestFloatConversionsVector.java | 8 +- + .../vectorization/TestPopulateIndex.java | 9 +- + .../vectorization/TestReverseBytes.java | 9 +- + .../vectorization/TestSignumVector.java | 8 +- + .../runner/ArrayIndexFillTest.java | 8 +- + .../runner/ArrayInvariantFillTest.java | 8 +- + .../runner/ArrayShiftOpTest.java | 8 +- + .../runner/BasicDoubleOpTest.java | 8 +- + .../runner/BasicFloatOpTest.java | 8 +- + .../vectorization/runner/BasicLongOpTest.java | 8 +- + .../runner/LoopArrayIndexComputeTest.java | 8 +- + .../runner/LoopReductionOpTest.java | 8 +- + .../compiler/TestLinkToNativeRBP.java | 8 +- + .../jtreg/loongson/25064/NUMAHelper.java | 100 + + .../loongson/25064/TestUseNUMADefault.java | 152 + + .../loongson/25064/TestUseNUMADisabled.java | 94 + + .../loongson/25064/TestUseNUMAEnabled.java | 165 + + .../jtreg/loongson/25443/Test25443.java | 58 + + .../jtreg/loongson/26733/Test26733.java | 570 + + .../jtreg/loongson/30358/MEMBARType.java | 38 + + .../jtreg/loongson/30358/TEST.properties | 25 + + .../jtreg/loongson/30358/TestLoadLoad.java | 129 + + .../30358/TestNewObjectWithFinal.java | 247 + + .../jtreg/loongson/30358/TestVolatile.java | 358 + + .../hotspot/jtreg/loongson/7432/Test7423.java | 61 + + .../ReservedStack/ReservedStackTest.java | 8 +- + .../jtreg/runtime/os/TestTracePageSizes.java | 8 +- + .../MyPackage/ASGCTBaseTest.java | 8 +- + .../sa/TestJhsdbJstackLineNumbers.java | 8 +- + .../ir_framework/tests/TestIRMatching.java | 8 +- + .../nsk/share/jdi/ArgumentHandler.java | 178 +- + .../TestLoongArch64CallArranger.java | 521 + + .../platform/PlatformLayouts.java | 59 + + .../ConcurrentHashMap/MapLoops.java | 8 +- + .../jdk/jfr/event/os/TestCPUInformation.java | 10 +- + .../nameservice/simple/DefaultCaching.java | 11 +- + ...stMutuallyExclusivePlatformPredicates.java | 8 +- + test/lib/jdk/test/lib/Platform.java | 10 + + .../org/openjdk/bench/loongarch/C2Memory.java | 67 + + .../bench/loongarch/MisAlignVector.java | 63 + + 301 files changed, 79758 insertions(+), 219 deletions(-) + create mode 100644 src/hotspot/cpu/loongarch/abstractInterpreter_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/assembler_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/assembler_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/assembler_loongarch.inline.hpp + create mode 100644 src/hotspot/cpu/loongarch/bytes_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/c1_CodeStubs_loongarch_64.cpp + create mode 100644 src/hotspot/cpu/loongarch/c1_Defs_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/c1_FpuStackSim_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/c1_FpuStackSim_loongarch_64.cpp + create mode 100644 src/hotspot/cpu/loongarch/c1_FrameMap_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/c1_FrameMap_loongarch_64.cpp + create mode 100644 src/hotspot/cpu/loongarch/c1_LIRAssembler_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/c1_LIRAssembler_loongarch_64.cpp + create mode 100644 src/hotspot/cpu/loongarch/c1_LIRGenerator_loongarch_64.cpp + create mode 100644 src/hotspot/cpu/loongarch/c1_LIR_loongarch_64.cpp + create mode 100644 src/hotspot/cpu/loongarch/c1_LinearScan_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/c1_LinearScan_loongarch_64.cpp + create mode 100644 src/hotspot/cpu/loongarch/c1_MacroAssembler_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/c1_MacroAssembler_loongarch_64.cpp + create mode 100644 src/hotspot/cpu/loongarch/c1_Runtime1_loongarch_64.cpp + create mode 100644 src/hotspot/cpu/loongarch/c1_globals_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/c2_CodeStubs_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/c2_MacroAssembler_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/c2_MacroAssembler_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/c2_globals_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/c2_init_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/codeBuffer_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/codeBuffer_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/compiledIC_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/continuationEntry_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/continuationEntry_loongarch.inline.hpp + create mode 100644 src/hotspot/cpu/loongarch/continuationFreezeThaw_loongarch.inline.hpp + create mode 100644 src/hotspot/cpu/loongarch/continuationHelper_loongarch.inline.hpp + create mode 100644 src/hotspot/cpu/loongarch/copy_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/copy_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/disassembler_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/downcallLinker_loongarch_64.cpp + create mode 100644 src/hotspot/cpu/loongarch/foreignGlobals_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/foreignGlobals_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/frame_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/frame_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/frame_loongarch.inline.hpp + create mode 100644 src/hotspot/cpu/loongarch/gc/g1/g1BarrierSetAssembler_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/gc/g1/g1BarrierSetAssembler_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/gc/g1/g1Globals_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/gc/shared/barrierSetAssembler_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/gc/shared/barrierSetAssembler_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/gc/shared/barrierSetNMethod_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/gc/shared/cardTableBarrierSetAssembler_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/gc/shared/cardTableBarrierSetAssembler_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/gc/shared/modRefBarrierSetAssembler_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/gc/shared/modRefBarrierSetAssembler_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/gc/shenandoah/c1/shenandoahBarrierSetC1_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/gc/shenandoah/shenandoahBarrierSetAssembler_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/gc/shenandoah/shenandoahBarrierSetAssembler_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/gc/shenandoah/shenandoah_loongarch_64.ad + create mode 100644 src/hotspot/cpu/loongarch/gc/x/xBarrierSetAssembler_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/gc/x/xBarrierSetAssembler_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/gc/x/xGlobals_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/gc/x/xGlobals_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/gc/x/x_loongarch_64.ad + create mode 100644 src/hotspot/cpu/loongarch/gc/z/zAddress_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/gc/z/zAddress_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/gc/z/zAddress_loongarch.inline.hpp + create mode 100644 src/hotspot/cpu/loongarch/gc/z/zBarrierSetAssembler_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/gc/z/zBarrierSetAssembler_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/gc/z/zGlobals_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/gc/z/z_loongarch_64.ad + create mode 100644 src/hotspot/cpu/loongarch/globalDefinitions_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/globals_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/icBuffer_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/icache_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/icache_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/interp_masm_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/interp_masm_loongarch_64.cpp + create mode 100644 src/hotspot/cpu/loongarch/interpreterRT_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/interpreterRT_loongarch_64.cpp + create mode 100644 src/hotspot/cpu/loongarch/javaFrameAnchor_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/jniFastGetField_loongarch_64.cpp + create mode 100644 src/hotspot/cpu/loongarch/jniTypes_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/jvmciCodeInstaller_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/loongarch.ad + create mode 100644 src/hotspot/cpu/loongarch/loongarch_64.ad + create mode 100644 src/hotspot/cpu/loongarch/macroAssembler_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/macroAssembler_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/macroAssembler_loongarch.inline.hpp + create mode 100644 src/hotspot/cpu/loongarch/macroAssembler_loongarch_chacha.cpp + create mode 100644 src/hotspot/cpu/loongarch/macroAssembler_loongarch_trig.cpp + create mode 100644 src/hotspot/cpu/loongarch/matcher_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/methodHandles_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/methodHandles_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/nativeInst_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/nativeInst_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/registerMap_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/register_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/register_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/relocInfo_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/relocInfo_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/sharedRuntime_loongarch_64.cpp + create mode 100644 src/hotspot/cpu/loongarch/smallRegisterMap_loongarch.inline.hpp + create mode 100644 src/hotspot/cpu/loongarch/stackChunkFrameStream_loongarch.inline.hpp + create mode 100644 src/hotspot/cpu/loongarch/stackChunkOop_loongarch.inline.hpp + create mode 100644 src/hotspot/cpu/loongarch/stubGenerator_loongarch_64.cpp + create mode 100644 src/hotspot/cpu/loongarch/stubRoutines_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/stubRoutines_loongarch_64.cpp + create mode 100644 src/hotspot/cpu/loongarch/templateInterpreterGenerator_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/templateTable_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/templateTable_loongarch_64.cpp + create mode 100644 src/hotspot/cpu/loongarch/upcallLinker_loongarch_64.cpp + create mode 100644 src/hotspot/cpu/loongarch/vmStructs_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/vm_version_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/vm_version_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/vmreg_loongarch.cpp + create mode 100644 src/hotspot/cpu/loongarch/vmreg_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/vmreg_loongarch.inline.hpp + create mode 100644 src/hotspot/cpu/loongarch/vmstorage_loongarch.hpp + create mode 100644 src/hotspot/cpu/loongarch/vtableStubs_loongarch_64.cpp + create mode 100644 src/hotspot/os_cpu/linux_loongarch/assembler_linux_loongarch.cpp + create mode 100644 src/hotspot/os_cpu/linux_loongarch/atomic_linux_loongarch.hpp + create mode 100644 src/hotspot/os_cpu/linux_loongarch/copy_linux_loongarch.inline.hpp + create mode 100644 src/hotspot/os_cpu/linux_loongarch/gc/x/xSyscall_linux_loongarch.hpp + create mode 100644 src/hotspot/os_cpu/linux_loongarch/gc/z/zSyscall_linux_loongarch.hpp + create mode 100644 src/hotspot/os_cpu/linux_loongarch/globals_linux_loongarch.hpp + create mode 100644 src/hotspot/os_cpu/linux_loongarch/javaThread_linux_loongarch.cpp + create mode 100644 src/hotspot/os_cpu/linux_loongarch/javaThread_linux_loongarch.hpp + create mode 100644 src/hotspot/os_cpu/linux_loongarch/linux_loongarch.s + create mode 100644 src/hotspot/os_cpu/linux_loongarch/orderAccess_linux_loongarch.hpp + create mode 100644 src/hotspot/os_cpu/linux_loongarch/os_linux_loongarch.cpp + create mode 100644 src/hotspot/os_cpu/linux_loongarch/os_linux_loongarch.inline.hpp + create mode 100644 src/hotspot/os_cpu/linux_loongarch/prefetch_linux_loongarch.inline.hpp + create mode 100644 src/hotspot/os_cpu/linux_loongarch/safefetch_linux_loongarch64.S + create mode 100644 src/hotspot/os_cpu/linux_loongarch/vmStructs_linux_loongarch.hpp + create mode 100644 src/hotspot/os_cpu/linux_loongarch/vm_version_linux_loongarch.cpp + create mode 100644 src/java.base/share/classes/jdk/internal/foreign/abi/loongarch64/LoongArch64Architecture.java + create mode 100644 src/java.base/share/classes/jdk/internal/foreign/abi/loongarch64/linux/LinuxLoongArch64CallArranger.java + create mode 100644 src/java.base/share/classes/jdk/internal/foreign/abi/loongarch64/linux/LinuxLoongArch64Linker.java + create mode 100644 src/java.base/share/classes/jdk/internal/foreign/abi/loongarch64/linux/TypeClass.java + create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionLOONGARCH64.java + create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/loongarch64/LinuxLOONGARCH64CFrame.java + create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/loongarch64/LinuxLOONGARCH64ThreadContext.java + create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/loongarch64/LOONGARCH64ThreadContext.java + create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/remote/loongarch64/RemoteLOONGARCH64Thread.java + create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/remote/loongarch64/RemoteLOONGARCH64ThreadContext.java + create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/remote/loongarch64/RemoteLOONGARCH64ThreadFactory.java + create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/linux_loongarch64/LinuxLOONGARCH64JavaThreadPDAccess.java + create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/loongarch64/LOONGARCH64CurrentFrameGuess.java + create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/loongarch64/LOONGARCH64Frame.java + create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/loongarch64/LOONGARCH64JavaCallWrapper.java + create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/loongarch64/LOONGARCH64RegisterMap.java + create mode 100644 src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/loongarch64/LoongArch64HotSpotJVMCIBackendFactory.java + create mode 100644 src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/loongarch64/LoongArch64HotSpotRegisterConfig.java + create mode 100644 src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/loongarch64/LoongArch64HotSpotVMConfig.java + create mode 100644 src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/loongarch64/package-info.java + create mode 100644 src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/loongarch64/LoongArch64.java + create mode 100644 src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/loongarch64/LoongArch64Kind.java + create mode 100644 src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/loongarch64/package-info.java + create mode 100644 test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/loongarch64/LoongArch64TestAssembler.java + create mode 100644 test/hotspot/jtreg/loongson/25064/NUMAHelper.java + create mode 100644 test/hotspot/jtreg/loongson/25064/TestUseNUMADefault.java + create mode 100644 test/hotspot/jtreg/loongson/25064/TestUseNUMADisabled.java + create mode 100644 test/hotspot/jtreg/loongson/25064/TestUseNUMAEnabled.java + create mode 100644 test/hotspot/jtreg/loongson/25443/Test25443.java + create mode 100644 test/hotspot/jtreg/loongson/26733/Test26733.java + create mode 100644 test/hotspot/jtreg/loongson/30358/MEMBARType.java + create mode 100644 test/hotspot/jtreg/loongson/30358/TEST.properties + create mode 100644 test/hotspot/jtreg/loongson/30358/TestLoadLoad.java + create mode 100644 test/hotspot/jtreg/loongson/30358/TestNewObjectWithFinal.java + create mode 100644 test/hotspot/jtreg/loongson/30358/TestVolatile.java + create mode 100644 test/hotspot/jtreg/loongson/7432/Test7423.java + create mode 100644 test/jdk/java/foreign/callarranger/TestLoongArch64CallArranger.java + create mode 100644 test/micro/org/openjdk/bench/loongarch/C2Memory.java + create mode 100644 test/micro/org/openjdk/bench/loongarch/MisAlignVector.java diff --git a/make/autoconf/jvm-features.m4 b/make/autoconf/jvm-features.m4 index 288019848..c37c5c274 100644 @@ -70749,7 +71224,7 @@ index 000000000..ab1202183 + return icache_line_size; +} diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp -index 7cb862d7c..21093e523 100644 +index 5ebd57fb3..f74969e66 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -23,6 +23,12 @@ @@ -70765,7 +71240,7 @@ index 7cb862d7c..21093e523 100644 // no precompiled headers #include "classfile/vmSymbols.hpp" #include "code/icBuffer.hpp" -@@ -2200,6 +2206,12 @@ bool os::Linux::query_process_memory_info(os::Linux::meminfo_t* info) { +@@ -2236,6 +2242,12 @@ bool os::Linux::query_process_memory_info(os::Linux::meminfo_t* info) { return false; } @@ -70778,7 +71253,7 @@ index 7cb862d7c..21093e523 100644 #ifdef __GLIBC__ // For Glibc, print a one-liner with the malloc tunables. // Most important and popular is MALLOC_ARENA_MAX, but we are -@@ -2416,7 +2428,7 @@ void os::print_memory_info(outputStream* st) { +@@ -2452,7 +2464,7 @@ void os::print_memory_info(outputStream* st) { // before "flags" so if we find a second "model name", then the // "flags" field is considered missing. static bool print_model_name_and_flags(outputStream* st, char* buf, size_t buflen) { @@ -70787,7 +71262,7 @@ index 7cb862d7c..21093e523 100644 // Other platforms have less repetitive cpuinfo files FILE *fp = os::fopen("/proc/cpuinfo", "r"); if (fp) { -@@ -2528,7 +2540,7 @@ void os::jfr_report_memory_info() { +@@ -2564,7 +2576,7 @@ void os::jfr_report_memory_info() { #endif // INCLUDE_JFR @@ -70796,7 +71271,7 @@ index 7cb862d7c..21093e523 100644 const char* search_string = "model name"; #elif defined(M68K) const char* search_string = "CPU"; -@@ -4531,6 +4543,44 @@ void os::Linux::numa_init() { +@@ -4567,6 +4579,44 @@ void os::Linux::numa_init() { // If there's only one node (they start from 0) or if the process // is bound explicitly to a single node using membind, disable NUMA UseNUMA = false; @@ -71994,10 +72469,10 @@ index 000000000..2d096943a +#endif // OS_CPU_LINUX_LOONGARCH_ORDERACCESS_LINUX_LOONGARCH_HPP diff --git a/src/hotspot/os_cpu/linux_loongarch/os_linux_loongarch.cpp b/src/hotspot/os_cpu/linux_loongarch/os_linux_loongarch.cpp new file mode 100644 -index 000000000..31bfcba8d +index 000000000..304ceb7b3 --- /dev/null +++ b/src/hotspot/os_cpu/linux_loongarch/os_linux_loongarch.cpp -@@ -0,0 +1,491 @@ +@@ -0,0 +1,474 @@ +/* + * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, Loongson Technology. All rights reserved. @@ -72458,23 +72933,6 @@ index 000000000..31bfcba8d + st->cr(); +} + -+void os::print_tos_pc(outputStream *st, const void *context) { -+ if (context == nullptr) return; -+ -+ const ucontext_t* uc = (const ucontext_t*)context; -+ -+ address sp = (address)os::Linux::ucontext_get_sp(uc); -+ print_tos(st, sp); -+ st->cr(); -+ -+ // Note: it may be unsafe to inspect memory near pc. For example, pc may -+ // point to garbage if entry point in an nmethod is corrupted. Leave -+ // this at the end, and hope for the best. -+ address pc = os::fetch_frame_from_context(uc).pc(); -+ print_instructions(st, pc); -+ st->cr(); -+} -+ +void os::setup_fpu() { + // no use for LA +} @@ -73964,7 +74422,7 @@ index ee0f754b8..e29e71641 100644 assert(owner_raw() != current, "invariant"); assert(_Responsible != current, "invariant"); diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp -index cd0709a7b..0c40c2730 100644 +index 286a148ac..17c94339a 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -22,6 +22,12 @@ @@ -73980,7 +74438,7 @@ index cd0709a7b..0c40c2730 100644 #include "precompiled.hpp" #include "classfile/javaClasses.hpp" #include "classfile/moduleEntry.hpp" -@@ -1311,7 +1317,8 @@ bool os::is_first_C_frame(frame* fr) { +@@ -1331,7 +1337,8 @@ bool os::is_first_C_frame(frame* fr) { if ((uintptr_t)fr->sender_sp() == (uintptr_t)-1 || is_pointer_bad(fr->sender_sp())) return true; uintptr_t old_fp = (uintptr_t)fr->link_or_null(); @@ -79788,7 +80246,7 @@ index 000000000..4c7686845 + +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java -index e9d84883f..f4cd7ec9d 100644 +index 8eb1af01d..151c422f9 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -21,6 +21,12 @@ @@ -80237,10 +80695,10 @@ index bf6a10b85..b97c0461e 100644 * @run driver compiler.vectorization.TestSignumVector */ diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java -index a4eca0fe8..de44f6052 100644 +index 5c9fc7d4f..ea6ccc8df 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java -@@ -21,6 +21,12 @@ +@@ -22,6 +22,12 @@ * questions. */ @@ -80253,7 +80711,7 @@ index a4eca0fe8..de44f6052 100644 /* * @test * @summary Vectorization test on array index fill -@@ -35,7 +41,7 @@ +@@ -36,7 +42,7 @@ * -XX:+WhiteBoxAPI * compiler.vectorization.runner.ArrayIndexFillTest * @@ -80289,10 +80747,10 @@ index 2e7302bba..964c03955 100644 */ diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayShiftOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayShiftOpTest.java -index eea79ac4c..526aec568 100644 +index 44f9286c6..bf90e9ef7 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayShiftOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayShiftOpTest.java -@@ -21,6 +21,12 @@ +@@ -22,6 +22,12 @@ * questions. */ @@ -80305,7 +80763,7 @@ index eea79ac4c..526aec568 100644 /* * @test * @bug 8183390 8332905 -@@ -36,7 +42,7 @@ +@@ -37,7 +43,7 @@ * -XX:+WhiteBoxAPI * compiler.vectorization.runner.ArrayShiftOpTest * @@ -83615,7 +84073,7 @@ index e78e200ac..0a6abfd08 100644 OS("isAix", "isLinux", "isOSX", "isWindows"), VM_TYPE("isClient", "isServer", "isMinimal", "isZero", "isEmbedded"), diff --git a/test/lib/jdk/test/lib/Platform.java b/test/lib/jdk/test/lib/Platform.java -index e2451fc83..be0ecfd98 100644 +index 4a4b164cd..ad6b11c3e 100644 --- a/test/lib/jdk/test/lib/Platform.java +++ b/test/lib/jdk/test/lib/Platform.java @@ -21,6 +21,12 @@ @@ -83631,7 +84089,7 @@ index e2451fc83..be0ecfd98 100644 package jdk.test.lib; import java.io.BufferedReader; -@@ -234,6 +240,10 @@ public class Platform { +@@ -235,6 +241,10 @@ public class Platform { return isArch("(i386)|(x86(?!_64))"); } @@ -83784,3 +84242,6 @@ index 000000000..1ee6649cd + } + } +} +-- +2.43.0 + diff --git a/heapdump-bug-fix.patch b/heapdump-bug-fix.patch new file mode 100644 index 0000000000000000000000000000000000000000..0533acba80c92a36469c34c991c6542bbf21d3be --- /dev/null +++ b/heapdump-bug-fix.patch @@ -0,0 +1,103 @@ +From 04681c0e50c884ef1835af026cee6ce8d8a826bc Mon Sep 17 00:00:00 2001 +Date: Wed, 14 May 2025 16:34:14 +0800 +Subject: heapdump bug fix + +--- + make/common/NativeCompilation.gmk | 4 ++-- + src/hotspot/os_cpu/linux_arm/linux_arm_32.S | 2 ++ + src/hotspot/share/services/heapDumper.cpp | 21 ++++++++++++++------- + 3 files changed, 18 insertions(+), 9 deletions(-) + +diff --git a/make/common/NativeCompilation.gmk b/make/common/NativeCompilation.gmk +index fbf2f62e0..4106c9de8 100644 +--- a/make/common/NativeCompilation.gmk ++++ b/make/common/NativeCompilation.gmk +@@ -795,7 +795,7 @@ define SetupNativeCompilationBody + ifeq ($$($1_COMPILE_WITH_DEBUG_SYMBOLS), true) + $1_EXTRA_CFLAGS += $$(CFLAGS_DEBUG_SYMBOLS) + $1_EXTRA_CXXFLAGS += $$(CFLAGS_DEBUG_SYMBOLS) +- ifeq ($(findstring $(OPENJDK_TARGET_CPU), arm), ) ++ ifneq ($(findstring $(OPENJDK_TARGET_CPU), arm), ) + $1_EXTRA_CFLAGS := $(filter-out -gdwarf-aranges,$(1_EXTRA_CFLAGS)) + $1_EXTRA_CXXFLAGS := $(filter-out -gdwarf-aranges,$(1_EXTRA_CXXFLAGS)) + endif +@@ -904,7 +904,7 @@ define SetupNativeCompilationBody + $1_PCH_FILE := $$($1_OBJECT_DIR)/precompiled/$$(notdir $$($1_PRECOMPILED_HEADER)).gch + $1_USE_PCH_FLAGS := -I$$($1_OBJECT_DIR)/precompiled + else ifeq ($(TOOLCHAIN_TYPE), clang) +- ifeq ($(findstring $(OPENJDK_TARGET_CPU), arm), ) ++ ifneq ($(findstring $(OPENJDK_TARGET_CPU), arm), ) + $1_PCH_FILE := $$($1_OBJECT_DIR)/precompiled/$$(notdir $$($1_PRECOMPILED_HEADER)).gch + $1_USE_PCH_FLAGS := -I$$($1_OBJECT_DIR)/precompiled + else +diff --git a/src/hotspot/os_cpu/linux_arm/linux_arm_32.S b/src/hotspot/os_cpu/linux_arm/linux_arm_32.S +index cfac173b1..aa839a1e3 100644 +--- a/src/hotspot/os_cpu/linux_arm/linux_arm_32.S ++++ b/src/hotspot/os_cpu/linux_arm/linux_arm_32.S +@@ -28,6 +28,8 @@ + # point or use it in the same manner as does the server + # compiler. + ++ .globl _Copy_conjoint_bytes ++ .type _Copy_conjoint_bytes, %function + .globl _Copy_arrayof_conjoint_bytes + .type _Copy_arrayof_conjoint_bytes, %function + .globl _Copy_disjoint_words +diff --git a/src/hotspot/share/services/heapDumper.cpp b/src/hotspot/share/services/heapDumper.cpp +index bbef3bfa1..6f7f9afdc 100644 +--- a/src/hotspot/share/services/heapDumper.cpp ++++ b/src/hotspot/share/services/heapDumper.cpp +@@ -2492,8 +2492,20 @@ class VM_HeapDumper : public VM_GC_Operation, public WorkerTask { + assert(_global_writer == nullptr, "Error"); + _global_writer = _local_writer; + } ++ void set_dump_instance_fields_descriptors() { ++ assert(_dump_instance_fields_descriptors == nullptr, "Error"); ++ assert(_global_writer != nullptr, "Error"); ++ if(_global_writer->getHeapDumpRedactLevel() == REDACT_ANNOTATION) { ++ _dump_instance_fields_descriptors = DumperSupport::dump_instance_annotation_field_descriptors; ++ } else if(_global_writer->getHeapDumpRedactLevel() == REDACT_DIYRULES) { ++ _dump_instance_fields_descriptors = DumperSupport::dump_instance_diyrules_field_descriptors; ++ } else { ++ _dump_instance_fields_descriptors = DumperSupport::dump_instance_field_descriptors; ++ } ++ } + void clear_global_dumper() { _global_dumper = nullptr; } + void clear_global_writer() { _global_writer = nullptr; } ++ void clear_dump_instance_fields_descriptors() { _dump_instance_fields_descriptors = nullptr; } + + bool skip_operation() const; + +@@ -2536,13 +2548,6 @@ class VM_HeapDumper : public VM_GC_Operation, public WorkerTask { + _dumper_controller = nullptr; + _poi = nullptr; + _large_object_list = new (std::nothrow) HeapDumpLargeObjectList(); +- if(writer->getHeapDumpRedactLevel() == REDACT_ANNOTATION) { +- _dump_instance_fields_descriptors = DumperSupport::dump_instance_annotation_field_descriptors; +- } else if(writer->getHeapDumpRedactLevel() == REDACT_DIYRULES) { +- _dump_instance_fields_descriptors = DumperSupport::dump_instance_diyrules_field_descriptors; +- } else { +- _dump_instance_fields_descriptors = DumperSupport::dump_instance_field_descriptors; +- } + + if (oome) { + assert(!Thread::current()->is_VM_thread(), "Dump from OutOfMemoryError cannot be called by the VMThread"); +@@ -2807,6 +2812,7 @@ void VM_HeapDumper::doit() { + // the following should be safe. + set_global_dumper(); + set_global_writer(); ++ set_dump_instance_fields_descriptors(); + + WorkerThreads* workers = ch->safepoint_workers(); + if (workers == nullptr) { +@@ -2832,6 +2838,7 @@ void VM_HeapDumper::doit() { + // Now we clear the global variables, so that a future dumper can run. + clear_global_dumper(); + clear_global_writer(); ++ clear_dump_instance_fields_descriptors(); + } + + void VM_HeapDumper::work(uint worker_id) { +-- +2.43.0.windows.1 + diff --git a/huawei-1-add-port-jbolt-feature.patch b/huawei-1-add-port-jbolt-feature.patch new file mode 100644 index 0000000000000000000000000000000000000000..92b5101a238f1ad8b6b1aaf4138825811a5fd5c4 --- /dev/null +++ b/huawei-1-add-port-jbolt-feature.patch @@ -0,0 +1,5391 @@ +Date: Fri, 14 Mar 2025 07:02:01 +0000 +Subject: !1 add port jbolt feature * add port jbolt feature + +--- + make/hotspot/lib/JvmFeatures.gmk | 2 + + make/hotspot/lib/JvmFlags.gmk | 6 + + make/hotspot/lib/JvmMapfile.gmk | 6 + + src/hotspot/os/linux/os_linux.cpp | 40 + + src/hotspot/os/linux/os_linux.hpp | 30 +- + src/hotspot/share/ci/ciEnv.cpp | 41 +- + src/hotspot/share/code/codeBlob.hpp | 9 +- + src/hotspot/share/code/codeCache.cpp | 33 + + src/hotspot/share/code/codeCache.hpp | 16 +- + src/hotspot/share/code/nmethod.cpp | 19 + + src/hotspot/share/code/nmethod.hpp | 8 + + src/hotspot/share/compiler/compileBroker.cpp | 9 + + src/hotspot/share/compiler/compileBroker.hpp | 3 + + src/hotspot/share/compiler/compileTask.hpp | 12 + + src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 8 + + src/hotspot/share/jbolt/jBoltCallGraph.cpp | 480 ++++++ + src/hotspot/share/jbolt/jBoltCallGraph.hpp | 277 ++++ + .../share/jbolt/jBoltControlThread.cpp | 220 +++ + .../share/jbolt/jBoltControlThread.hpp | 69 + + src/hotspot/share/jbolt/jBoltDcmds.cpp | 221 +++ + src/hotspot/share/jbolt/jBoltDcmds.hpp | 129 ++ + src/hotspot/share/jbolt/jBoltManager.cpp | 1429 +++++++++++++++++ + src/hotspot/share/jbolt/jBoltManager.hpp | 335 ++++ + src/hotspot/share/jbolt/jBoltUtils.cpp | 38 + + src/hotspot/share/jbolt/jBoltUtils.hpp | 55 + + src/hotspot/share/jbolt/jBoltUtils.inline.hpp | 38 + + src/hotspot/share/jbolt/jbolt_globals.hpp | 62 + + src/hotspot/share/jfr/metadata/metadata.xml | 2 + + .../share/jfr/periodic/jfrPeriodic.cpp | 7 + + .../periodic/sampling/jfrThreadSampler.cpp | 13 +- + .../jfr/recorder/stacktrace/jfrStackTrace.cpp | 12 +- + .../jfr/recorder/stacktrace/jfrStackTrace.hpp | 29 + + .../stacktrace/jfrStackTraceRepository.cpp | 114 ++ + .../stacktrace/jfrStackTraceRepository.hpp | 23 + + src/hotspot/share/logging/logTag.hpp | 1 + + src/hotspot/share/opto/doCall.cpp | 4 +- + src/hotspot/share/opto/parse1.cpp | 2 +- + src/hotspot/share/runtime/flags/allFlags.hpp | 15 +- + src/hotspot/share/runtime/java.cpp | 9 + + src/hotspot/share/runtime/threads.cpp | 19 + + src/hotspot/share/utilities/growableArray.hpp | 7 + + src/hotspot/share/utilities/macros.hpp | 15 + + .../cli/common/CodeCacheCLITestCase.java | 12 +- + .../cli/common/CodeCacheOptions.java | 62 +- + .../codecache/jbolt/JBoltDumpModeTest.java | 187 +++ + .../codecache/jbolt/JBoltVMOptionsTest.java | 291 ++++ + .../jtreg/compiler/codecache/jbolt/o1.log | 2 + + .../jtreg/compiler/codecache/jbolt/o2.log | 2 + + .../jtreg/compiler/codecache/jbolt/o3.log | 4 + + .../jtreg/compiler/codecache/jbolt/o4.log | 12 + + .../runtime/cds/appcds/ClassLoaderTest.java | 2 +- + test/lib/jdk/test/whitebox/code/BlobType.java | 24 +- + 52 files changed, 4428 insertions(+), 37 deletions(-) + create mode 100644 src/hotspot/share/jbolt/jBoltCallGraph.cpp + create mode 100644 src/hotspot/share/jbolt/jBoltCallGraph.hpp + create mode 100644 src/hotspot/share/jbolt/jBoltControlThread.cpp + create mode 100644 src/hotspot/share/jbolt/jBoltControlThread.hpp + create mode 100644 src/hotspot/share/jbolt/jBoltDcmds.cpp + create mode 100644 src/hotspot/share/jbolt/jBoltDcmds.hpp + create mode 100644 src/hotspot/share/jbolt/jBoltManager.cpp + create mode 100644 src/hotspot/share/jbolt/jBoltManager.hpp + create mode 100644 src/hotspot/share/jbolt/jBoltUtils.cpp + create mode 100644 src/hotspot/share/jbolt/jBoltUtils.hpp + create mode 100644 src/hotspot/share/jbolt/jBoltUtils.inline.hpp + create mode 100644 src/hotspot/share/jbolt/jbolt_globals.hpp + create mode 100644 test/hotspot/jtreg/compiler/codecache/jbolt/JBoltDumpModeTest.java + create mode 100644 test/hotspot/jtreg/compiler/codecache/jbolt/JBoltVMOptionsTest.java + create mode 100644 test/hotspot/jtreg/compiler/codecache/jbolt/o1.log + create mode 100644 test/hotspot/jtreg/compiler/codecache/jbolt/o2.log + create mode 100644 test/hotspot/jtreg/compiler/codecache/jbolt/o3.log + create mode 100644 test/hotspot/jtreg/compiler/codecache/jbolt/o4.log + +diff --git a/make/hotspot/lib/JvmFeatures.gmk b/make/hotspot/lib/JvmFeatures.gmk +index cbe60fde2..f57406dc1 100644 +--- a/make/hotspot/lib/JvmFeatures.gmk ++++ b/make/hotspot/lib/JvmFeatures.gmk +@@ -160,6 +160,8 @@ endif + ifneq ($(call check-jvm-feature, jfr), true) + JVM_CFLAGS_FEATURES += -DINCLUDE_JFR=0 + JVM_EXCLUDE_PATTERNS += jfr ++ JVM_CFLAGS_FEATURES += -DINCLUDE_JBOLT=0 ++ JVM_EXCLUDE_PATTERNS += jbolt + JVM_EXCLUDE_FILES += compilerEvent.cpp + endif + +diff --git a/make/hotspot/lib/JvmFlags.gmk b/make/hotspot/lib/JvmFlags.gmk +index 1fadd5331..b77f1e3a6 100644 +--- a/make/hotspot/lib/JvmFlags.gmk ++++ b/make/hotspot/lib/JvmFlags.gmk +@@ -41,6 +41,12 @@ JVM_SRC_DIRS += $(call uniq, $(wildcard $(foreach d, $(JVM_SRC_ROOTS), \ + $(JVM_VARIANT_OUTPUTDIR)/gensrc + # + ++JVM_ACC_PLUGIN_DIR := $(call FindSrcDirsForLib, java.base, jplugin) ++JVM_ACC_PLUGIN_SRC := $(JVM_ACC_PLUGIN_DIR)/feature ++ifeq ($(wildcard $(JVM_ACC_PLUGIN_SRC)), $(JVM_ACC_PLUGIN_SRC)) ++ JVM_SRC_DIRS += $(JVM_ACC_PLUGIN_SRC) ++endif ++ + JVM_CFLAGS_INCLUDES += \ + $(patsubst %,-I%,$(JVM_SRC_DIRS)) \ + -I$(TOPDIR)/src/hotspot/share/precompiled \ +diff --git a/make/hotspot/lib/JvmMapfile.gmk b/make/hotspot/lib/JvmMapfile.gmk +index 2808ac2af..916aff4a3 100644 +--- a/make/hotspot/lib/JvmMapfile.gmk ++++ b/make/hotspot/lib/JvmMapfile.gmk +@@ -48,6 +48,12 @@ ifneq ($(findstring debug, $(DEBUG_LEVEL)), ) + endif + endif + ++JVM_ACC_PLUGIN_DIR := $(call FindSrcDirsForLib, java.base, jplugin) ++JVM_ACC_PLUGIN_SYMBOLS_SRC := $(JVM_ACC_PLUGIN_DIR)/make/hotspot-symbols ++ifeq ($(wildcard $(JVM_ACC_PLUGIN_SYMBOLS_SRC)), $(JVM_ACC_PLUGIN_SYMBOLS_SRC)) ++ SYMBOLS_SRC += $(JVM_ACC_PLUGIN_SYMBOLS_SRC)/symbols-plugin ++endif ++ + ################################################################################ + # Create a dynamic list of symbols from the built object files. This is highly + # platform dependent. +diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp +index ddfa97ecb..3cb529bc8 100644 +--- a/src/hotspot/os/linux/os_linux.cpp ++++ b/src/hotspot/os/linux/os_linux.cpp +@@ -4582,6 +4582,46 @@ void os::Linux::numa_init() { + } + } + ++#if INCLUDE_JBOLT ++os::Linux::jboltLog_precalc_t os::Linux::_jboltLog_precalc; ++os::Linux::jboltLog_do_t os::Linux::_jboltLog_do; ++os::Linux::jboltMerge_judge_t os::Linux::_jboltMerge_judge; ++#endif // INCLUDE_JBOLT ++ ++void os::Linux::load_plugin_library() { ++ ++#if INCLUDE_JBOLT ++ _jboltLog_precalc = CAST_TO_FN_PTR(jboltLog_precalc_t, dlsym(RTLD_DEFAULT, "JBoltLog_PreCalc")); ++ _jboltLog_do = CAST_TO_FN_PTR(jboltLog_do_t, dlsym(RTLD_DEFAULT, "JBoltLog_DO")); ++ _jboltMerge_judge = CAST_TO_FN_PTR(jboltMerge_judge_t, dlsym(RTLD_DEFAULT, "JBoltMerge_Judge")); ++#endif // INCLUDE_JBOLT ++ ++ char path[JVM_MAXPATHLEN]; ++ char ebuf[1024]; ++ void* handle = NULL; ++ if (os::dll_locate_lib(path, sizeof(path), Arguments::get_dll_dir(), "jvm21_Acc") || ++ os::dll_locate_lib(path, sizeof(path), "/usr/lib64", "jvm21_Acc")) { ++ handle = dlopen(path, RTLD_LAZY); ++ } ++ if (handle != NULL) { ++#if INCLUDE_JBOLT ++ if (_jboltLog_precalc == NULL) { ++ _jboltLog_precalc = CAST_TO_FN_PTR(jboltLog_precalc_t, dlsym(handle, "JBoltLog_PreCalc")); ++ } ++ if (_jboltLog_do == NULL) { ++ _jboltLog_do = CAST_TO_FN_PTR(jboltLog_do_t, dlsym(handle, "JBoltLog_DO")); ++ } ++ if (_jboltMerge_judge == NULL) { ++ _jboltMerge_judge = CAST_TO_FN_PTR(jboltMerge_judge_t, dlsym(handle, "JBoltMerge_Judge")); ++ } ++#endif // INCLUDE_JBOLT ++ } ++ ++ JBOLT_ONLY(log_debug(jbolt)("Plugin library for JBolt: %s %s %s", BOOL_TO_STR(_jboltLog_precalc != nullptr), ++ BOOL_TO_STR(_jboltLog_do != nullptr), ++ BOOL_TO_STR(_jboltMerge_judge != nullptr));) ++} ++ + #if defined(IA32) && !defined(ZERO) + /* + * Work-around (execute code at a high address) for broken NX emulation using CS limit, +diff --git a/src/hotspot/os/linux/os_linux.hpp b/src/hotspot/os/linux/os_linux.hpp +index 029f2aa7a..4192c90bd 100644 +--- a/src/hotspot/os/linux/os_linux.hpp ++++ b/src/hotspot/os/linux/os_linux.hpp +@@ -139,6 +139,7 @@ class os::Linux { + static const char *libc_version() { return _libc_version; } + static const char *libpthread_version() { return _libpthread_version; } + ++ static void load_plugin_library(); + static void libpthread_init(); + static void sched_getcpu_init(); + static bool libnuma_init(); +@@ -214,7 +215,14 @@ class os::Linux { + typedef void (*numa_set_bind_policy_func_t)(int policy); + typedef int (*numa_bitmask_isbitset_func_t)(struct bitmask *bmp, unsigned int n); + typedef int (*numa_distance_func_t)(int node1, int node2); +- ++#if INCLUDE_JBOLT ++ typedef void (*jboltLog_precalc_t)(unsigned int topFrameIndex, unsigned int &max_frames); ++ typedef bool (*jboltLog_do_t)(uintptr_t related_data[], address stacktrace, unsigned int i, int comp_level, address new_func, address *tempfunc); ++ typedef int (*jboltMerge_judge_t)(uintptr_t data_layout[], int candidate, address clusters, address merged, address cluster); ++ static jboltLog_precalc_t _jboltLog_precalc; ++ static jboltLog_do_t _jboltLog_do; ++ static jboltMerge_judge_t _jboltMerge_judge; ++#endif + static sched_getcpu_func_t _sched_getcpu; + static numa_node_to_cpus_func_t _numa_node_to_cpus; + static numa_node_to_cpus_v2_func_t _numa_node_to_cpus_v2; +@@ -431,6 +439,26 @@ class os::Linux { + // otherwise does nothing and returns -2. + static int malloc_info(FILE* stream); + #endif // GLIBC ++ ++#if INCLUDE_JBOLT ++ static void jboltLog_precalc(unsigned int topFrameIndex, unsigned int &max_frames) { ++ if (_jboltLog_precalc != nullptr) { ++ _jboltLog_precalc(topFrameIndex, max_frames); ++ } ++ } ++ static bool jboltLog_do(uintptr_t related_data[], address stacktrace, unsigned int i, int comp_level, address new_func, address *tempfunc) { ++ if (_jboltLog_do != nullptr) { ++ return _jboltLog_do(related_data, stacktrace, i, comp_level, new_func, tempfunc); ++ } ++ return false; ++ } ++ static int jboltMerge_judge(uintptr_t data_layout[], int candidate, address clusters, address merged, address cluster) { ++ if (_jboltMerge_judge != nullptr) { ++ return _jboltMerge_judge(data_layout, candidate, clusters, merged, cluster); ++ } ++ return -1; ++ } ++#endif // INCLUDE_JBOLT + }; + + #endif // OS_LINUX_OS_LINUX_HPP +diff --git a/src/hotspot/share/ci/ciEnv.cpp b/src/hotspot/share/ci/ciEnv.cpp +index 6112b46ac..0c48b6153 100644 +--- a/src/hotspot/share/ci/ciEnv.cpp ++++ b/src/hotspot/share/ci/ciEnv.cpp +@@ -83,6 +83,9 @@ + #ifdef COMPILER2 + #include "opto/runtime.hpp" + #endif ++#if INCLUDE_JBOLT ++#include "jbolt/jBoltManager.hpp" ++#endif + + // ciEnv + // +@@ -1117,15 +1120,35 @@ void ciEnv::register_method(ciMethod* target, + assert(offsets->value(CodeOffsets::Deopt) != -1, "must have deopt entry"); + assert(offsets->value(CodeOffsets::Exceptions) != -1, "must have exception entry"); + +- nm = nmethod::new_nmethod(method, +- compile_id(), +- entry_bci, +- offsets, +- orig_pc_offset, +- debug_info(), dependencies(), code_buffer, +- frame_words, oop_map_set, +- handler_table, inc_table, +- compiler, CompLevel(task()->comp_level())); ++#if INCLUDE_JBOLT ++ if (UseJBolt && JBoltManager::reorder_phase_is_collecting_or_reordering()) { ++ CodeBlobType code_blob_type = JBoltManager::calc_code_blob_type(method(), task(), THREAD); ++ nm = nmethod::new_nmethod(method, ++ compile_id(), ++ entry_bci, ++ offsets, ++ orig_pc_offset, ++ debug_info(), dependencies(), code_buffer, ++ frame_words, oop_map_set, ++ handler_table, inc_table, ++ compiler, CompLevel(task()->comp_level()), ++#if INCLUDE_JVMCI ++ nullptr, 0, nullptr, ++#endif ++ code_blob_type); ++ } else ++#endif // INCLUDE_JBOLT ++ { ++ nm = nmethod::new_nmethod(method, ++ compile_id(), ++ entry_bci, ++ offsets, ++ orig_pc_offset, ++ debug_info(), dependencies(), code_buffer, ++ frame_words, oop_map_set, ++ handler_table, inc_table, ++ compiler, CompLevel(task()->comp_level())); ++ } + + // Free codeBlobs + code_buffer->free_blob(); +diff --git a/src/hotspot/share/code/codeBlob.hpp b/src/hotspot/share/code/codeBlob.hpp +index e842bd88a..8392499d3 100644 +--- a/src/hotspot/share/code/codeBlob.hpp ++++ b/src/hotspot/share/code/codeBlob.hpp +@@ -44,9 +44,12 @@ class OopMapSet; + enum class CodeBlobType { + MethodNonProfiled = 0, // Execution level 1 and 4 (non-profiled) nmethods (including native nmethods) + MethodProfiled = 1, // Execution level 2 and 3 (profiled) nmethods +- NonNMethod = 2, // Non-nmethods like Buffers, Adapters and Runtime Stubs +- All = 3, // All types (No code cache segmentation) +- NumTypes = 4 // Number of CodeBlobTypes ++ MethodJBoltHot = 2, // Hot methods (determined by JBolt) of level 1 and 4 nmethods ++ MethodJBoltTmp = 3, // Temporary storage of JBolt hot methods ++ NonNMethod = 4, // Non-nmethods like Buffers, Adapters and Runtime Stubs ++ All = 5, // All types (No code cache segmentation) ++ AOT = 6, // AOT methods ++ NumTypes = 7 // Number of CodeBlobTypes + }; + + // CodeBlob - superclass for all entries in the CodeCache. +diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp +index 9b0bdc364..f63e4f266 100644 +--- a/src/hotspot/share/code/codeCache.cpp ++++ b/src/hotspot/share/code/codeCache.cpp +@@ -77,6 +77,9 @@ + #include "opto/compile.hpp" + #include "opto/node.hpp" + #endif ++#if INCLUDE_JBOLT ++#include "jbolt/jBoltManager.hpp" ++#endif // INCLUDE_JBOLT + + // Helper class for printing in CodeCache + class CodeBlob_sizes { +@@ -329,6 +332,16 @@ void CodeCache::initialize_heaps() { + profiled_size = align_down(profiled_size, alignment); + non_profiled_size = align_down(non_profiled_size, alignment); + ++#if INCLUDE_JBOLT ++ if (UseJBolt && !JBoltDumpMode) { ++ // We replace the original add-heap logic with the JBolt one. manual dump mode doesn't need that ++ JBoltManager::init_code_heaps(non_nmethod_size, profiled_size, non_profiled_size, cache_size, ps, alignment); ++ return; ++ } ++ // The following add-heap logic will not be executed if JBolt load mode is on. ++ // If the following logic is modified, remember to modify the JBolt logic accordingly. ++#endif // INCLUDE_JBOLT ++ + // Reserve one continuous chunk of memory for CodeHeaps and split it into + // parts for the individual heaps. The memory layout looks like this: + // ---------- high ----------- +@@ -384,6 +397,12 @@ ReservedCodeSpace CodeCache::reserve_heap_memory(size_t size, size_t rs_ps) { + + // Heaps available for allocation + bool CodeCache::heap_available(CodeBlobType code_blob_type) { ++ if (code_blob_type == CodeBlobType::MethodJBoltHot) { ++ return JBOLT_ONLY(UseJBolt && !JBoltDumpMode) NOT_JBOLT(false); ++ } else if (code_blob_type == CodeBlobType::MethodJBoltTmp) { ++ return JBOLT_ONLY(UseJBolt && !JBoltDumpMode) NOT_JBOLT(false); ++ } ++ + if (!SegmentedCodeCache) { + // No segmentation: use a single code heap + return (code_blob_type == CodeBlobType::All); +@@ -411,6 +430,12 @@ const char* CodeCache::get_code_heap_flag_name(CodeBlobType code_blob_type) { + case CodeBlobType::MethodProfiled: + return "ProfiledCodeHeapSize"; + break; ++ case CodeBlobType::MethodJBoltHot: ++ return "JBoltHotCodeHeapSize"; ++ break; ++ case CodeBlobType::MethodJBoltTmp: ++ return "JBoltTmpCodeHeapSize"; ++ break; + default: + ShouldNotReachHere(); + return nullptr; +@@ -558,6 +583,14 @@ CodeBlob* CodeCache::allocate(int size, CodeBlobType code_blob_type, bool handle + type = CodeBlobType::MethodNonProfiled; + } + break; ++#if INCLUDE_JBOLT ++ case CodeBlobType::MethodJBoltHot: ++ type = CodeBlobType::MethodNonProfiled; ++ break; ++ case CodeBlobType::MethodJBoltTmp: ++ type = CodeBlobType::MethodNonProfiled; ++ break; ++#endif // INCLUDE_JBOLT + default: + break; + } +diff --git a/src/hotspot/share/code/codeCache.hpp b/src/hotspot/share/code/codeCache.hpp +index fbcaefd2a..df38664e7 100644 +--- a/src/hotspot/share/code/codeCache.hpp ++++ b/src/hotspot/share/code/codeCache.hpp +@@ -48,6 +48,10 @@ + // executed at level 2 or 3 + // - Non-Profiled nmethods: nmethods that are not profiled, i.e., those + // executed at level 1 or 4 and native methods ++// - JBolt nmethods: sorted non-profiled nmethods that are judged to be hot ++// by JBolt ++// - JBolt tmp nmethods: non-profiled nmethods that are judged to be hot by ++// JBolt but not sorted yet + // - All: Used for code of all types if code cache segmentation is disabled. + // + // In the rare case of the non-nmethod code heap getting full, non-nmethod code +@@ -87,6 +91,10 @@ class CodeCache : AllStatic { + friend class WhiteBox; + friend class CodeCacheLoader; + friend class ShenandoahParallelCodeHeapIterator; ++#if INCLUDE_JBOLT ++ friend class JBoltManager; ++#endif // INCLUDE_JBOLT ++ + private: + // CodeHeaps of the cache + static GrowableArray* _heaps; +@@ -266,12 +274,16 @@ class CodeCache : AllStatic { + } + + static bool code_blob_type_accepts_compiled(CodeBlobType code_blob_type) { +- bool result = code_blob_type == CodeBlobType::All || code_blob_type <= CodeBlobType::MethodProfiled; ++ // Modified `type <= CodeBlobType::MethodProfiled` to `type < CodeBlobType::NonNMethod` ++ // after adding the JBolt heap. The two logics are still equivalent even without JBolt. ++ bool result = code_blob_type == CodeBlobType::All || code_blob_type < CodeBlobType::NonNMethod; + return result; + } + + static bool code_blob_type_accepts_nmethod(CodeBlobType type) { +- return type == CodeBlobType::All || type <= CodeBlobType::MethodProfiled; ++ // Modified `type <= CodeBlobType::MethodProfiled` to `type < CodeBlobType::NonNMethod` ++ // after adding the JBolt heap. The two logics are still equivalent even without JBolt. ++ return type == CodeBlobType::All || type < CodeBlobType::NonNMethod; + } + + static bool code_blob_type_accepts_allocable(CodeBlobType type) { +diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp +index 500ddbc2f..24b5139cf 100644 +--- a/src/hotspot/share/code/nmethod.cpp ++++ b/src/hotspot/share/code/nmethod.cpp +@@ -85,6 +85,9 @@ + #if INCLUDE_JVMCI + #include "jvmci/jvmciRuntime.hpp" + #endif ++#if INCLUDE_JBOLT ++#include "jbolt/jBoltManager.hpp" ++#endif + + #ifdef DTRACE_ENABLED + +@@ -554,6 +557,9 @@ nmethod* nmethod::new_nmethod(const methodHandle& method, + int speculations_len, + JVMCINMethodData* jvmci_data + #endif ++#if INCLUDE_JBOLT ++ , CodeBlobType code_blob_type // for jbolt ++#endif // INCLUDE_JBOLT + ) + { + assert(debug_info->oop_recorder() == code_buffer->oop_recorder(), "shared OR"); +@@ -577,7 +583,11 @@ nmethod* nmethod::new_nmethod(const methodHandle& method, + { + MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + ++#if INCLUDE_JBOLT ++ nm = new (nmethod_size, comp_level, code_blob_type) ++#else // INCLUDE_JBOLT + nm = new (nmethod_size, comp_level) ++#endif // INCLUDE_JBOLT + nmethod(method(), compiler->type(), nmethod_size, compile_id, entry_bci, offsets, + orig_pc_offset, debug_info, dependencies, code_buffer, frame_size, + oop_maps, +@@ -761,6 +771,15 @@ void* nmethod::operator new(size_t size, int nmethod_size, bool allow_NonNMethod + return CodeCache::allocate(nmethod_size, CodeBlobType::NonNMethod); + } + ++#if INCLUDE_JBOLT ++void* nmethod::operator new(size_t size, int nmethod_size, int comp_level, CodeBlobType code_blob_type) throw () { ++ if (code_blob_type < CodeBlobType::All) { ++ return CodeCache::allocate(nmethod_size, code_blob_type); ++ } ++ return CodeCache::allocate(nmethod_size, CodeCache::get_code_blob_type(comp_level)); ++} ++#endif // INCLUDE_JBOLT ++ + nmethod::nmethod( + Method* method, + CompilerType type, +diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp +index 03b8210c3..ddb8a1274 100644 +--- a/src/hotspot/share/code/nmethod.hpp ++++ b/src/hotspot/share/code/nmethod.hpp +@@ -307,6 +307,11 @@ class nmethod : public CompiledMethod { + // findable by nmethod iterators! In particular, they must not contain oops! + void* operator new(size_t size, int nmethod_size, bool allow_NonNMethod_space) throw(); + ++#if INCLUDE_JBOLT ++ // For JBolt. So the code can be allocated in code segments defined by JBolt. ++ void* operator new(size_t size, int nmethod_size, int comp_level, CodeBlobType code_blob_type) throw (); ++#endif // INCLUDE_JBOLT ++ + const char* reloc_string_for(u_char* begin, u_char* end); + + bool try_transition(int new_state); +@@ -349,6 +354,9 @@ class nmethod : public CompiledMethod { + int speculations_len = 0, + JVMCINMethodData* jvmci_data = nullptr + #endif ++#if INCLUDE_JBOLT ++ , CodeBlobType code_blob_type = CodeBlobType::All // for jbolt ++#endif // INCLUDE_JBOLT + ); + + // Only used for unit tests. +diff --git a/src/hotspot/share/compiler/compileBroker.cpp b/src/hotspot/share/compiler/compileBroker.cpp +index 9c1bf5393..3a59a17eb 100644 +--- a/src/hotspot/share/compiler/compileBroker.cpp ++++ b/src/hotspot/share/compiler/compileBroker.cpp +@@ -83,6 +83,9 @@ + #include "jvmci/jvmciEnv.hpp" + #include "jvmci/jvmciRuntime.hpp" + #endif ++#if INCLUDE_JBOLT ++#include "jbolt/jBoltManager.hpp" ++#endif // INCLUDE_JBOLT + + #ifdef DTRACE_ENABLED + +@@ -1959,6 +1962,12 @@ void CompileBroker::compiler_thread_loop() { + task->set_failure_reason("breakpoints are present"); + } + ++#if INCLUDE_JBOLT ++ if (UseJBolt && JBoltLoadMode) { ++ JBoltManager::check_start_reordering(thread); ++ } ++#endif // INCLUDE_JBOLT ++ + if (UseDynamicNumberOfCompilerThreads) { + possibly_add_compiler_threads(thread); + assert(!thread->has_pending_exception(), "should have been handled"); +diff --git a/src/hotspot/share/compiler/compileBroker.hpp b/src/hotspot/share/compiler/compileBroker.hpp +index b7f09259f..a2143ad93 100644 +--- a/src/hotspot/share/compiler/compileBroker.hpp ++++ b/src/hotspot/share/compiler/compileBroker.hpp +@@ -140,6 +140,9 @@ public: + class CompileBroker: AllStatic { + friend class Threads; + friend class CompileTaskWrapper; ++#if INCLUDE_JBOLT ++ friend class JBoltManager; ++#endif // INCLUDE_JBOLT + + public: + enum { +diff --git a/src/hotspot/share/compiler/compileTask.hpp b/src/hotspot/share/compiler/compileTask.hpp +index f5b628232..aff2df38a 100644 +--- a/src/hotspot/share/compiler/compileTask.hpp ++++ b/src/hotspot/share/compiler/compileTask.hpp +@@ -56,6 +56,9 @@ class CompileTask : public CHeapObj { + Reason_Whitebox, // Whitebox API + Reason_MustBeCompiled, // Used for -Xcomp or AlwaysCompileLoopMethods (see CompilationPolicy::must_be_compiled()) + Reason_Bootstrap, // JVMCI bootstrap ++#if INCLUDE_JBOLT ++ Reason_Reorder, // JBolt reorder ++#endif + Reason_Count + }; + +@@ -69,6 +72,9 @@ class CompileTask : public CHeapObj { + "whitebox", + "must_be_compiled", + "bootstrap" ++#if INCLUDE_JBOLT ++ , "reorder" ++#endif + }; + return reason_names[compile_reason]; + } +@@ -230,6 +236,12 @@ public: + print_inlining_inner(tty, method, inline_level, bci, msg); + } + static void print_inlining_ul(ciMethod* method, int inline_level, int bci, const char* msg = nullptr); ++ ++#if INCLUDE_JBOLT ++ CompileReason compile_reason() { return _compile_reason; } ++ int hot_count() { return _hot_count; } ++ const char* failure_reason() { return _failure_reason; } ++#endif // INCLUDE_JBOLT + }; + + #endif // SHARE_COMPILER_COMPILETASK_HPP +diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +index 9b6b22a29..67667a08c 100644 +--- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp ++++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +@@ -116,6 +116,9 @@ + #include "utilities/bitMap.inline.hpp" + #include "utilities/globalDefinitions.hpp" + #include "utilities/stack.inline.hpp" ++#if INCLUDE_JBOLT ++#include "jbolt/jBoltManager.hpp" ++#endif // INCLUDE_JBOLT + + size_t G1CollectedHeap::_humongous_object_threshold_in_words = 0; + +@@ -1645,6 +1648,11 @@ size_t G1CollectedHeap::recalculate_used() const { + } + + bool G1CollectedHeap::is_user_requested_concurrent_full_gc(GCCause::Cause cause) { ++#if INCLUDE_JBOLT ++ if (UseJBolt && cause == GCCause::_java_lang_system_gc && JBoltManager::gc_should_sweep_code_heaps_now()) { ++ return true; ++ } ++#endif // INCLUDE_JBOLT + return GCCause::is_user_requested_gc(cause) && ExplicitGCInvokesConcurrent; + } + +diff --git a/src/hotspot/share/jbolt/jBoltCallGraph.cpp b/src/hotspot/share/jbolt/jBoltCallGraph.cpp +new file mode 100644 +index 000000000..c171215ad +--- /dev/null ++++ b/src/hotspot/share/jbolt/jBoltCallGraph.cpp +@@ -0,0 +1,480 @@ ++/* ++ * Copyright (c) 2020, 2024, 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 "jbolt/jBoltCallGraph.hpp" ++#include "jfr/utilities/jfrAllocation.hpp" ++#include "jfr/support/jfrMethodLookup.hpp" ++#include "logging/log.hpp" ++#include "logging/logStream.hpp" ++#include "oops/method.inline.hpp" ++#include "runtime/os.hpp" ++#include "utilities/defaultStream.hpp" ++#ifdef LINUX ++#include "os_linux.hpp" ++#endif ++ ++#define PAGE_SIZE os::vm_page_size() ++ ++static GrowableArray* _clusters = NULL; ++static GrowableArray* _calls = NULL; ++static GrowableArray* _funcs = NULL; ++ ++// (JBolt hfsort optional)sort final clusters by density ++static const bool _jbolt_density_sort = false; ++// (JBolt hfsort optional)freeze merging while exceeding pagesize ++static const bool _jbolt_merge_frozen = false; ++ ++void JBoltCallGraph::initialize() { ++ ::_clusters = JBoltCallGraph::callgraph_instance().callgraph_clusters(); ++ ::_calls = JBoltCallGraph::callgraph_instance().callgraph_calls(); ++ ::_funcs = JBoltCallGraph::callgraph_instance().callgraph_funcs(); ++} ++ ++void JBoltCallGraph::deinitialize() { ++ ::_clusters = NULL; ++ ::_calls = NULL; ++ ::_funcs = NULL; ++} ++ ++int JBoltCallGraph::clear_instance() { ++ delete _clusters; ++ delete _calls; ++ delete _funcs; ++ ++ // Reinit default cluster start id ++ _init_cluster_id = 0; ++ ++ // Re-allocate ++ _clusters = create_growable_array(); ++ _calls = create_growable_array(); ++ _funcs = create_growable_array(); ++ ++ // Re-initialize ++ initialize(); ++ ++ return 0; ++} ++ ++static GrowableArray* clusters_copy() { ++ GrowableArray* copy = create_growable_array(_clusters->length()); ++ copy->appendAll(_clusters); ++ return copy; ++} ++ ++static GrowableArray* funcs_copy() { ++ GrowableArray* copy = create_growable_array(_funcs->length()); ++ copy->appendAll(_funcs); ++ return copy; ++} ++ ++static int find_func_index(const JBoltFunc* func) { ++ for (int i = 0; i < _funcs->length(); ++i) { ++ JBoltFunc& existing = _funcs->at(i); ++ if (existing == (*func)) { ++ return i; ++ } ++ } ++ return -1; ++} ++ ++// Searching for a cluster with corresponding func or creating a new one if doesn't exist ++static JBoltCluster* find_cluster(JBoltFunc* func) { ++ for (int i = 0; i < _clusters->length(); ++i) { ++ JBoltCluster& cluster = _clusters->at(i); ++ int index = cluster.func_indexes()->at(0); ++ if (_funcs->at(index) == (*func)) { ++ return &cluster; ++ } ++ } ++ _funcs->append(*func); ++ _clusters->append(JBoltCluster(*func)); ++ JBoltCluster& cluster = _clusters->at(_clusters->length() - 1); ++ _funcs->at(_funcs->length() - 1).set_cluster_id(cluster.id()); ++ return &cluster; ++} ++ ++// Creating a new call in graph or updating the weight if exists ++static void add_call_to_calls(GrowableArray* calls, const JBoltCall* call) { ++ for (int i = 0; i < calls->length(); ++i) { ++ JBoltCall& existing_call = calls->at(i); ++ if (existing_call == *call) { ++ if (existing_call.stacktrace_id() == call->stacktrace_id()) { ++ assert(call->call_count() >= existing_call.call_count(), "invariant"); ++ existing_call.callee().add_heat(call->call_count() - existing_call.call_count()); ++ existing_call.set_call_count(call->call_count()); ++ } ++ else { ++ existing_call.callee().add_heat(call->call_count()); ++ existing_call.set_call_count(existing_call.call_count() + call->call_count()); ++ } ++ return; ++ } ++ } ++ ++ calls->append(*call); ++ call->callee().add_heat(call->call_count()); ++ call->callee().append_call_index(calls->length() - 1); ++} ++ ++// Getting final funcs order from an array of processed clusters ++static GrowableArray* clusters_to_funcs_order(GrowableArray* clusters) { ++ log_debug(jbolt)( "sorted clusters:\n"); ++ for (int i = 0; i < clusters->length(); ++i) { ++ log_debug(jbolt)( "cluster id: %d heats: %ld size: %dB density: %f\n", clusters->at(i).id(), clusters->at(i).heats(), clusters->at(i).size(), clusters->at(i).density()); ++ for (int j = 0; j < clusters->at(i).get_funcs_count(); ++j) { ++ JBoltFunc& func = _funcs->at(clusters->at(i).func_indexes()->at(j)); ++ const Method* const method = JfrMethodLookup::lookup(func.klass(), func.method_id()); ++ if (method != NULL) { ++ log_debug(jbolt)( "%d: method signature:%s heat: %ld size: %dB\n", ++ j, method->external_name(), func.heat(), func.size()); ++ } ++ } ++ } ++ ++ GrowableArray* order = create_growable_array(_funcs->length()); ++ // used to seperator distinct cluster, klass = NULL ++ JBoltFunc seperator_func; ++ order->append(seperator_func); ++ for (int i = 0; i < clusters->length(); ++i) { ++ JBoltCluster& cluster = clusters->at(i); ++ GrowableArray* func_indexes = cluster.func_indexes(); ++ ++ for (int j = 0; j < func_indexes->length(); ++j) { ++ int index = func_indexes->at(j); ++ order->append(_funcs->at(index)); ++ } ++ ++ order->append(seperator_func); ++ } ++ return order; ++} ++ ++// Comparing function needed to sort an array of funcs by their weights (in decreasing order) ++static int func_comparator(JBoltFunc* func1, JBoltFunc* func2) { ++ return func1->heat() < func2->heat(); ++} ++ ++// Comparing cluster needed to sort an array of clusters by their densities (in decreasing order) ++static int cluster_comparator(JBoltCluster* cluster1, JBoltCluster* cluster2) { ++ return _jbolt_density_sort ? (cluster1->density() < cluster2->density()) : (cluster1->heats() < cluster2 -> heats()); ++} ++ ++// Comparing call indexes needed to sort an array of call indexes by their call counts (in decreasing order) ++static int func_call_indexes_comparator(int* index1, int* index2) { ++ return _calls->at(*index1).call_count() < _calls->at(*index2).call_count(); ++} ++ ++JBoltCallGraph& JBoltCallGraph::callgraph_instance() { ++ static JBoltCallGraph _call_graph; ++ return _call_graph; ++} ++ ++void JBoltCallGraph::add_func(JBoltFunc* func) { ++ if (!(UseJBolt && JBoltManager::reorder_phase_is_profiling_or_waiting())) return; ++ JBoltCluster* cluster = find_cluster(func); ++ assert(cluster != NULL, "invariant"); ++} ++ ++void JBoltCallGraph::add_call(JBoltCall* call) { ++ if (!(UseJBolt && JBoltManager::reorder_phase_is_profiling_or_waiting())) return; ++ add_call_to_calls(_calls, call); ++} ++ ++uintptr_t data_layout_jbolt[] = { ++ (uintptr_t)in_bytes(JBoltCluster::id_offset()), ++ (uintptr_t)in_bytes(JBoltCluster::heats_offset()), ++ (uintptr_t)in_bytes(JBoltCluster::frozen_offset()), ++ (uintptr_t)in_bytes(JBoltCluster::size_offset()), ++ (uintptr_t)in_bytes(JBoltCluster::density_offset()), ++ (uintptr_t)in_bytes(JBoltCluster::func_indexes_offset()), ++ ++ (uintptr_t)in_bytes(GrowableArrayView
::data_offset()), ++ ++ (uintptr_t)JBoltCluster::find_cluster_by_id, ++ (uintptr_t)_jbolt_merge_frozen ++}; ++ ++static void deal_with_each_func(GrowableArray* clusters, GrowableArray* funcs, GrowableArray* merged) { ++ for (int i = 0; i < funcs->length(); ++i) { ++ JBoltFunc& func = funcs->at(i); ++ ++ JBoltCluster* cluster = JBoltCluster::find_cluster_by_id(clusters, func.cluster_id()); ++ ++ // for cluster size larger than page size, should be frozen and don't merge with any cluster ++ if (_jbolt_merge_frozen && cluster->frozen()) continue; ++ ++ // find best predecessor ++ func.call_indexes()->sort(&func_call_indexes_comparator); ++ ++ int bestPred = -1; ++ ++ for (int j = 0; j < func.call_indexes()->length(); ++j) { ++ const JBoltCall& call = _calls->at(func.call_indexes()->at(j)); ++ ++ bestPred = os::Linux::jboltMerge_judge(data_layout_jbolt, call.caller().cluster_id(), (address)clusters, (address)merged, (address)cluster); ++ ++ if (bestPred == -1) continue; ++ ++ break; ++ } ++ ++ // not merge -- no suitable caller nodes ++ if (bestPred == -1) { ++ continue; ++ } ++ ++ JBoltCluster* predCluster = JBoltCluster::find_cluster_by_id(clusters, bestPred); ++ ++ // merge callee cluster to caller cluster ++ for (int j = 0; j < cluster->func_indexes()->length(); ++j) { ++ int index = cluster->func_indexes()->at(j); ++ predCluster->append_func_index(index); ++ } ++ predCluster->add_heat(cluster->heats()); ++ predCluster->add_size(cluster->size()); ++ predCluster->update_density(); ++ merged->at(cluster->id()) = bestPred; ++ cluster->clear(); ++ } ++} ++ ++// Every node is a cluster with funcs ++// Initially each cluster has only one func inside ++GrowableArray* JBoltCallGraph::hfsort() { ++ if (!(UseJBolt && (JBoltDumpMode || JBoltManager::auto_mode()))) return NULL; ++ log_debug(jbolt)( "hfsort begin...\n"); ++ // Copies are needed for saving initial graph in memory ++ GrowableArray* clusters = clusters_copy(); ++ GrowableArray* funcs = funcs_copy(); ++ ++ // store a map for finding head of merge chain ++ GrowableArray* merged = create_growable_array(clusters->length()); ++ for (int i = 0; i < clusters->length(); ++i) { ++ merged->append(-1); ++ } ++ ++ // sorted by func(initially a node) weight(now just as 'heat') ++ funcs->sort(&func_comparator); ++ ++ // Process each function, and consider merging its cluster with the ++ // one containing its most likely predecessor. ++ deal_with_each_func(clusters, funcs, merged); ++ ++ // the set of clusters that are left ++ GrowableArray* sortedClusters = create_growable_array(); ++ for (int i = 0; i < clusters->length(); ++i) { ++ if (clusters->at(i).id() != -1) { ++ sortedClusters->append(clusters->at(i)); ++ } ++ } ++ ++ sortedClusters->sort(&cluster_comparator); ++ ++ GrowableArray* order = clusters_to_funcs_order(sortedClusters); ++ ++ delete clusters; ++ delete funcs; ++ delete merged; ++ delete sortedClusters; ++ log_debug(jbolt)( "hfsort over...\n"); ++ ++ return order; ++} ++ ++JBoltFunc::JBoltFunc() : ++ _klass(NULL), ++ _method_id(0), ++ _heat(0), ++ _size(0), ++ _cluster_id(-1), ++ _method_key(), ++ _call_indexes(create_growable_array()) {} ++ ++JBoltFunc::JBoltFunc(const JBoltFunc& func) : ++ _klass(func._klass), ++ _method_id(func._method_id), ++ _heat(func._heat), ++ _size(func._size), ++ _cluster_id(func._cluster_id), ++ _method_key(func._method_key), ++ _call_indexes(create_growable_array(func.get_calls_count())) { ++ GrowableArray* array = func.call_indexes(); ++ _call_indexes->appendAll(array); ++ } ++ ++JBoltFunc::JBoltFunc(const InstanceKlass* klass, traceid method_id, int size, JBoltMethodKey method_key) : ++ _klass(klass), ++ _method_id(method_id), ++ _heat(0), ++ _size(size), ++ _cluster_id(-1), ++ _method_key(method_key), ++ _call_indexes(create_growable_array()) { ++ // not new_symbol, need to inc reference cnt ++ _method_key.klass()->increment_refcount(); ++ _method_key.name()->increment_refcount(); ++ _method_key.sig()->increment_refcount(); ++ } ++ ++void JBoltFunc::add_heat(int64_t heat) { ++ _heat += heat; ++ assert(_cluster_id != -1, "invariant"); ++ _clusters->at(_cluster_id).add_heat(heat); ++ _clusters->at(_cluster_id).update_density(); ++} ++ ++void JBoltFunc::set_heat(int64_t heat) { ++ int64_t diff = heat - _heat; ++ _heat = heat; ++ assert(_cluster_id != -1, "invariant"); ++ _clusters->at(_cluster_id).add_heat(diff); ++ _clusters->at(_cluster_id).update_density(); ++} ++ ++void JBoltFunc::set_cluster_id(int cluster_id) { _cluster_id = cluster_id; } ++ ++void JBoltFunc::append_call_index(int index) { _call_indexes->append(index); } ++ ++JBoltFunc* JBoltFunc::constructor(const InstanceKlass* klass, traceid method_id, int size, JBoltMethodKey method_key) { ++ JBoltFunc *ret = new JBoltFunc(klass, method_id, size, method_key); ++ return ret; ++} ++ ++JBoltFunc* JBoltFunc::copy_constructor(const JBoltFunc* func) { ++ JBoltFunc *ret = new JBoltFunc(*func); ++ return ret; ++} ++ ++JBoltCluster::JBoltCluster() : ++ _id(-1), ++ _heats(0), ++ _frozen(false), ++ _size(0), ++ _density(0.0), ++ _func_indexes(create_growable_array()) {} ++ ++JBoltCluster::JBoltCluster(const JBoltFunc& func) : ++ _id(_init_cluster_id++), ++ _heats(func.heat()), ++ _frozen(false), ++ _size(func.size()), ++ _density(0.0), ++ _func_indexes(create_growable_array()) { ++ if (_size >= (int) PAGE_SIZE) ++ freeze(); ++ ++ update_density(); ++ ++ int func_idx = find_func_index(&func); ++ assert(func_idx != -1, "invariant"); ++ _func_indexes->append(func_idx); ++ } ++ ++JBoltCluster::JBoltCluster(const JBoltCluster& cluster) : ++ _id(cluster.id()), ++ _heats(cluster.heats()), ++ _frozen(cluster.frozen()), ++ _size(cluster.size()), ++ _density(cluster.density()), ++ _func_indexes(create_growable_array(cluster.get_funcs_count())) { ++ GrowableArray* array = cluster.func_indexes(); ++ _func_indexes->appendAll(array); ++ } ++ ++void JBoltCluster::add_heat(int64_t heat) { _heats += heat; } ++ ++void JBoltCluster::freeze() { _frozen = true; } ++ ++void JBoltCluster::add_size(int size) { _size += size; } ++ ++void JBoltCluster::update_density() { _density = (double)_heats / (double)_size; } ++ ++void JBoltCluster::append_func_index(int index) { _func_indexes->append(index); } ++ ++void JBoltCluster::clear() { ++ _id = -1; ++ _heats = 0; ++ _frozen = false; ++ _size = 0; ++ _density = 0.0; ++ _func_indexes->clear(); ++} ++ ++// Searching for a cluster by its id ++JBoltCluster* JBoltCluster::find_cluster_by_id(GrowableArray* clusters, u4 id) { ++ if (id >= (u4)clusters->length()) return NULL; ++ ++ return &(clusters->at(id)); ++} ++ ++JBoltCluster* JBoltCluster::constructor(const JBoltFunc* func) { ++ JBoltCluster *ret = new JBoltCluster(*func); ++ return ret; ++} ++ ++JBoltCluster* JBoltCluster::copy_constructor(const JBoltCluster* cluster) { ++ JBoltCluster *ret = new JBoltCluster(*cluster); ++ return ret; ++} ++ ++JBoltCall::JBoltCall() : ++ _caller_index(-1), ++ _callee_index(-1), ++ _call_count(0), ++ _stacktrace_id(0) {} ++ ++JBoltCall::JBoltCall(const JBoltCall& call) : ++ _caller_index(call._caller_index), ++ _callee_index(call._callee_index), ++ _call_count(call._call_count), ++ _stacktrace_id(call._stacktrace_id) {} ++ ++JBoltCall::JBoltCall(const JBoltFunc& caller_func, const JBoltFunc& callee_func, u4 call_count, traceid stacktrace_id) : ++ _call_count(call_count), ++ _stacktrace_id(stacktrace_id) { ++ _caller_index = find_func_index(&caller_func); ++ _callee_index = find_func_index(&callee_func); ++ assert(_caller_index != -1, "invariant"); ++ assert(_callee_index != -1, "invariant"); ++ } ++ ++JBoltFunc& JBoltCall::caller() const { return _funcs->at(_caller_index); } ++ ++JBoltFunc& JBoltCall::callee() const { return _funcs->at(_callee_index); } ++ ++void JBoltCall::set_caller_index(int index) { _caller_index = index; } ++ ++void JBoltCall::set_callee_index(int index) { _callee_index = index; } ++ ++void JBoltCall::set_call_count(u4 call_count) { _call_count = call_count; } ++ ++JBoltCall* JBoltCall::constructor(const JBoltFunc* caller_func, const JBoltFunc* callee_func, u4 call_count, traceid stacktrace_id) { ++ JBoltCall *ret = new JBoltCall(*caller_func, *callee_func, call_count, stacktrace_id); ++ return ret; ++} ++ ++JBoltCall* JBoltCall::copy_constructor(const JBoltCall* call) { ++ JBoltCall *ret = new JBoltCall(*call); ++ return ret; ++} +\ No newline at end of file +diff --git a/src/hotspot/share/jbolt/jBoltCallGraph.hpp b/src/hotspot/share/jbolt/jBoltCallGraph.hpp +new file mode 100644 +index 000000000..93115f3ce +--- /dev/null ++++ b/src/hotspot/share/jbolt/jBoltCallGraph.hpp +@@ -0,0 +1,277 @@ ++/* ++ * Copyright (c) 2020, 2024, 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_JBOLT_JBOLTCALLGRAPH_HPP ++#define SHARE_JBOLT_JBOLTCALLGRAPH_HPP ++ ++#include "jbolt/jbolt_globals.hpp" ++#include "jbolt/jBoltManager.hpp" ++#include "jfr/utilities/jfrTypes.hpp" ++#include "utilities/growableArray.hpp" ++ ++class JBoltFunc; ++class JBoltCall; ++class JBoltCluster; ++ ++template ++static GrowableArray* create_growable_array(int size = 1) { ++ GrowableArray* array = new (mtTracing) GrowableArray(size, mtTracing); ++ assert(array != NULL, "invariant"); ++ return array; ++} ++ ++// initial cluster id ++static u4 _init_cluster_id = 0; ++ ++class JBoltCallGraph : public CHeapObj { ++ private: ++ GrowableArray* _clusters = NULL; ++ GrowableArray* _calls = NULL; ++ GrowableArray* _funcs = NULL; ++ ++ JBoltCallGraph() { ++ _clusters = create_growable_array(); ++ _calls = create_growable_array(); ++ _funcs = create_growable_array(); ++ } ++ ++ JBoltCallGraph(const JBoltCallGraph &) = delete; ++ JBoltCallGraph(const JBoltCallGraph &&) = delete; ++ ++ // for constructing CG ++ void add_func(JBoltFunc* func); // Node ++ void add_call(JBoltCall* call); // Edge ++ ++ public: ++ static JBoltCallGraph& callgraph_instance(); ++ // these two funcs initialize and deinitialize homonymous static array pointers in global ++ static void initialize(); ++ static void deinitialize(); ++ ++ GrowableArray* callgraph_clusters() { return _clusters; } ++ GrowableArray* callgraph_calls() { return _calls; } ++ GrowableArray* callgraph_funcs() { return _funcs; } ++ ++ static void static_add_func(JBoltFunc* func) { callgraph_instance().add_func(func); } ++ static void static_add_call(JBoltCall* call) { callgraph_instance().add_call(call); } ++ ++ // for dealing with CG ++ GrowableArray* hfsort(); ++ ++ int clear_instance(); ++ ++ virtual ~JBoltCallGraph() { ++ delete _clusters; ++ delete _calls; ++ delete _funcs; ++ ++ _clusters = NULL; ++ _calls = NULL; ++ _funcs = NULL; ++ } ++}; ++ ++class JBoltFunc : public CHeapObj { ++ private: ++ const InstanceKlass* _klass; ++ traceid _method_id; ++ int64_t _heat; ++ int _size; ++ int _cluster_id; ++ JBoltMethodKey _method_key; ++ GrowableArray* _call_indexes; ++ ++ public: ++ JBoltFunc(); ++ JBoltFunc(const JBoltFunc& func); ++ JBoltFunc(const InstanceKlass* klass, traceid method_id, int size, JBoltMethodKey method_key); ++ ++ virtual ~JBoltFunc() { ++ delete _call_indexes; ++ } ++ ++ bool operator==(const JBoltFunc& func) const { return (_klass == func._klass && _method_id == func._method_id) || (_method_key.equals(func._method_key)); } ++ bool operator!=(const JBoltFunc& func) const { return (_klass != func._klass || _method_id != func._method_id) && !(_method_key.equals(func._method_key)); } ++ ++ JBoltFunc& operator=(const JBoltFunc& func) { ++ _klass = func._klass; ++ _method_id = func._method_id; ++ _heat = func._heat; ++ _size = func._size; ++ _cluster_id = func._cluster_id; ++ _method_key = func._method_key; ++ if (_call_indexes != nullptr) { ++ delete _call_indexes; ++ } ++ _call_indexes = create_growable_array(func.get_calls_count()); ++ _call_indexes->appendAll(func.call_indexes()); ++ ++ return *this; ++ } ++ ++ const InstanceKlass* klass() const { return _klass; } ++ const traceid method_id() const { return _method_id; } ++ const int64_t heat() const { return _heat; } ++ const int size() const { return _size; } ++ const int cluster_id() const { return _cluster_id; } ++ JBoltMethodKey method_key() const { return _method_key; } ++ GrowableArray* call_indexes() const { return _call_indexes; } ++ int get_calls_count() const { return _call_indexes->length(); } ++ ++ void add_heat(int64_t heat); ++ void set_heat(int64_t heat); ++ void set_cluster_id(int cluster_id); ++ void append_call_index(int index); ++ ++ static ByteSize klass_offset() { return byte_offset_of(JBoltFunc, _klass); } ++ static ByteSize method_id_offset() { return byte_offset_of(JBoltFunc, _method_id); } ++ static ByteSize heat_offset() { return byte_offset_of(JBoltFunc, _heat); } ++ static ByteSize size_offset() { return byte_offset_of(JBoltFunc, _size); } ++ static ByteSize cluster_id_offset() { return byte_offset_of(JBoltFunc, _cluster_id); } ++ static ByteSize call_indexes_offset() { return byte_offset_of(JBoltFunc, _call_indexes); } ++ ++ static JBoltFunc* constructor(const InstanceKlass* klass, traceid method_id, int size, JBoltMethodKey method_key); ++ static JBoltFunc* copy_constructor(const JBoltFunc* func); ++}; ++ ++class JBoltCluster : public CHeapObj { ++ private: ++ int _id; ++ int64_t _heats; ++ bool _frozen; ++ int _size; ++ double _density; ++ GrowableArray* _func_indexes; ++ ++ public: ++ JBoltCluster(); ++ JBoltCluster(const JBoltFunc& func); ++ JBoltCluster(const JBoltCluster& cluster); ++ ++ bool operator==(const JBoltCluster& cluster) const { ++ if (_id != cluster.id()) return false; ++ ++ int count = get_funcs_count(); ++ if (count != cluster.get_funcs_count()) ++ return false; ++ ++ for (int i = 0; i < count; ++i) { ++ if (_func_indexes->at(i) != cluster._func_indexes->at(i)) { ++ return false; ++ } ++ } ++ ++ return true; ++ } ++ ++ JBoltCluster& operator=(const JBoltCluster& cluster) { ++ _id = cluster.id(); ++ _heats = cluster.heats(); ++ _frozen = cluster.frozen(); ++ _size = cluster.size(); ++ _density = cluster.density(); ++ if (_func_indexes != nullptr) { ++ delete _func_indexes; ++ } ++ _func_indexes = create_growable_array(cluster.get_funcs_count()); ++ _func_indexes->appendAll(cluster.func_indexes()); ++ return *this; ++ } ++ ++ virtual ~JBoltCluster() { delete _func_indexes; } ++ ++ int id() const { return _id; } ++ int64_t heats() const { return _heats; } ++ bool frozen() const { return _frozen; } ++ int size() const { return _size; } ++ double density() const { return _density; } ++ GrowableArray* func_indexes() const { return _func_indexes; } ++ int get_funcs_count() const { return _func_indexes->length(); } ++ ++ void add_heat(int64_t heat); ++ void freeze(); ++ void add_size(int size); ++ void update_density(); ++ void append_func_index(int index); ++ void clear(); ++ ++ static JBoltCluster* find_cluster_by_id(GrowableArray* clusters, u4 id); ++ ++ static ByteSize id_offset() { return byte_offset_of(JBoltCluster, _id); } ++ static ByteSize heats_offset() { return byte_offset_of(JBoltCluster, _heats); } ++ static ByteSize frozen_offset() { return byte_offset_of(JBoltCluster, _frozen); } ++ static ByteSize size_offset() { return byte_offset_of(JBoltCluster, _size); } ++ static ByteSize density_offset() { return byte_offset_of(JBoltCluster, _density); } ++ static ByteSize func_indexes_offset() { return byte_offset_of(JBoltCluster, _func_indexes); } ++ ++ static JBoltCluster* constructor(const JBoltFunc* func); ++ static JBoltCluster* copy_constructor(const JBoltCluster* cluster); ++}; ++ ++class JBoltCall : public CHeapObj { ++ private: ++ int _caller_index; ++ int _callee_index; ++ u4 _call_count; ++ traceid _stacktrace_id; ++ ++ public: ++ JBoltCall(); ++ JBoltCall(const JBoltCall& call); ++ JBoltCall(const JBoltFunc& caller_func, const JBoltFunc& callee_func, u4 call_count, traceid stacktrace_id); ++ ++ bool operator==(const JBoltCall& call) const { ++ return _caller_index == call._caller_index && _callee_index == call._callee_index; ++ } ++ ++ JBoltCall& operator=(const JBoltCall& call) { ++ _caller_index = call._caller_index; ++ _callee_index = call._callee_index; ++ _call_count = call._call_count; ++ _stacktrace_id = call._stacktrace_id; ++ return *this; ++ } ++ ++ virtual ~JBoltCall() {} ++ ++ int caller_index() const { return _caller_index; } ++ int callee_index() const { return _callee_index; } ++ u4 call_count() const { return _call_count; } ++ traceid stacktrace_id() const { return _stacktrace_id; } ++ ++ JBoltFunc& caller() const; ++ JBoltFunc& callee() const; ++ void set_caller_index(int index); ++ void set_callee_index(int index); ++ void set_call_count(u4 count); ++ ++ static ByteSize caller_offset() { return byte_offset_of(JBoltCall, _caller_index); } ++ static ByteSize callee_offset() { return byte_offset_of(JBoltCall, _caller_index); } ++ static ByteSize call_count_offset() { return byte_offset_of(JBoltCall, _call_count); } ++ static ByteSize stacktrace_id_offset() { return byte_offset_of(JBoltCall, _stacktrace_id); } ++ ++ static JBoltCall* constructor(const JBoltFunc* caller_func, const JBoltFunc* callee_func, u4 call_count, traceid stacktrace_id); ++ static JBoltCall* copy_constructor(const JBoltCall* call); ++}; ++ ++#endif // SHARE_JBOLT_JBOLTCALLGRAPH_HPP +diff --git a/src/hotspot/share/jbolt/jBoltControlThread.cpp b/src/hotspot/share/jbolt/jBoltControlThread.cpp +new file mode 100644 +index 000000000..dc42ca77b +--- /dev/null ++++ b/src/hotspot/share/jbolt/jBoltControlThread.cpp +@@ -0,0 +1,220 @@ ++/* ++ * Copyright (c) 2020, 2024, 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 "classfile/javaClasses.inline.hpp" ++#include "classfile/vmClasses.hpp" ++#include "classfile/vmSymbols.hpp" ++#include "jbolt/jBoltControlThread.hpp" ++#include "jbolt/jBoltManager.hpp" ++#include "logging/log.hpp" ++#include "logging/logStream.hpp" ++#include "runtime/atomic.hpp" ++#include "runtime/handles.inline.hpp" ++#include "runtime/interfaceSupport.inline.hpp" ++#include "runtime/handles.inline.hpp" ++#include "runtime/javaCalls.hpp" ++#include "runtime/jniHandles.inline.hpp" ++#include "runtime/thread.inline.hpp" ++ ++JavaThread* volatile JBoltControlThread::_the_java_thread = nullptr; ++Monitor* JBoltControlThread::_control_wait_monitor = nullptr; ++Monitor* JBoltControlThread::_sample_wait_monitor = nullptr; ++jobject JBoltControlThread::_thread_obj = nullptr; ++int volatile JBoltControlThread::_signal = JBoltControlThread::SIG_NULL; ++bool volatile JBoltControlThread::_abort = false; ++intx volatile JBoltControlThread::_interval = 0; ++ ++static bool not_first = false; ++ ++void JBoltControlThread::init(TRAPS) { ++ Handle string = java_lang_String::create_from_str("JBolt Control", CATCH); ++ Handle thread_group(THREAD, Universe::system_thread_group()); ++ Handle thread_oop = JavaCalls::construct_new_instance( ++ vmClasses::Thread_klass(), ++ vmSymbols::threadgroup_string_void_signature(), ++ thread_group, ++ string, ++ CATCH); ++ _thread_obj = JNIHandles::make_global(thread_oop); ++ _control_wait_monitor = new Monitor(Mutex::safepoint - 1, "JBoltControlMonitor"); ++ _sample_wait_monitor = new Monitor(Mutex::safepoint - 1, "JBoltSampleMonitor"); ++ Atomic::release_store(&_interval, JBoltSampleInterval); ++} ++ ++void JBoltControlThread::start_thread(TRAPS) { ++ guarantee(Atomic::load_acquire(&_the_java_thread) == nullptr, "sanity"); ++ JavaThread* new_thread = new JavaThread(&thread_entry); ++ if (new_thread->osthread() == nullptr) { ++ fatal("Failed to create JBoltControlThread as no os thread!"); ++ return; ++ } ++ ++ Handle thread_oop(THREAD, JNIHandles::resolve_non_null(_thread_obj)); ++ JavaThread::start_internal_daemon(THREAD, new_thread, thread_oop, MinPriority); ++ guarantee(Atomic::cmpxchg(&_the_java_thread, (JavaThread*) nullptr, new_thread) == nullptr, "sanity"); ++} ++ ++intx JBoltControlThread::sample_interval() { ++ return Atomic::load_acquire(&_interval); ++} ++ ++// Work to do before restarting a control schedule, twice and after only ++bool JBoltControlThread::prev_control_schdule(TRAPS) { ++ guarantee(JBoltManager::auto_mode(), "sanity"); ++ // Clear obsolete data structures ++ if (JBoltManager::clear_last_sample_datas() != 0) { ++ log_error(jbolt)("Something wrong happened in data clean, not going on..."); ++ return false; ++ } ++ ++ // Restart JFR ++ bufferedStream output; ++ DCmd::parse_and_execute(DCmd_Source_Internal, &output, "JFR.start name=jbolt-jfr", ' ', THREAD); ++ if (HAS_PENDING_EXCEPTION) { ++ ResourceMark rm; ++ log_warning(jbolt)("unable to start jfr jbolt-jfr"); ++ log_warning(jbolt)("exception type: %s", PENDING_EXCEPTION->klass()->external_name()); ++ // don't unwind this exception ++ CLEAR_PENDING_EXCEPTION; ++ } ++ ++ return true; ++} ++ ++void JBoltControlThread::control_schdule(TRAPS) { ++ guarantee(JBoltManager::auto_mode(), "sanity"); ++ { MonitorLocker locker(_sample_wait_monitor); ++ // Perform time wait ++ log_info(jbolt)("JBolt Starting Sample for %lds!!!", sample_interval()); ++ const jlong interval = (jlong) sample_interval(); ++ jlong cur_time = os::javaTimeMillis(); ++ const jlong end_time = cur_time + (interval * 1000); ++ while ((end_time > cur_time) && Atomic::load_acquire(&_signal) != SIG_STOP_PROFILING) { ++ int64_t timeout = (int64_t) (end_time - cur_time); ++ locker.wait(timeout); ++ cur_time = os::javaTimeMillis(); ++ } ++ } ++ // Close JFR ++ guarantee(JBoltManager::reorder_phase_profiling_to_waiting(), "sanity"); ++ bufferedStream output; ++ DCmd::parse_and_execute(DCmd_Source_Internal, &output, "JFR.stop name=jbolt-jfr", ' ', THREAD); ++ if (HAS_PENDING_EXCEPTION) { ++ ResourceMark rm; ++ // JFR.stop maybe failed if a jfr recording is already stopped ++ // but it's nothing worry, jbolt should continue to work normally ++ log_warning(jbolt)("unable to stop jfr jbolt-jfr"); ++ log_warning(jbolt)("exception type: %s", PENDING_EXCEPTION->klass()->external_name()); ++ // don't unwind this exception ++ CLEAR_PENDING_EXCEPTION; ++ } ++ if (Atomic::cmpxchg(&_abort, true, false) == /* should abort */ true) { ++ return; ++ } ++ ++ size_t total_nmethod_size = 0; ++ // Init structures for load phase ++ JBoltManager::init_auto_transition(&total_nmethod_size, CATCH); ++ ++ if (total_nmethod_size > JBoltCodeHeapSize) { ++ log_warning(jbolt)("JBolt reordering not complete because JBolt CodeHeap is too small to place all ordered methods. Please use -XX:JBoltCodeHeapSize to enlarge"); ++ log_warning(jbolt)("JBoltCodeHeapSize=" UINTX_FORMAT " B ( need " UINTX_FORMAT " B).", JBoltCodeHeapSize, total_nmethod_size); ++ } ++ ++ if (not_first) { ++ // Exchange Hot Segment primary and secondary relationships ++ JBoltManager::swap_semi_jbolt_segs(); ++ } ++ ++ guarantee(JBoltManager::reorder_phase_waiting_to_reordering(), "sanity"); ++ Atomic::release_store(&_signal, SIG_NULL); ++ ++ // Start reorder ++ JBoltManager::reorder_all_methods(CATCH); ++} ++ ++// Work to do after reordering, twice and after only ++void JBoltControlThread::post_control_schdule(TRAPS) { ++ JBoltManager::clear_secondary_hot_seg(THREAD); ++} ++ ++void JBoltControlThread::thread_run(TRAPS) { ++ if (JBoltManager::auto_mode()) { ++ do { ++ Atomic::release_store(&_signal, SIG_NULL); ++ if (not_first && !prev_control_schdule(THREAD)) continue; ++ guarantee(JBoltManager::reorder_phase_available_to_profiling(), "sanity"); ++ control_schdule(THREAD); ++ if (!JBoltManager::reorder_phase_reordering_to_available()) { ++ // abort logic ++ guarantee(JBoltManager::reorder_phase_waiting_to_available(), "sanity"); ++ guarantee(Atomic::cmpxchg(&_signal, SIG_STOP_PROFILING, SIG_NULL) == SIG_STOP_PROFILING, "sanity"); ++ } ++ else if (not_first) { ++ post_control_schdule(THREAD); ++ } ++ not_first = true; ++ MonitorLocker locker(_control_wait_monitor); ++ while (Atomic::load_acquire(&_signal) != SIG_START_PROFILING) { ++ locker.wait(60 * 1000); ++ } ++ JBoltManager::clear_structures(); ++ } while(true); ++ } else { ++ guarantee(JBoltManager::can_reorder_now(), "sanity"); ++ guarantee(JBoltManager::reorder_phase_collecting_to_reordering(), "sanity"); ++ JBoltManager::reorder_all_methods(CATCH); ++ JBoltManager::clear_structures(); ++ guarantee(JBoltManager::reorder_phase_reordering_to_end(), "sanity"); ++ assert(JBoltLoadMode, "Only manual JBoltLoadMode can reach here"); ++ } ++} ++ ++bool JBoltControlThread::notify_sample_wait(bool abort) { ++ int old_sig = Atomic::cmpxchg(&_signal, SIG_NULL, SIG_STOP_PROFILING); ++ if (old_sig == SIG_NULL) { ++ MonitorLocker locker(_sample_wait_monitor); ++ // abort implementation maybe not in order in extreme cases ++ // add fence? or delete abort() if not so useful. ++ Atomic::release_store(&_abort, abort); ++ locker.notify(); ++ return true; ++ } ++ return false; ++} ++ ++bool JBoltControlThread::notify_control_wait(intx interval) { ++ int old_sig = Atomic::cmpxchg(&_signal, SIG_NULL, SIG_START_PROFILING); ++ if (old_sig == SIG_NULL) { ++ // this lock will be grabbed by ControlThread until it's waiting ++ MonitorLocker locker(_control_wait_monitor); ++ Atomic::release_store(&_interval, interval); ++ locker.notify(); ++ return true; ++ } ++ return false; ++} ++ ++JavaThread* JBoltControlThread::get_thread() { ++ return Atomic::load_acquire(&_the_java_thread); ++} +diff --git a/src/hotspot/share/jbolt/jBoltControlThread.hpp b/src/hotspot/share/jbolt/jBoltControlThread.hpp +new file mode 100644 +index 000000000..e63dd1ea9 +--- /dev/null ++++ b/src/hotspot/share/jbolt/jBoltControlThread.hpp +@@ -0,0 +1,69 @@ ++/* ++ * Copyright (c) 2020, 2024, 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_JBOLT_JBOLTCONTROLTHREAD_HPP ++#define SHARE_JBOLT_JBOLTCONTROLTHREAD_HPP ++ ++#include "runtime/thread.hpp" ++ ++/** ++ * Control JBolt how to run in this thread. ++ */ ++class JBoltControlThread : public AllStatic { ++public: ++ static const int SIG_NULL = 0; ++ static const int SIG_START_PROFILING = 1; ++ static const int SIG_STOP_PROFILING = 2; ++ ++private: ++ static JavaThread* volatile _the_java_thread; ++ // Can be notified by jcmd JBolt.start, restart a control schedule ++ static Monitor* _control_wait_monitor; ++ // Can be notified by jcmd JBolt.stop/abort, stop a running JFR ++ static Monitor* _sample_wait_monitor; ++ static jobject _thread_obj; ++ static int volatile _signal; ++ static bool volatile _abort; ++ static intx volatile _interval; ++ ++ static void thread_entry(JavaThread* thread, TRAPS) { thread_run(thread); } ++ static void thread_run(TRAPS); ++ ++ static intx sample_interval(); ++ static bool prev_control_schdule(TRAPS); ++ static void control_schdule(TRAPS); ++ static void post_control_schdule(TRAPS); ++ ++public: ++ static void init(TRAPS); ++ ++ static void start_thread(TRAPS); ++ ++ static bool notify_sample_wait(bool abort = false); ++ ++ static bool notify_control_wait(intx interval); ++ ++ static JavaThread* get_thread(); ++}; ++ ++#endif // SHARE_JBOLT_JBOLTCONTROLTHREAD_HPP +diff --git a/src/hotspot/share/jbolt/jBoltDcmds.cpp b/src/hotspot/share/jbolt/jBoltDcmds.cpp +new file mode 100644 +index 000000000..0cf1c75b4 +--- /dev/null ++++ b/src/hotspot/share/jbolt/jBoltDcmds.cpp +@@ -0,0 +1,221 @@ ++/* ++ * Copyright (c) 2020, 2024, 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 "jbolt/jBoltDcmds.hpp" ++#include "jbolt/jBoltControlThread.hpp" ++#include "jbolt/jBoltManager.hpp" ++ ++bool register_jbolt_dcmds() { ++ uint32_t full_export = DCmd_Source_Internal | DCmd_Source_AttachAPI | DCmd_Source_MBean; ++ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); ++ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); ++ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); ++ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); ++ return true; ++} ++ ++JBoltStartDCmd::JBoltStartDCmd(outputStream* output, bool heap) : DCmdWithParser(output, heap), ++ _duration("duration", "Duration of time(second) in this sample.", "INT", false, "600") { ++ _dcmdparser.add_dcmd_option(&_duration); ++} ++ ++int JBoltStartDCmd::num_arguments() { ++ ResourceMark rm; ++ JBoltStartDCmd* dcmd = new JBoltStartDCmd(NULL, false); ++ if (dcmd != NULL) { ++ DCmdMark mark(dcmd); ++ return dcmd->_dcmdparser.num_arguments(); ++ } else { ++ return 0; ++ } ++} ++ ++void JBoltStartDCmd::execute(DCmdSource source, TRAPS) { ++ if (!UseJBolt) { ++ output()->print_cr("Unable to execute because \"UseJBolt\" is disabled."); ++ return; ++ } ++ ++ if (!JBoltManager::auto_mode()) { ++ output()->print_cr("JBolt JCMD can only be used in auto mode."); ++ return; ++ } ++ ++ if (!JBoltManager::reorder_phase_is_available()) { ++ output()->print_cr("Unable to start because it's working now. Stop it first."); ++ return; ++ } ++ ++ intx interval = _duration.is_set() ? _duration.value() : JBoltSampleInterval; ++ ++ if (interval < 0) { ++ output()->print_cr("duration is set to %ld which is above range, should be in [0, %d]", interval, max_jint); ++ return; ++ } ++ ++ if (JBoltControlThread::notify_control_wait(interval)) { ++ output()->print_cr("OK. Start a new JBolt schedule, duration=%lds.", interval); ++ } ++ else { ++ output()->print_cr("It's busy now. Please try again later..."); ++ } ++} ++ ++void JBoltStartDCmd::print_help(const char* name) const { ++ output()->print_cr( ++ "Syntax : %s [options]\n" ++ "\n" ++ "Options:\n" ++ "\n" ++ " duration (Optional) Duration of time(second) in this sample. (INT, default value=600)\n" ++ "\n" ++ "Options must be specified using the or = syntax.\n" ++ "\n" ++ "Example usage:\n" ++ " $ jcmd JBolt.start\n" ++ " $ jcmd JBolt.start duration=900", name); ++} ++ ++void JBoltStopDCmd::execute(DCmdSource source, TRAPS) { ++ if (!UseJBolt) { ++ output()->print_cr("Unable to execute because \"UseJBolt\" is disabled."); ++ return; ++ } ++ ++ if (!JBoltManager::auto_mode()) { ++ output()->print_cr("JBolt JCMD can only be used in auto mode."); ++ return; ++ } ++ ++ if (!JBoltManager::reorder_phase_is_profiling()) { ++ output()->print_cr("Unable to stop because it's not sampling now."); ++ return; ++ } ++ ++ if (JBoltControlThread::notify_sample_wait()) { ++ output()->print_cr("OK.\"jbolt-jfr\" would be stopped and turn to reorder."); ++ } else { ++ output()->print_cr("It's busy now. Please try again later..."); ++ } ++} ++ ++void JBoltStopDCmd::print_help(const char* name) const { ++ output()->print_cr( ++ "Syntax : %s\n" ++ "\n" ++ "Example usage:\n" ++ " $ jcmd JBolt.stop", name); ++} ++ ++void JBoltAbortDCmd::execute(DCmdSource source, TRAPS) { ++ if (!UseJBolt) { ++ output()->print_cr("Unable to execute because \"UseJBolt\" is disabled."); ++ return; ++ } ++ ++ if (!JBoltManager::auto_mode()) { ++ output()->print_cr("JBolt JCMD can only be used in auto mode."); ++ return; ++ } ++ ++ if (!JBoltManager::reorder_phase_is_profiling()) { ++ output()->print_cr("Unable to abort because it's not sampling now."); ++ return; ++ } ++ ++ if (JBoltControlThread::notify_sample_wait(true)) { ++ output()->print_cr("OK.\"jbolt-jfr\" would be aborted."); ++ } else { ++ output()->print_cr("It's busy now. Please try again later..."); ++ } ++} ++ ++void JBoltAbortDCmd::print_help(const char* name) const { ++ output()->print_cr( ++ "Syntax : %s\n" ++ "\n" ++ "Example usage:\n" ++ " $ jcmd JBolt.abort", name); ++} ++ ++JBoltDumpDCmd::JBoltDumpDCmd(outputStream* output, bool heap) : DCmdWithParser(output, heap), ++ _filename("filename", "Name of the file to which the flight recording data is dumped", "STRING", true, NULL) { ++ _dcmdparser.add_dcmd_option(&_filename); ++} ++ ++int JBoltDumpDCmd::num_arguments() { ++ ResourceMark rm; ++ JBoltDumpDCmd* dcmd = new JBoltDumpDCmd(NULL, false); ++ if (dcmd != NULL) { ++ DCmdMark mark(dcmd); ++ return dcmd->_dcmdparser.num_arguments(); ++ } else { ++ return 0; ++ } ++} ++ ++void JBoltDumpDCmd::execute(DCmdSource source, TRAPS) { ++ if (!UseJBolt) { ++ output()->print_cr("Unable to execute because \"UseJBolt\" is disabled."); ++ return; ++ } ++ ++ if (!JBoltManager::auto_mode()) { ++ output()->print_cr("JBolt JCMD can only be used in auto mode."); ++ return; ++ } ++ ++ const char* path = _filename.value(); ++ char buffer[PATH_MAX]; ++ char* rp = NULL; ++ ++ JBoltErrorCode ec = JBoltManager::dump_order_in_jcmd(path); ++ switch (ec) { ++ case JBoltOrderNULL: ++ output()->print_cr("Failed: No order applied by JBolt now."); ++ break; ++ case JBoltOpenFileError: ++ output()->print_cr("Failed: File open error or NULL: %s", path); ++ break; ++ case JBoltOK: ++ rp = realpath(path, buffer); ++ output()->print_cr("Successful: Dump to %s", buffer); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++} ++ ++void JBoltDumpDCmd::print_help(const char* name) const { ++ output()->print_cr( ++ "Syntax : %s [options]\n" ++ "\n" ++ "Options:\n" ++ "\n" ++ " filename Name of the file to which the flight recording data is dumped. (STRING, no default value)\n" ++ "\n" ++ "Options must be specified using the or = syntax.\n" ++ "\n" ++ "Example usage:\n" ++ " $ jcmd JBolt.dump filename=order.log", name); ++} +\ No newline at end of file +diff --git a/src/hotspot/share/jbolt/jBoltDcmds.hpp b/src/hotspot/share/jbolt/jBoltDcmds.hpp +new file mode 100644 +index 000000000..f73fc01e6 +--- /dev/null ++++ b/src/hotspot/share/jbolt/jBoltDcmds.hpp +@@ -0,0 +1,129 @@ ++/* ++ * Copyright (c) 2020, 2024, 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_JBOLT_JBOLTDCMDS_HPP ++#define SHARE_JBOLT_JBOLTDCMDS_HPP ++ ++#include "services/diagnosticCommand.hpp" ++ ++class JBoltStartDCmd : public DCmdWithParser { ++ protected: ++ DCmdArgument _duration; ++ public: ++ JBoltStartDCmd(outputStream* output, bool heap); ++ ++ static const char* name() { ++ return "JBolt.start"; ++ } ++ static const char* description() { ++ return "Starts a new JBolt sample schedule(fail if sampling)"; ++ } ++ static const char* impact() { ++ return "Medium: Depending on JFR that JBolt rely on, the impact can range from low to high."; ++ } ++ static const JavaPermission permission() { ++ JavaPermission p = {"java.lang.management.ManagementPermission", "control", NULL}; ++ return p; ++ } ++ static int num_arguments(); ++ virtual void execute(DCmdSource source, TRAPS); ++ virtual void print_help(const char* name) const; ++}; ++ ++class JBoltStopDCmd : public DCmd { ++ public: ++ JBoltStopDCmd(outputStream* output, bool heap) : DCmd(output, heap) {} ++ ++ static const char* name() { ++ return "JBolt.stop"; ++ } ++ static const char* description() { ++ return "Stop a running JBolt sample schedule and reorder immediately(fail if not sampling)"; ++ } ++ static const char* impact() { ++ return "Low"; ++ } ++ static const JavaPermission permission() { ++ JavaPermission p = {"java.lang.management.ManagementPermission", "control", NULL}; ++ return p; ++ } ++ static int num_arguments() { ++ return 0; ++ } ++ ++ virtual void execute(DCmdSource source, TRAPS); ++ virtual void print_help(const char* name) const; ++}; ++ ++class JBoltAbortDCmd : public DCmd { ++ public: ++ JBoltAbortDCmd(outputStream* output, bool heap) : DCmd(output, heap) {} ++ ++ static const char* name() { ++ return "JBolt.abort"; ++ } ++ static const char* description() { ++ return "Stop a running JBolt sample schedule but don't reorder(fail if not sampling)"; ++ } ++ static const char* impact() { ++ return "Low"; ++ } ++ 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); ++ virtual void print_help(const char* name) const; ++}; ++ ++class JBoltDumpDCmd : public DCmdWithParser { ++ protected: ++ DCmdArgument _filename; ++ public: ++ JBoltDumpDCmd(outputStream* output, bool heap); ++ ++ static const char* name() { ++ return "JBolt.dump"; ++ } ++ static const char* description() { ++ return "dump an effective order to file(fail if no order)"; ++ } ++ static const char* impact() { ++ return "Low"; ++ } ++ static const JavaPermission permission() { ++ JavaPermission p = {"java.lang.management.ManagementPermission", "monitor", NULL}; ++ return p; ++ } ++ static int num_arguments(); ++ virtual void execute(DCmdSource source, TRAPS); ++ virtual void print_help(const char* name) const; ++}; ++ ++bool register_jbolt_dcmds(); ++ ++#endif // SHARE_JBOLT_JBOLTDCMDS_HPP +\ No newline at end of file +diff --git a/src/hotspot/share/jbolt/jBoltManager.cpp b/src/hotspot/share/jbolt/jBoltManager.cpp +new file mode 100644 +index 000000000..4cb6f4d1a +--- /dev/null ++++ b/src/hotspot/share/jbolt/jBoltManager.cpp +@@ -0,0 +1,1429 @@ ++/* ++ * Copyright (c) 2020, 2024, 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 ++#include ++#include ++ ++#include "classfile/javaClasses.inline.hpp" ++#include "classfile/symbolTable.hpp" ++#include "classfile/vmSymbols.hpp" ++#include "code/codeBlob.hpp" ++#include "code/codeCache.hpp" ++#include "compiler/compileBroker.hpp" ++#include "compiler/compilerDefinitions.inline.hpp" ++#include "jbolt/jBoltCallGraph.hpp" ++#include "jbolt/jBoltControlThread.hpp" ++#include "jbolt/jBoltManager.hpp" ++#include "jbolt/jBoltUtils.inline.hpp" ++#include "jfr/jfr.hpp" ++#include "jfr/support/jfrMethodLookup.hpp" ++#include "logging/log.hpp" ++#include "logging/logStream.hpp" ++#include "memory/resourceArea.hpp" ++#include "memory/universe.hpp" ++#include "oops/klass.inline.hpp" ++#include "oops/method.inline.hpp" ++#include "runtime/arguments.hpp" ++#include "runtime/atomic.hpp" ++#include "runtime/globals_extension.hpp" ++#include "runtime/handles.inline.hpp" ++#include "runtime/interfaceSupport.inline.hpp" ++#include "runtime/jniHandles.hpp" ++#include "runtime/os.hpp" ++#include "runtime/safepointVerifiers.hpp" ++#include "utilities/formatBuffer.hpp" ++#ifdef LINUX ++#include "os_linux.hpp" ++#endif ++ ++static constexpr int LINE_BUF_SIZE = 8192; // used to parse JBolt order file ++static constexpr int MIN_FRAMESCOUNT = 2; // used as default stacktrace depth ++static constexpr int ILL_NM_STATE = -2; // used to present nmethod illegal state ++ ++#define B_TF(b) (b ? "V" : "X") ++ ++GrowableArray* JBoltManager::_hot_methods_sorted = nullptr; ++JBoltManager::MethodKeyMap* JBoltManager::_hot_methods_vis = nullptr; ++int JBoltManager::_reorder_method_threshold_cnt = 0; ++ ++volatile int JBoltManager::_reorder_phase = JBoltReorderPhase::Available; ++volatile int JBoltManager::_reorderable_method_cnt = 0; ++Method* volatile JBoltManager::_cur_reordering_method = nullptr; ++ ++Thread* JBoltManager::_start_reordering_thread = nullptr; ++ ++JBoltManager::StackFrameKeyMap* JBoltManager::_sampled_methods_refs = nullptr; ++ ++bool JBoltManager::_auto_mode = false; ++ ++// swap between MethodJBoltHot and MethodJBoltTmp ++volatile int JBoltManager::_primary_hot_seg = int(CodeBlobType::MethodJBoltHot); ++volatile int JBoltManager::_secondary_hot_seg = int(CodeBlobType::MethodJBoltTmp); ++ ++volatile int JBoltManager::_gc_should_sweep_code_heaps_now = 0; ++ ++GrowableArray* _order_stored = nullptr; ++ ++// This is a tmp obj used only in initialization phases. ++// We cannot alloc Symbol in phase 1 so we have to parses the order file again ++// in phase 2. ++// This obj will be freed after initialization. ++static FILE* _order_fp = nullptr; ++ ++// The threshold to trigger JBolt reorder in load mode. ++static const double _jbolt_reorder_threshold = 0.8; ++ ++static bool read_line(FILE* fp, char* buf, int buf_len, int* res_len) { ++ if (fgets(buf, buf_len, fp) == nullptr) { ++ return false; ++ } ++ int len = (int) strcspn(buf, "\r\n"); ++ buf[len] = '\0'; ++ *res_len = len; ++ return true; ++} ++ ++static bool read_a_size(char* buf, size_t* res) { ++ char* t = strchr(buf, ' '); ++ if (t == nullptr) return false; ++ *t = '\0'; ++ julong v; ++ if (!Arguments::atojulong(buf, &v)) { ++ *t = ' '; ++ return false; ++ } ++ *t = ' '; ++ *res = (size_t) v; ++ return true; ++} ++ ++static void replace_all(char* s, char from, char to) { ++ char* begin = s; ++ while (true) { ++ char* t = strchr(begin, from); ++ if (t == nullptr) { ++ break; ++ } ++ *t = to; ++ begin = t + 1; ++ } ++} ++ ++JBoltMethodValue::~JBoltMethodValue() { ++ if (_comp_info != nullptr) delete get_comp_info(); ++} ++ ++CompileTaskInfo* JBoltMethodValue::get_comp_info() { ++ return Atomic::load_acquire(&_comp_info); ++} ++ ++bool JBoltMethodValue::set_comp_info(CompileTaskInfo* info) { ++ return Atomic::cmpxchg(&_comp_info, (CompileTaskInfo*) nullptr, info) == nullptr; ++} ++ ++void JBoltMethodValue::clear_comp_info_but_not_release() { ++ Atomic::release_store(&_comp_info, (CompileTaskInfo*) nullptr); ++} ++ ++JBoltStackFrameValue::~JBoltStackFrameValue() { ++ if (_method_holder != nullptr) { ++ if (JNIHandles::is_weak_global_handle(_method_holder)) { ++ JNIHandles::destroy_weak_global(_method_holder); ++ } else { ++ JNIHandles::destroy_global(_method_holder); ++ } ++ } ++} ++ ++jobject JBoltStackFrameValue::get_method_holder() { return _method_holder; } ++ ++void JBoltStackFrameValue::clear_method_holder_but_not_release() { _method_holder = nullptr; } ++ ++CompileTaskInfo::CompileTaskInfo(Method* method, int osr_bci, int comp_level, int comp_reason, Method* hot_method, int hot_cnt): ++ _method(method), _osr_bci(osr_bci), _comp_level(comp_level), _comp_reason(comp_reason), _hot_method(hot_method), _hot_count(hot_cnt) { ++ Thread* thread = Thread::current(); ++ ++ assert(_method != nullptr, "sanity"); ++ // _method_holder can be null for boot loader (the null loader) ++ _method_holder = JNIHandles::make_weak_global(Handle(thread, _method->method_holder()->klass_holder())); ++ ++ if (_hot_method != nullptr && _hot_method != _method) { ++ _hot_method_holder = JNIHandles::make_weak_global(Handle(thread, _hot_method->method_holder()->klass_holder())); ++ } else { ++ _hot_method_holder = nullptr; ++ } ++} ++ ++CompileTaskInfo::~CompileTaskInfo() { ++ if (_method_holder != nullptr) { ++ if (JNIHandles::is_weak_global_handle(_method_holder)) { ++ JNIHandles::destroy_weak_global(_method_holder); ++ } else { ++ JNIHandles::destroy_global(_method_holder); ++ } ++ } ++ if (_hot_method_holder != nullptr) { ++ if (JNIHandles::is_weak_global_handle(_hot_method_holder)) { ++ JNIHandles::destroy_weak_global(_hot_method_holder); ++ } else { ++ JNIHandles::destroy_global(_hot_method_holder); ++ } ++ } ++} ++ ++/** ++ * Set the weak reference to strong reference if the method is not unloaded. ++ * It seems that the life cycle of Method is consistent with that of the Klass and CLD. ++ * @see CompileTask::select_for_compilation() ++ */ ++bool CompileTaskInfo::try_select() { ++ NoSafepointVerifier nsv; ++ Thread* thread = Thread::current(); ++ // is unloaded ++ if (_method_holder != nullptr && JNIHandles::is_weak_global_handle(_method_holder) && JNIHandles::is_weak_global_cleared(_method_holder)) { ++ if (log_is_enabled(Debug, jbolt)) { ++ log_debug(jbolt)("Some method has been unloaded so skip reordering for it: p=%p.", _method); ++ } ++ return false; ++ } ++ ++ assert(_method->method_holder()->is_loader_alive(), "should be alive"); ++ Handle method_holder(thread, _method->method_holder()->klass_holder()); ++ JNIHandles::destroy_weak_global(_method_holder); ++ _method_holder = JNIHandles::make_global(method_holder); ++ ++ if (_hot_method_holder != nullptr) { ++ Handle hot_method_holder(thread, _hot_method->method_holder()->klass_holder()); ++ JNIHandles::destroy_weak_global(_hot_method_holder); ++ _hot_method_holder = JNIHandles::make_global(Handle(thread, _hot_method->method_holder()->klass_holder())); ++ } ++ return true; ++} ++ ++static void check_arg_not_set(JVMFlagsEnum flag) { ++ if (JVMFlag::is_cmdline(flag)) { ++ vm_exit_during_initialization(err_msg("Do not set VM option %s without UseJBolt enabled.", ++ JVMFlag::flag_from_enum(flag)->name())); ++ } ++} ++ ++static const char *method_type_to_string(u1 type) { ++ switch (type) { ++ case JfrStackFrame::FRAME_INTERPRETER: ++ return "Interpreted"; ++ case JfrStackFrame::FRAME_JIT: ++ return "JIT compiled"; ++ case JfrStackFrame::FRAME_INLINE: ++ return "Inlined"; ++ case JfrStackFrame::FRAME_NATIVE: ++ return "Native"; ++ default: ++ ShouldNotReachHere(); ++ return "Unknown"; ++ } ++} ++ ++uintptr_t related_data_jbolt[] = { ++ (uintptr_t)in_bytes(JfrStackTrace::hash_offset()), ++ (uintptr_t)in_bytes(JfrStackTrace::id_offset()), ++ (uintptr_t)in_bytes(JfrStackTrace::hotcount_offset()), ++ (uintptr_t)in_bytes(JfrStackTrace::frames_offset()), ++ (uintptr_t)in_bytes(JfrStackTrace::frames_count_offset()), ++ ++ (uintptr_t)in_bytes(JfrStackFrame::klass_offset()), ++ (uintptr_t)in_bytes(JfrStackFrame::methodid_offset()), ++ (uintptr_t)in_bytes(JfrStackFrame::bci_offset()), ++ (uintptr_t)in_bytes(JfrStackFrame::type_offset()), ++ ++ (uintptr_t)JBoltFunc::constructor, ++ (uintptr_t)JBoltFunc::copy_constructor, ++ (uintptr_t)JBoltCall::constructor, ++ (uintptr_t)JBoltCall::copy_constructor, ++ (uintptr_t)JBoltCallGraph::static_add_func, ++ (uintptr_t)JBoltCallGraph::static_add_call ++}; ++ ++/** ++ * Invoked in JfrStackTraceRepository::add_jbolt(). ++ * Each time JFR record a valid stacktrace, ++ * we log a weak ptr of each unique method in _sampled_methods_refs. ++ */ ++void JBoltManager::log_stacktrace(const JfrStackTrace& stacktrace) { ++ Thread* thread = Thread::current(); ++ HandleMark hm(thread); ++ ++ const JfrStackFrame* frames = stacktrace.get_frames(); ++ unsigned int framesCount = stacktrace.get_framesCount(); ++ ++ for (u4 i = 0; i < framesCount; ++i) { ++ const JfrStackFrame& frame = frames[i]; ++ ++ JBoltStackFrameKey stackframe_key(const_cast(frame.get_klass()), frame.get_methodId()); ++ ++ if (!_sampled_methods_refs->contains(stackframe_key)) { ++ jobject method_holder = JNIHandles::make_weak_global(Handle(thread, frame.get_klass()->klass_holder())); ++ JBoltStackFrameValue stackframe_value(method_holder); ++ _sampled_methods_refs->put(stackframe_key, stackframe_value); ++ // put() transmits method_holder ownership to element in map ++ // set the method_holder to nullptr in temp variable stackframe_value, to avoid double free ++ stackframe_value.clear_method_holder_but_not_release(); ++ } ++ } ++} ++ ++methodHandle JBoltManager::lookup_method(InstanceKlass* klass, traceid method_id) { ++ Thread* thread = Thread::current(); ++ JBoltStackFrameKey stackframe_key(klass, method_id); ++ JBoltStackFrameValue* stackframe_value = _sampled_methods_refs->get(stackframe_key); ++ if (stackframe_value == nullptr) { ++ return methodHandle(); ++ } ++ ++ jobject method_holder = stackframe_value->get_method_holder(); ++ if (method_holder != nullptr && JNIHandles::is_weak_global_handle(method_holder) && JNIHandles::is_weak_global_cleared(method_holder)) { ++ log_debug(jbolt)("method klass at %p is unloaded", (void*)klass); ++ return methodHandle(); ++ } ++ ++ const Method* const lookup_method = JfrMethodLookup::lookup(klass, method_id); ++ if (lookup_method == NULL) { ++ // stacktrace obsolete ++ return methodHandle(); ++ } ++ assert(lookup_method != NULL, "invariant"); ++ methodHandle method(thread, const_cast(lookup_method)); ++ ++ return method; ++} ++ ++void JBoltManager::construct_stacktrace(const JfrStackTrace& stacktrace) { ++ NoSafepointVerifier nsv; ++ if (stacktrace.get_framesCount() < MIN_FRAMESCOUNT) ++ return; ++ ++ u4 topFrameIndex = 0; ++ u4 max_frames = 0; ++ ++ const JfrStackFrame* frames = stacktrace.get_frames(); ++ unsigned int framesCount = stacktrace.get_framesCount(); ++ ++ // Native method subsidence ++ while (topFrameIndex < framesCount) { ++ const JfrStackFrame& frame = frames[topFrameIndex]; ++ ++ if (reinterpret_cast(method_type_to_string(frame.get_type())) != reinterpret_cast("Native")) { ++ break; ++ } ++ ++ topFrameIndex++; ++ } ++ ++ if (framesCount - topFrameIndex < MIN_FRAMESCOUNT) { ++ return; ++ } ++ ++ os::Linux::jboltLog_precalc(topFrameIndex, max_frames); ++ ++ JBoltFunc **tempfunc = NULL; ++ ++ for (u4 i = 0; i < max_frames; ++i) { ++ const JfrStackFrame& frame = frames[topFrameIndex + i]; ++ ++ methodHandle method = lookup_method(const_cast(frame.get_klass()), frame.get_methodId()); ++ if (method.is_null()) { ++ break; ++ } ++ const CompiledMethod* const compiled = method->code(); ++ ++ log_trace(jbolt)( ++ "Method id - %lu\n\tBytecode index - %hu\n\tSignature - %s\n\tType - %s\n\tCompiler - %s\n\tCompile Level - %d\n\tSize - %dB\n", ++ frame.get_methodId(), ++ frame.get_byteCodeIndex(), ++ method->external_name(), ++ method_type_to_string(frame.get_type()), ++ compiled != NULL ? compiled->compiler_name() : "None", ++ compiled != NULL ? compiled->comp_level() : -1, ++ compiled != NULL ? compiled->size() : 0); ++ ++ if (compiled == NULL) break; ++ ++ JBoltMethodKey method_key(method->constants()->pool_holder()->name(), method->name(), method->signature()); ++ JBoltFunc* func = JBoltFunc::constructor(frame.get_klass(), frame.get_methodId(), compiled->size(), method_key); ++ ++ if (!os::Linux::jboltLog_do(related_data_jbolt, (address)&stacktrace, i, compiled->comp_level(), (address)func, (address*)&tempfunc)) { ++ delete func; ++ func = NULL; ++ break; ++ } ++ } ++ ++ log_trace(jbolt)( ++ "StackTrace hash - %u hotcount - %d\n==============================\n", stacktrace.hash(), stacktrace.hotcount()); ++} ++ ++/** ++ * Invoked in JfrStackTraceRepository::write(). ++ * Each time JfrChunkWrite do write and clear stacktrace table, ++ * we update the CG by invoke construct_stacktrace(). ++ */ ++void JBoltManager::construct_cg_once() { ++ guarantee((UseJBolt && JBoltManager::reorder_phase_is_profiling_or_waiting()), "sanity"); ++ ++ GrowableArray* traces = create_growable_array(); ++ ++ { ++ MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag); ++ const JfrStackTraceRepository& repository = JfrStackTraceRepository::instance(); ++ ++ if (repository.get_entries_count_jbolt() == 0) { ++ return; ++ } ++ ++ const JfrStackTrace* const * table = repository.get_stacktrace_table_jbolt(); ++ for (uint i = 0; i < repository.TABLE_SIZE; ++i) { ++ for (const JfrStackTrace* trace = table[i]; trace != nullptr; trace = trace->next()) { ++ traces->append(const_cast(trace)); ++ } ++ } ++ } ++ ++ Thread* thread = Thread::current(); ++ if (thread->is_Java_thread()) { ++ JavaThread* java_thread = JavaThread::current(); ++ ThreadInVMfromNative tiv(java_thread); ++ for (int i = 0; i < traces->length(); ++i) { ++ construct_stacktrace(*(traces->at(i))); ++ } ++ } else { ++ for (int i = 0; i < traces->length(); ++i) { ++ construct_stacktrace(*(traces->at(i))); ++ } ++ } ++ ++ log_trace(jbolt)( ++ "+++++++ one time log over ++++++\n\n"); ++ delete traces; ++} ++ ++static void write_order(const GrowableArray* order, fileStream& fs) { ++ assert(order != nullptr, "sanity"); ++ const char* methodFlag = "M"; ++ const char* segmentor = "C\n"; ++ ++ log_debug(jbolt)("+============================+\n\t\t\tORDER\n"); ++ ++ for (int i = 0; i < order->length(); ++i) { ++ const JBoltFunc& func = order->at(i); ++ if (func.klass() == NULL) { ++ fs.write(segmentor, strlen(segmentor)); ++ continue; ++ } ++ ++ char* holder_name = func.method_key().klass()->as_C_string(); ++ char* name = func.method_key().name()->as_C_string(); ++ char* signature = func.method_key().sig()->as_C_string(); ++ char size[LINE_BUF_SIZE] = {0}; ++ snprintf(size, sizeof(size), "%d", func.size()); ++ ++ log_debug(jbolt)("order %d --- Method - %s %s %s\n", i, holder_name, name, signature); ++ ++ fs.write(methodFlag, strlen(methodFlag)); ++ fs.write(" ", 1); ++ fs.write(size, strlen(size)); ++ fs.write(" ", 1); ++ fs.write(holder_name, strlen(holder_name)); ++ fs.write(" ", 1); ++ fs.write(name, strlen(name)); ++ fs.write(" ", 1); ++ fs.write(signature, strlen(signature)); ++ fs.write("\n", 1); ++ } ++} ++ ++/** ++ * Invoked in before_exit(). ++ * Only use in manual mode. ++ * Dump the order to JBoltOrderFile before vm exit. ++ */ ++void JBoltManager::dump_order_in_manual() { ++ guarantee((UseJBolt && JBoltDumpMode), "sanity"); ++ guarantee(reorder_phase_profiling_to_waiting(), "sanity"); ++ NoSafepointVerifier nsv; ++ ResourceMark rm; ++ GrowableArray* order = JBoltCallGraph::callgraph_instance().hfsort(); ++ ++ fileStream orderFile(JBoltOrderFile, "w+"); ++ ++ if (JBoltOrderFile == NULL || !orderFile.is_open()) { ++ log_error(jbolt)("JBoltOrderFile open error"); ++ vm_exit_during_initialization("JBoltOrderFile open error"); ++ } ++ ++ write_order(order, orderFile); ++ ++ log_info(jbolt)("order generate successful !!"); ++ log_debug(jbolt)("+============================+\n"); ++ delete order; ++ delete _sampled_methods_refs; ++ _sampled_methods_refs = nullptr; ++ JBoltCallGraph::deinitialize(); ++} ++ ++JBoltErrorCode JBoltManager::dump_order_in_jcmd(const char* filename) { ++ guarantee(UseJBolt, "sanity"); ++ NoSafepointVerifier nsv; ++ ResourceMark rm; ++ ++ if (_order_stored == nullptr) return JBoltOrderNULL; ++ ++ fileStream orderFile(filename, "w+"); ++ ++ if (filename == NULL || !orderFile.is_open()) return JBoltOpenFileError; ++ ++ write_order(_order_stored, orderFile); ++ ++ return JBoltOK; ++} ++ ++/** ++ * Do not set the JBolt-related flags manually if UseJBolt is not enabled. ++ */ ++void JBoltManager::check_arguments_not_set() { ++ if (UseJBolt) return; ++ ++ check_arg_not_set(FLAG_MEMBER_ENUM(JBoltDumpMode)); ++ check_arg_not_set(FLAG_MEMBER_ENUM(JBoltLoadMode)); ++ check_arg_not_set(FLAG_MEMBER_ENUM(JBoltOrderFile)); ++ check_arg_not_set(FLAG_MEMBER_ENUM(JBoltSampleInterval)); ++ check_arg_not_set(FLAG_MEMBER_ENUM(JBoltCodeHeapSize)); ++} ++ ++/** ++ * Check which mode is JBolt in. ++ * If JBoltDumpMode or JBoltLoadMode is set manually then do nothing, else it will be fully auto sched by JBolt itself. ++ */ ++void JBoltManager::check_mode() { ++ if (!(JBoltDumpMode || JBoltLoadMode)) { ++ _auto_mode = true; ++ return; ++ } ++ ++ if (!FLAG_IS_DEFAULT(JBoltSampleInterval)) { ++ log_warning(jbolt)("JBoltSampleInterval is ignored because it is not in auto mode."); ++ } ++ ++ if (JBoltDumpMode && JBoltLoadMode) { ++ vm_exit_during_initialization("Do not set both JBoltDumpMode and JBoltLoadMode!"); ++ } ++ ++ guarantee((JBoltDumpMode ^ JBoltLoadMode), "Must set either JBoltDumpMode or JBoltLoadMode!"); ++} ++ ++/** ++ * If in auto mode, JBoltOrderFile will be ignored ++ * If in any manual mode, then JBoltOrderFile will be necessary. ++ * Check whether the order file exists or is accessable. ++ */ ++void JBoltManager::check_order_file() { ++ if (auto_mode()) { ++ if (JBoltOrderFile != nullptr) log_warning(jbolt)("JBoltOrderFile is ignored because it is in auto mode."); ++ return; ++ } ++ ++ if (JBoltOrderFile == nullptr) { ++ vm_exit_during_initialization("JBoltOrderFile is not set!"); ++ } ++ ++ bool file_exist = (::access(JBoltOrderFile, F_OK) == 0); ++ if (file_exist) { ++ if (JBoltDumpMode) { ++ log_warning(jbolt)("JBoltOrderFile to dump already exists and will be overwritten: file=%s.", JBoltOrderFile); ++ ::remove(JBoltOrderFile); ++ } ++ } else { ++ if (JBoltLoadMode) { ++ vm_exit_during_initialization(err_msg("JBoltOrderFile does not exist or cannot be accessed! file=\"%s\".", JBoltOrderFile)); ++ } ++ } ++} ++ ++void JBoltManager::check_dependency() { ++ if (FLAG_IS_CMDLINE(FlightRecorder) ? !FlightRecorder : false) { ++ vm_exit_during_initialization("JBolt depends on JFR!"); ++ } ++ ++ if (!CompilerConfig::is_c2_enabled()) { ++ vm_exit_during_initialization("JBolt depends on C2!"); ++ } ++ ++ if (!SegmentedCodeCache) { ++ vm_exit_during_initialization("JBolt depends on SegmentedCodeCache!"); ++ } ++} ++ ++size_t JBoltManager::calc_nmethod_size_with_padding(size_t nmethod_size) { ++ return align_up(nmethod_size, (size_t) CodeCacheSegmentSize); ++} ++ ++size_t JBoltManager::calc_segment_size_with_padding(size_t segment_size) { ++ size_t page_size = CodeCache::page_size(); ++ if (segment_size < page_size) return page_size; ++ return align_down(segment_size, page_size); ++} ++ ++/** ++ * We have to parse the file twice because SymbolTable is not inited in phase 1... ++ */ ++void JBoltManager::load_order_file_phase1(int* method_cnt, size_t* segment_size) { ++ assert(JBoltOrderFile != nullptr, "sanity"); ++ ++ _order_fp = os::fopen(JBoltOrderFile, "r"); ++ if (_order_fp == nullptr) { ++ vm_exit_during_initialization(err_msg("Cannot open file JBoltOrderFile! file=\"%s\".", JBoltOrderFile)); ++ } ++ ++ int mth_cnt = 0; ++ size_t seg_size = 0; ++ ++ char line[LINE_BUF_SIZE]; ++ int len = -1; ++ while (read_line(_order_fp, line, sizeof(line), &len)) { ++ if (len <= 2) continue; ++ if (line[0] != 'M' || line[1] != ' ') continue; ++ char* left_start = line + 2; ++ ++ // parse nmethod size ++ size_t nmethod_size; ++ if (!read_a_size(left_start, &nmethod_size)) { ++ vm_exit_during_initialization(err_msg("Wrong format of JBolt order line! line=\"%s\".", line)); ++ } ++ ++mth_cnt; ++ seg_size += calc_nmethod_size_with_padding(nmethod_size); ++ } ++ ++ *method_cnt = mth_cnt; ++ *segment_size = seg_size; ++ log_trace(jbolt)("Read order file method_cnt=%d, estimated_segment_size=" SIZE_FORMAT ".", mth_cnt, seg_size); ++} ++ ++bool JBoltManager::parse_method_line_phase2(char* const line, const int len) { ++ // Skip "M ". ++ char* left_start = line + 2; ++ ++ // Skip nmethod size (has parsed in phase1). ++ { ++ char* t = strchr(left_start, ' '); ++ if (t == nullptr) return false; ++ left_start = t + 1; ++ } ++ ++ // Modify "java.lang.Obj" to "java/lang/Obj". ++ replace_all(left_start, '.', '/'); ++ ++ // Parse the three symbols: class name, method name, signature. ++ Symbol* three_symbols[3]; ++ for (int i = 0; i < 2; ++i) { ++ char* t = strchr(left_start, ' '); ++ if (t == nullptr) return false; ++ Symbol* sym = SymbolTable::new_symbol(left_start, t - left_start); ++ three_symbols[i] = sym; ++ left_start = t + 1; ++ } ++ Symbol* sym = SymbolTable::new_symbol(left_start, line + len - left_start); ++ three_symbols[2] = sym; ++ if (log_is_enabled(Trace, jbolt)) { ++ log_trace(jbolt)("HotMethod init: key={%s %s %s}", ++ three_symbols[0]->as_C_string(), ++ three_symbols[1]->as_C_string(), ++ three_symbols[2]->as_C_string()); ++ } ++ ++ // Add to data structure. ++ JBoltMethodKey method_key(three_symbols[0], three_symbols[1], three_symbols[2]); ++ _hot_methods_sorted->append(method_key); ++ JBoltMethodValue method_value; ++ bool put = _hot_methods_vis->put(method_key, method_value); ++ if (!put) { ++ vm_exit_during_initialization(err_msg("Duplicated method: {%s %s %s}!", ++ three_symbols[0]->as_C_string(), ++ three_symbols[1]->as_C_string(), ++ three_symbols[2]->as_C_string())); ++ } ++ ++ return true; ++} ++ ++bool JBoltManager::parse_connected_component_line_phase2(char* const line, const int len) { return true; } ++ ++void JBoltManager::load_order_file_phase2(TRAPS) { ++ guarantee(_order_fp != nullptr, "sanity"); ++ ++ // re-scan ++ fseek(_order_fp, 0, SEEK_SET); ++ ++ char line[LINE_BUF_SIZE]; ++ int len = -1; ++ while (read_line(_order_fp, line, sizeof(line), &len)) { ++ if (len <= 0) continue; ++ bool success = false; ++ switch (line[0]) { ++ case '#': success = true; break; // ignore comments ++ case 'M': success = parse_method_line_phase2(line, len); break; ++ case 'C': success = parse_connected_component_line_phase2(line, len); break; ++ default: break; ++ } ++ if (!success) { ++ vm_exit_during_initialization(err_msg("Wrong format of JBolt order line! line=\"%s\".", line)); ++ } ++ } ++ fclose(_order_fp); ++ _order_fp = nullptr; ++} ++ ++void JBoltManager::init_load_mode_phase1() { ++ if (!(auto_mode() || JBoltLoadMode)) return; ++ ++ if (auto_mode()) { ++ // auto mode has no order now, initialize as default. ++ _hot_methods_sorted = new (mtCompiler) GrowableArray(1, mtCompiler); ++ _hot_methods_vis = new (mtCompiler) MethodKeyMap(); ++ log_info(jbolt)("Default set JBoltCodeHeapSize=" UINTX_FORMAT " B (" UINTX_FORMAT " MB).", JBoltCodeHeapSize, JBoltCodeHeapSize / 1024 / 1024); ++ return; ++ } ++ guarantee(reorder_phase_available_to_collecting(), "sanity"); ++ size_t total_nmethod_size = 0; ++ int method_cnt = 0; ++ load_order_file_phase1(&method_cnt, &total_nmethod_size); ++ ++ _hot_methods_sorted = new (mtCompiler) GrowableArray(method_cnt, mtCompiler); ++ _hot_methods_vis = new (mtCompiler) MethodKeyMap(); ++ ++ if (FLAG_IS_DEFAULT(JBoltCodeHeapSize)) { ++ FLAG_SET_ERGO(JBoltCodeHeapSize, calc_segment_size_with_padding(total_nmethod_size)); ++ log_info(jbolt)("Auto set JBoltCodeHeapSize=" UINTX_FORMAT " B (" UINTX_FORMAT " MB).", JBoltCodeHeapSize, JBoltCodeHeapSize / 1024 / 1024); ++ } ++} ++ ++void JBoltManager::init_load_mode_phase2(TRAPS) { ++ // Only manual load mode need load phase2 ++ if (!JBoltLoadMode) return; ++ ++ load_order_file_phase2(CHECK); ++ _reorderable_method_cnt = 0; ++ _reorder_method_threshold_cnt = _hot_methods_sorted->length() * _jbolt_reorder_threshold; ++} ++ ++void JBoltManager::init_dump_mode_phase2(TRAPS) { ++ if (!(auto_mode() || JBoltDumpMode)) return; ++ ++ JBoltCallGraph::initialize(); ++ _sampled_methods_refs = new (mtTracing) StackFrameKeyMap(); ++ ++ // JBolt will create a JFR by itself ++ // In auto mode, will stop in JBoltControlThread::start_thread() after JBoltSampleInterval. ++ // In manual dump mode, won't stop until program exit. ++ log_info(jbolt)("JBolt in dump mode now, start a JFR recording named \"jbolt-jfr\"."); ++ bufferedStream output; ++ DCmd::parse_and_execute(DCmd_Source_Internal, &output, "JFR.start name=jbolt-jfr", ' ', THREAD); ++ if (HAS_PENDING_EXCEPTION) { ++ ResourceMark rm; ++ log_warning(jbolt)("unable to start jfr jbolt-jfr"); ++ log_warning(jbolt)("exception type: %s", PENDING_EXCEPTION->klass()->external_name()); ++ // don't unwind this exception ++ CLEAR_PENDING_EXCEPTION; ++ } ++} ++ ++static void update_stored_order(const GrowableArray* order) { ++ if (_order_stored != nullptr) { ++ // use a tmp for releasing space to provent _order_stored from being a wild pointer ++ GrowableArray* tmp = _order_stored; ++ _order_stored = nullptr; ++ delete tmp; ++ } ++ _order_stored = new (mtTracing) GrowableArray(order->length(), mtTracing); ++ _order_stored->appendAll(order); ++} ++ ++static CompileTaskInfo* create_compile_task_info(methodHandle& method) { ++ CompiledMethod* compiled = method->code(); ++ if (compiled == nullptr) { ++ log_warning(jbolt)("Recompilation Task init failed because of null nmethod. func: %s.", method->external_name()); ++ return nullptr; ++ } ++ int osr_bci = compiled->is_osr_method() ? compiled->osr_entry_bci() : InvocationEntryBci; ++ int comp_level = compiled->comp_level(); ++ // comp_level adaptation for deoptmization ++ if (comp_level > CompLevel_simple && comp_level <= CompLevel_full_optimization) comp_level = CompLevel_full_optimization; ++ CompileTask::CompileReason comp_reason = CompileTask::Reason_Reorder; ++ CompileTaskInfo* ret = new CompileTaskInfo(method(), osr_bci, comp_level, (int)comp_reason, ++ nullptr, 0); ++ return ret; ++} ++ ++/** ++ * This function is invoked by JBoltControlThread. ++ * Do initialization for converting dump mode to load mode. ++ */ ++void JBoltManager::init_auto_transition(size_t* segment_size, TRAPS) { ++ guarantee(UseJBolt && auto_mode(), "sanity"); ++ NoSafepointVerifier nsv; ++ ResourceMark rm; ++ ++ GrowableArray* order = JBoltCallGraph::callgraph_instance().hfsort(); ++ update_stored_order(order); ++ ++ size_t seg_size = 0; ++ for (int i = 0; i < order->length(); ++i) { ++ const JBoltFunc& func = order->at(i); ++ if (func.klass() == NULL) { ++ continue; ++ } ++ ++ methodHandle method = lookup_method(const_cast(func.klass()), func.method_id()); ++ if (method.is_null()) { ++ continue; ++ } ++ ++ CompileTaskInfo* cti = create_compile_task_info(method); ++ if (cti == nullptr) { ++ continue; ++ } ++ ++ JBoltMethodKey method_key = func.method_key(); ++ JBoltMethodValue method_value; ++ if (!method_value.set_comp_info(cti)) { ++ delete cti; ++ continue; ++ } ++ ++ seg_size += calc_nmethod_size_with_padding(func.size()); ++ _hot_methods_sorted->append(method_key); ++ bool put = _hot_methods_vis->put(method_key, method_value); ++ if (!put) { ++ vm_exit_during_initialization(err_msg("Duplicated method: {%s %s %s}!", ++ method_key.klass()->as_C_string(), ++ method_key.name()->as_C_string(), ++ method_key.sig()->as_C_string())); ++ } ++ method_value.clear_comp_info_but_not_release(); ++ } ++ log_info(jbolt)("order generate successful !!"); ++ *segment_size = calc_segment_size_with_padding(seg_size); ++ delete order; ++} ++ ++/** ++ * This function must be invoked after CompilerConfig::ergo_initialize() in Arguments::apply_ergo(). ++ * This function must be invoked before CodeCache::initialize_heaps() in codeCache_init() in init_globals(). ++ * Thread and SymbolTable is not inited now! ++ */ ++void JBoltManager::init_phase1() { ++ if (!UseJBolt) return; ++ check_mode(); ++ check_dependency(); ++ check_order_file(); ++ ++ /* dump mode has nothing to do in phase1 */ ++ init_load_mode_phase1(); ++} ++ ++void JBoltManager::init_phase2(TRAPS) { ++ if (!UseJBolt) return; ++ ++ ResourceMark rm(THREAD); ++ init_dump_mode_phase2(CHECK); ++ init_load_mode_phase2(CHECK); ++ ++ // Manual dump mode doesn't need JBoltControlThread, directly go to profiling phase ++ if (JBoltDumpMode) { ++ guarantee(JBoltManager::reorder_phase_available_to_profiling(), "sanity"); ++ return; ++ } ++ ++ JBoltControlThread::init(CHECK); ++ // Auto mode will start control thread earlier. ++ // Manual load mode start later in check_start_reordering() ++ if (auto_mode()) { ++ JBoltControlThread::start_thread(CHECK_AND_CLEAR); ++ } ++} ++ ++/** ++ * Code heaps are initialized between init phase 1 and init phase 2. ++ */ ++void JBoltManager::init_code_heaps(size_t non_nmethod_size, size_t profiled_size, size_t non_profiled_size, size_t cache_size, size_t ps, size_t alignment) { ++ assert(UseJBolt && !JBoltDumpMode, "sanity"); ++ if(!is_aligned(JBoltCodeHeapSize, alignment)) { ++ vm_exit_during_initialization(err_msg("JBoltCodeHeapSize should be %ld aligned, please adjust", alignment)); ++ } ++ ++ size_t jbolt_hot_size = JBoltCodeHeapSize; ++ size_t jbolt_tmp_size = JBoltCodeHeapSize; ++ size_t jbolt_total_size = jbolt_hot_size + jbolt_tmp_size; ++ if (non_profiled_size <= jbolt_total_size) { ++ vm_exit_during_initialization(err_msg("Not enough space in non-profiled code heap to split out JBolt heap(s): " SIZE_FORMAT "K <= " SIZE_FORMAT "K", non_profiled_size/K, jbolt_total_size/K)); ++ } ++ non_profiled_size -= jbolt_total_size; ++ non_profiled_size = align_down(non_profiled_size, alignment); ++ FLAG_SET_ERGO(NonProfiledCodeHeapSize, non_profiled_size); ++ ++ // Memory layout with JBolt: ++ // ---------- high ----------- ++ // Non-profiled nmethods ++ // JBolt tmp nmethods ++ // JBolt hot nmethods ++ // Non-nmethods ++ // Profiled nmethods ++ // ---------- low ------------ ++ ReservedCodeSpace rs = CodeCache::reserve_heap_memory(cache_size, ps); ++ ReservedSpace profiled_space = rs.first_part(profiled_size); ++ ReservedSpace r1 = rs.last_part(profiled_size); ++ ReservedSpace non_nmethod_space = r1.first_part(non_nmethod_size); ++ ReservedSpace r2 = r1.last_part(non_nmethod_size); ++ ReservedSpace jbolt_hot_space = r2.first_part(jbolt_hot_size); ++ ReservedSpace r3 = r2.last_part(jbolt_hot_size); ++ ReservedSpace jbolt_tmp_space = r3.first_part(jbolt_tmp_size); ++ ReservedSpace non_profiled_space = r3.last_part(jbolt_tmp_size); ++ ++ CodeCache::add_heap(non_nmethod_space, "CodeHeap 'non-nmethods'", CodeBlobType::NonNMethod); ++ CodeCache::add_heap(profiled_space, "CodeHeap 'profiled nmethods'", CodeBlobType::MethodProfiled); ++ CodeCache::add_heap(non_profiled_space, "CodeHeap 'non-profiled nmethods'", CodeBlobType::MethodNonProfiled); ++ const char* no_space = nullptr; ++ CodeCache::add_heap(jbolt_hot_space, "CodeHeap 'jbolt hot nmethods'", CodeBlobType::MethodJBoltHot); ++ if (jbolt_hot_size != jbolt_hot_space.size()) { ++ no_space = "hot"; ++ } ++ CodeCache::add_heap(jbolt_tmp_space, "CodeHeap 'jbolt tmp nmethods'", CodeBlobType::MethodJBoltTmp); ++ if (jbolt_tmp_size != jbolt_tmp_space.size()) { ++ no_space = "tmp"; ++ } ++ if (no_space != nullptr) { ++ vm_exit_during_initialization(FormatBuffer<1024>( ++ "No enough space for JBolt %s heap: \n" ++ "Expect: cache_size=" SIZE_FORMAT "K, profiled_size=" SIZE_FORMAT "K, non_nmethod_size=" SIZE_FORMAT "K, jbolt_hot_size=" SIZE_FORMAT "K, non_profiled_size=" SIZE_FORMAT "K, jbolt_tmp_size=" SIZE_FORMAT "K\n" ++ "Actual: cache_size=" SIZE_FORMAT "K, profiled_size=" SIZE_FORMAT "K, non_nmethod_size=" SIZE_FORMAT "K, jbolt_hot_size=" SIZE_FORMAT "K, non_profiled_size=" SIZE_FORMAT "K, jbolt_tmp_size=" SIZE_FORMAT "K\n" ++ "alignment=" SIZE_FORMAT, ++ no_space, ++ cache_size/K, profiled_size/K, non_nmethod_size/K, jbolt_hot_size/K, non_profiled_size/K, jbolt_tmp_size/K, ++ rs.size()/K, profiled_space.size()/K, non_nmethod_space.size()/K, jbolt_hot_space.size()/K, non_profiled_space.size()/K, jbolt_tmp_space.size()/K, ++ alignment)); ++ } ++} ++ ++int JBoltManager::reorder_phase() { ++ return Atomic::load_acquire(&_reorder_phase); ++} ++ ++bool JBoltManager::reorder_phase_available_to_collecting() { ++ assert(!auto_mode(), "two-phase only"); ++ return Atomic::cmpxchg(&_reorder_phase, JBoltReorderPhase::Available, JBoltReorderPhase::Collecting) == JBoltReorderPhase::Available; ++} ++ ++bool JBoltManager::reorder_phase_collecting_to_reordering() { ++ assert(!auto_mode(), "two-phase only"); ++ return Atomic::cmpxchg(&_reorder_phase, JBoltReorderPhase::Collecting, JBoltReorderPhase::Reordering) == JBoltReorderPhase::Collecting; ++} ++ ++bool JBoltManager::reorder_phase_available_to_profiling() { ++ assert(auto_mode(), "one-phase only"); ++ return Atomic::cmpxchg(&_reorder_phase, JBoltReorderPhase::Available, JBoltReorderPhase::Profiling) == JBoltReorderPhase::Available; ++} ++ ++bool JBoltManager::reorder_phase_profiling_to_reordering() { ++ assert(auto_mode(), "one-phase only"); ++ return Atomic::cmpxchg(&_reorder_phase, JBoltReorderPhase::Profiling, JBoltReorderPhase::Reordering) == JBoltReorderPhase::Profiling; ++} ++ ++bool JBoltManager::reorder_phase_reordering_to_available() { ++ assert(auto_mode(), "one-phase only"); ++ return Atomic::cmpxchg(&_reorder_phase, JBoltReorderPhase::Reordering, JBoltReorderPhase::Available) == JBoltReorderPhase::Reordering; ++} ++ ++bool JBoltManager::reorder_phase_profiling_to_available() { ++ assert(auto_mode(), "one-phase only"); ++ return Atomic::cmpxchg(&_reorder_phase, JBoltReorderPhase::Profiling, JBoltReorderPhase::Available) == JBoltReorderPhase::Profiling; ++} ++ ++bool JBoltManager::reorder_phase_profiling_to_waiting() { ++ return Atomic::cmpxchg(&_reorder_phase, JBoltReorderPhase::Profiling, JBoltReorderPhase::Waiting) == JBoltReorderPhase::Profiling; ++} ++ ++bool JBoltManager::reorder_phase_waiting_to_reordering() { ++ assert(auto_mode(), "one-phase only"); ++ return Atomic::cmpxchg(&_reorder_phase, JBoltReorderPhase::Waiting, JBoltReorderPhase::Reordering) == JBoltReorderPhase::Waiting; ++} ++ ++bool JBoltManager::reorder_phase_waiting_to_available() { ++ assert(auto_mode(), "one-phase only"); ++ return Atomic::cmpxchg(&_reorder_phase, JBoltReorderPhase::Waiting, JBoltReorderPhase::Available) == JBoltReorderPhase::Waiting; ++} ++ ++bool JBoltManager::reorder_phase_reordering_to_end() { ++ return Atomic::cmpxchg(&_reorder_phase, JBoltReorderPhase::Reordering, JBoltReorderPhase::End) == JBoltReorderPhase::Reordering; ++} ++ ++bool JBoltManager::reorder_phase_is_waiting() { ++ return Atomic::load_acquire(&_reorder_phase) == JBoltReorderPhase::Waiting; ++} ++ ++bool JBoltManager::reorder_phase_is_available() { ++ bool res = (Atomic::load_acquire(&_reorder_phase) == JBoltReorderPhase::Available); ++ assert(!res || auto_mode(), "one-phase only"); ++ return res; ++} ++ ++bool JBoltManager::reorder_phase_is_collecting() { ++ bool res = (Atomic::load_acquire(&_reorder_phase) == JBoltReorderPhase::Collecting); ++ assert(!res || !auto_mode(), "two-phase only"); ++ return res; ++} ++ ++bool JBoltManager::reorder_phase_is_profiling() { ++ bool res = (Atomic::load_acquire(&_reorder_phase) == JBoltReorderPhase::Profiling); ++ assert(!res || auto_mode(), "for two-phase dump mode & one-phase"); ++ return res; ++} ++ ++bool JBoltManager::reorder_phase_is_reordering() { ++ return Atomic::load_acquire(&_reorder_phase) == JBoltReorderPhase::Reordering; ++} ++ ++bool JBoltManager::reorder_phase_is_collecting_or_reordering() { ++ int p = Atomic::load_acquire(&_reorder_phase); ++ assert(p != JBoltReorderPhase::Collecting || !auto_mode(), "two-phase only"); ++ return p == JBoltReorderPhase::Collecting || p == JBoltReorderPhase::Reordering; ++} ++ ++bool JBoltManager::reorder_phase_is_profiling_or_waiting() { ++ int p = Atomic::load_acquire(&_reorder_phase); ++ return p == JBoltReorderPhase::Profiling || p == JBoltReorderPhase::Waiting; ++} ++ ++Method* JBoltManager::cur_reordering_method() { ++ return Atomic::load_acquire(&_cur_reordering_method); ++} ++ ++void JBoltManager::set_cur_reordering_method(Method* method) { ++ Atomic::release_store(&_cur_reordering_method, method); ++} ++ ++int JBoltManager::inc_reorderable_method_cnt() { ++ return Atomic::add(&_reorderable_method_cnt, +1); ++} ++ ++bool JBoltManager::can_reorder_now() { ++ return Atomic::load_acquire(&_reorderable_method_cnt) >= _reorder_method_threshold_cnt; ++} ++ ++bool JBoltManager::should_reorder_now() { ++ return Atomic::load_acquire(&_reorderable_method_cnt) == _reorder_method_threshold_cnt; ++} ++ ++bool JBoltManager::gc_should_sweep_code_heaps_now() { ++ return Atomic::load_acquire(&_gc_should_sweep_code_heaps_now) != 0; ++} ++ ++CodeBlobType JBoltManager::primary_hot_seg() { ++ return CodeBlobType(Atomic::load_acquire(&_primary_hot_seg)); ++} ++ ++CodeBlobType JBoltManager::secondary_hot_seg() { ++ return CodeBlobType(Atomic::load_acquire(&_secondary_hot_seg)); ++} ++ ++int JBoltManager::clear_manager() { ++ /* _hot_methods_sorted, _hot_methods_vis and _sampled_methods_refs have been cleared in other pos, don't delete again */ ++ guarantee(_hot_methods_sorted == nullptr, "sanity"); ++ guarantee(_hot_methods_vis == nullptr, "sanity"); ++ guarantee(_sampled_methods_refs == nullptr, "sanity"); ++ // Re-allocate them ++ _hot_methods_sorted = new (mtCompiler) GrowableArray(1, mtCompiler); ++ _hot_methods_vis = new (mtCompiler) MethodKeyMap(); ++ _sampled_methods_refs = new (mtTracing) StackFrameKeyMap(); ++ ++ return 0; ++} ++ ++/** ++ * Invoked in JBoltControlThread::prev_control_schedule(). ++ * Expect to only execute in auto mode while JBolt.start triggered. ++ * Clear JBolt related data structures to restore a initial env same as sample never happening. ++*/ ++int JBoltManager::clear_last_sample_datas() { ++ int ret = 0; ++ // Clear _table_jbolt in JfrStackTraceRepository ++ ret = JfrStackTraceRepository::clear_jbolt(); ++ // Clear JBoltCallGraph ++ ret = JBoltCallGraph::callgraph_instance().clear_instance(); ++ // Clear JBoltManager ++ ret = clear_manager(); ++ ++ return ret; ++} ++ ++/** ++ * Invoked in JBoltControlThread::prev_control_schedule(). ++ * Swap primary hot segment with secondary hot segment ++ */ ++void JBoltManager::swap_semi_jbolt_segs() { ++ guarantee(reorder_phase_is_waiting(), "swap must happen in reorder phase Profiling."); ++ int tmp = Atomic::xchg(&_secondary_hot_seg, Atomic::load_acquire(&_primary_hot_seg)); ++ Atomic::xchg(&_primary_hot_seg, tmp); ++} ++ ++/** ++ * Invoked in JBoltControlThread::post_control_schdule(). ++ * Free scondary hot segment space for next reorder. ++ */ ++void JBoltManager::clear_secondary_hot_seg(TRAPS) { ++ guarantee(reorder_phase_is_available(), "secondary clear must happen in reorder phase Available."); ++ // scan secondary hot seg and recompile alive nmethods to non-profiled ++ ResourceMark rm(THREAD); ++ // We cannot alloc weak handle within CodeCache_lock because of the mutex rank check. ++ // So instead we keep the methods alive only within the scope of this method. ++ JBoltUtils::MetaDataKeepAliveMark mdm(THREAD); ++ const GrowableArray& to_recompile = mdm.kept(); ++ ++ { ++ MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); ++ CodeHeap* sec_hot = CodeCache::get_code_heap(secondary_hot_seg()); ++ for (CodeBlob* cb = (CodeBlob*) sec_hot->first(); cb != nullptr; cb = (CodeBlob*) sec_hot->next(cb)) { ++ nmethod* nm = cb->as_nmethod_or_null(); ++ Method* m = nm->method(); ++ if (nm && nm->get_state() == CompiledMethod::in_use && m != nullptr) { ++ mdm.add(m); ++ } ++ } ++ } ++ ++ for (int i = 0; i < to_recompile.length(); ++i) { ++ Method* m = (Method*) to_recompile.at(i); ++ methodHandle method(THREAD, m); ++ CompileTaskInfo* cti = create_compile_task_info(method); ++ if (cti == nullptr) continue; ++ guarantee(cti->try_select(), "method is on stack, should be ok"); ++ assert(cti->hot_method() == nullptr, "sanity"); ++ methodHandle hot_method; ++ ++ bool recompile_result = enqueue_recompile_task(cti, method, hot_method, THREAD); ++ if(recompile_result) { ++ check_compiled_result(method(), CodeBlobType::MethodNonProfiled, THREAD); ++ } ++ delete cti; ++ } ++ ++ Atomic::release_store(&_gc_should_sweep_code_heaps_now, 1); ++ Universe::heap()->collect(GCCause::_java_lang_system_gc); ++ Universe::heap()->collect(GCCause::_java_lang_system_gc); ++ Atomic::release_store(&_gc_should_sweep_code_heaps_now, 0); ++ log_info(jbolt)("Sweep secondary segment"); ++ print_code_heaps(); ++} ++ ++/** ++ * Invoked in ciEnv::register_method() in CompilerThread. ++ * Controls where the new nmethod should be allocated. ++ * ++ * Returns CodeBlobType::All if it is not determined by JBolt logic. ++ */ ++CodeBlobType JBoltManager::calc_code_blob_type(Method* method, CompileTask* task, TRAPS) { ++ assert(UseJBolt && reorder_phase_is_collecting_or_reordering(), "sanity"); ++ const CodeBlobType not_care = CodeBlobType::All; ++ ++ // Only cares about non-profiled segment. ++ int lvl = task->comp_level(); ++ if (lvl != CompLevel_full_optimization && lvl != CompLevel_simple) { ++ return not_care; ++ } ++ ++ // Ignore on-stack-replacement. ++ if (task->osr_bci() != InvocationEntryBci) { ++ return not_care; ++ } ++ ++ int cur_reorder_phase = reorder_phase(); ++ // Do nothing after reordering. ++ if (cur_reorder_phase != JBoltReorderPhase::Collecting && cur_reorder_phase != JBoltReorderPhase::Reordering) { ++ return not_care; ++ } ++ // Only cares about the current reordering method. ++ if (cur_reorder_phase == JBoltReorderPhase::Reordering) { ++ if (cur_reordering_method() == method) { ++ log_trace(jbolt)("Compiling to JBolt heap: method=%s.", method->name_and_sig_as_C_string()); ++ return primary_hot_seg(); ++ } ++ return not_care; ++ } ++ guarantee(cur_reorder_phase == JBoltReorderPhase::Collecting, "sanity"); ++ assert(!auto_mode(), "sanity"); ++ ++ JBoltMethodKey method_key(method); ++ JBoltMethodValue* method_value = _hot_methods_vis->get(method_key); ++ if (method_value == nullptr) { ++ return not_care; ++ } ++ ++ // Register the method and the compile task. ++ if (method_value->get_comp_info() == nullptr) { ++ CompileTaskInfo* cti = new CompileTaskInfo(method, task->osr_bci(), task->comp_level(), (int) task->compile_reason(), ++ task->hot_method(), task->hot_count()); ++ if (method_value->set_comp_info(cti)) { ++ int cnt = inc_reorderable_method_cnt(); ++ log_trace(jbolt)("Reorderable method found: cnt=%d, lvl=%d, p=%p, method=%s.", ++ cnt, task->comp_level(), method, method->name_and_sig_as_C_string()); ++ if (is_power_of_2(_reorder_method_threshold_cnt - cnt)) { ++ log_debug(jbolt)("Reorderable cnt: %d/%d/%d", cnt, _reorder_method_threshold_cnt, _hot_methods_sorted->length()); ++ } ++ if (cnt == _reorder_method_threshold_cnt) { ++ log_info(jbolt)("Time to reorder: %d/%d/%d", cnt, _reorder_method_threshold_cnt, _hot_methods_sorted->length()); ++ _start_reordering_thread = THREAD; ++ } ++ } else { ++ delete cti; ++ } ++ } ++ ++ return secondary_hot_seg(); ++} ++ ++/** ++ * Check if reordering should start. ++ * The reordering should only start once (for now). ++ * We don't do this check in "if (cnt == _reorder_method_threshold_cnt)" in calc_code_blob_type() ++ * because it will cause an assert error: "Possible safepoint reached by thread that does not allow it". ++ */ ++void JBoltManager::check_start_reordering(TRAPS) { ++ // _start_reordering_thread is set and tested in the same thread. No need to be atomic. ++ if (_start_reordering_thread == THREAD) { ++ _start_reordering_thread = nullptr; ++ if (JBoltControlThread::get_thread() == nullptr) { ++ assert(can_reorder_now(), "sanity"); ++ log_info(jbolt)("Starting JBoltControlThread to reorder."); ++ JBoltControlThread::start_thread(CHECK_AND_CLEAR); ++ } ++ } ++} ++ ++/** ++ * The task will be added to the compile queue and be compiled just like other tasks. ++ */ ++CompileTask* JBoltManager::create_a_task_instance(CompileTaskInfo* cti, methodHandle& method, methodHandle& hot_method, TRAPS) { ++ int osr_bci = cti->osr_bci(); ++ int comp_level = cti->comp_level(); ++ CompileTask::CompileReason comp_reason = (CompileTask::CompileReason) cti->comp_reason(); ++ int hot_count = cti->hot_count(); ++ bool is_blocking = true; ++ ++ // init a task (@see CompileBroker::create_compile_task()) ++ CompileTask* task = CompileTask::allocate(); ++ int compile_id = CompileBroker::assign_compile_id(method, osr_bci); ++ task->initialize(compile_id, method, osr_bci, comp_level, ++ hot_method, hot_count, comp_reason, ++ is_blocking); ++ return task; ++} ++ ++/** ++ * Print the failure reason if something is wrong in recompilation. ++ */ ++void JBoltManager::check_compiled_result(Method* method, CodeBlobType check_blob_type, TRAPS) { ++ CompiledMethod* cm = method->code(); ++ if (cm == nullptr) { ++ log_warning(jbolt)("Recompilation failed because of null nmethod."); ++ return; ++ } ++ nmethod* nm = cm->as_nmethod_or_null(); ++ if (nm == nullptr) { ++ log_warning(jbolt)("Recompilation failed because the code is not a nmethod."); ++ return; ++ } ++ CodeBlobType code_blob_type = CodeCache::get_code_blob_type(nm); ++ if (code_blob_type != check_blob_type) { ++ log_warning(jbolt)("Recompilation failed because the nmethod is not in heap [%s]: it's in [%s].", ++ CodeCache::get_code_heap_name(check_blob_type), CodeCache::get_code_heap_name(code_blob_type)); ++ return; ++ } ++ log_trace(jbolt)("Recompilation good: code=%p, size=%d, method=%s, heap=%s.", ++ nm, nm->size(), method->name_and_sig_as_C_string(), CodeCache::get_code_heap_name(check_blob_type)); ++} ++ ++/** ++ * Create the compile task instance and enqueue into compile queue ++ */ ++bool JBoltManager::enqueue_recompile_task(CompileTaskInfo* cti, methodHandle& method, methodHandle& hot_method, TRAPS) { ++ CompileTask* task = nullptr; ++ CompileQueue* queue = CompileBroker::compile_queue(cti->comp_level()); ++ { MutexLocker locker(THREAD, MethodCompileQueue_lock); ++ if (CompileBroker::compilation_is_in_queue(method)) { ++ log_warning(jbolt)("JBOLT won't compile as \"compilation is in queue\": method=%s.", method->name_and_sig_as_C_string()); ++ return false; ++ } ++ ++ task = create_a_task_instance(cti, method, hot_method, CHECK_AND_CLEAR_false); ++ if (task == nullptr) { ++ log_warning(jbolt)("JBOLT won't compile as \"task instance is NULL\": method=%s.", method->name_and_sig_as_C_string()); ++ return false; ++ } ++ queue->add(task); ++ } ++ ++ // Same waiting logic as CompileBroker::wait_for_completion(). ++ { MonitorLocker ml(THREAD, task->lock()); ++ while (!task->is_complete() && !CompileBroker::is_compilation_disabled_forever()) { ++ ml.wait(); ++ } ++ } ++ ++ CompileBroker::wait_for_completion(task); ++ task = nullptr; // freed ++ return true; ++} ++ ++/** ++ * Recompilation is to move the nmethod to _primary_hot_seg. ++ */ ++bool JBoltManager::recompile_one(CompileTaskInfo* cti, methodHandle& method, methodHandle& hot_method, TRAPS) { ++ ResourceMark rm(THREAD); ++ ++ if (cti->osr_bci() != InvocationEntryBci) { ++ log_trace(jbolt)("We don't handle on-stack-replacement nmethods: method=%s.", method->name_and_sig_as_C_string()); ++ return false; ++ } ++ ++ if (log_is_enabled(Trace, jbolt)) { ++ const char* heap_name = nullptr; ++ CompiledMethod* cm = method->code(); ++ if (cm == nullptr) heap_name = ""; ++ else if (!cm->is_nmethod()) heap_name = ""; ++ else heap_name = CodeCache::get_code_heap_name(CodeCache::get_code_blob_type(cm)); ++ log_trace(jbolt)("Start to recompile & reorder: heap=%s, method=%s.", heap_name, method->name_and_sig_as_C_string()); ++ } ++ ++ // Add a compilation task. ++ set_cur_reordering_method(method()); ++ enqueue_recompile_task(cti, method, hot_method, CHECK_AND_CLEAR_false); ++ check_compiled_result(method(), primary_hot_seg(), CHECK_AND_CLEAR_false); ++ ++ return true; ++} ++ ++/** ++ * This method is invoked in a new thread JBoltControlThread. ++ * Recompiles the methods in the order list one by one (serially) based on the hot order. ++ * The methods to recompile were almost all in MethodJBoltTmp, and will in install in ++ * MethodJBoltHot after recompilation. ++ */ ++void JBoltManager::reorder_all_methods(TRAPS) { ++ guarantee(UseJBolt && reorder_phase_is_reordering(), "sanity"); ++ log_info(jbolt)("Start to reorder!"); ++ print_code_heaps(); ++ ++ ResourceMark rm(THREAD); ++ for (int i = 0; i < _hot_methods_sorted->length(); ++i) { ++ JBoltMethodKey k = _hot_methods_sorted->at(i); ++ JBoltMethodValue* v = _hot_methods_vis->get(k); ++ if (v == nullptr) continue; ++ CompileTaskInfo* cti = v->get_comp_info(); ++ if (cti == nullptr) continue; ++ if (!cti->try_select()) continue; ++ ++ methodHandle method(THREAD, cti->method()); ++ methodHandle hot_method(THREAD, cti->hot_method()); ++ ++ recompile_one(cti, method, hot_method, THREAD); ++ if (HAS_PENDING_EXCEPTION) { ++ Handle ex(THREAD, PENDING_EXCEPTION); ++ CLEAR_PENDING_EXCEPTION; ++ LogTarget(Warning, jbolt) lt; ++ if (lt.is_enabled()) { ++ LogStream ls(lt); ++ ls.print("Failed to recompile the method: %s.", method->name_and_sig_as_C_string()); ++ java_lang_Throwable::print(ex(), &ls); ++ } ++ } ++ } ++ ++ log_info(jbolt)("JBolt reordering succeeds."); ++ print_code_heaps(); ++ ++} ++ ++void JBoltManager::clear_structures() { ++ delete _sampled_methods_refs; ++ _sampled_methods_refs = nullptr; ++ JBoltCallGraph::deinitialize(); ++ set_cur_reordering_method(nullptr); ++ delete _hot_methods_sorted; ++ _hot_methods_sorted = nullptr; ++ delete _hot_methods_vis; ++ _hot_methods_vis = nullptr; ++} ++ ++void JBoltManager::print_code_heap(outputStream& ls, CodeHeap* heap, const char* name) { ++ for (CodeBlob* cb = (CodeBlob*) heap->first(); cb != nullptr; cb = (CodeBlob*) heap->next(cb)) { ++ nmethod* nm = cb->as_nmethod_or_null(); ++ Method* m = nm != nullptr ? nm->method() : nullptr; ++ ls.print_cr("%s %p %d nmethod=%s, entrant=%s, name=[%s %s %s]", ++ name, ++ cb, cb->size(), ++ B_TF(cb->is_nmethod()), ++ nm ? B_TF(!nm->is_not_entrant()) : "?", ++ m ? m->method_holder()->name()->as_C_string() : cb->name(), ++ m ? m->name()->as_C_string() : nullptr, ++ m ? m->signature()->as_C_string() : nullptr); ++ } ++} ++ ++void JBoltManager::print_code_heaps() { ++ { ++ LogTarget(Debug, jbolt) lt; ++ if (!lt.is_enabled()) return; ++ LogStream ls(lt); ++ MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); ++ CodeCache::print_summary(&ls, true); ++ } ++ ++ { ++ LogTarget(Trace, jbolt) lt; ++ if (!lt.is_enabled()) return; ++ LogStream ls(lt); ++ CodeHeap* hot_heap = CodeCache::get_code_heap(CodeBlobType::MethodJBoltHot); ++ CodeHeap* tmp_heap = CodeCache::get_code_heap(CodeBlobType::MethodJBoltTmp); ++ ++ ResourceMark rm; ++ if (hot_heap == nullptr) { ++ ls.print_cr("The jbolt hot heap is null."); ++ } else { ++ print_code_heap(ls, hot_heap, "hot"); ++ } ++ if (tmp_heap == nullptr) { ++ ls.print_cr("The jbolt tmp heap is null."); ++ } else { ++ print_code_heap(ls, tmp_heap, "tmp"); ++ } ++ } ++} ++ ++#undef B_TF +\ No newline at end of file +diff --git a/src/hotspot/share/jbolt/jBoltManager.hpp b/src/hotspot/share/jbolt/jBoltManager.hpp +new file mode 100644 +index 000000000..c0e752073 +--- /dev/null ++++ b/src/hotspot/share/jbolt/jBoltManager.hpp +@@ -0,0 +1,335 @@ ++/* ++ * Copyright (c) 2020, 2024, 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_JBOLT_JBOLTMANAGER_HPP ++#define SHARE_JBOLT_JBOLTMANAGER_HPP ++ ++#include "compiler/compileTask.hpp" ++#include "jbolt/jbolt_globals.hpp" ++#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" ++#include "jfr/dcmd/jfrDcmds.hpp" ++#include "memory/allocation.hpp" ++#include "memory/heap.hpp" ++#include "oops/symbol.hpp" ++#include "runtime/handles.hpp" ++#include "utilities/growableArray.hpp" ++#include "utilities/resourceHash.hpp" ++ ++class CompileTask; ++class CompileTaskInfo; ++class Method; ++class Thread; ++ ++enum JBoltErrorCode { ++ JBoltOK = 0, ++ JBoltOrderNULL = 1, ++ JBoltOpenFileError = 2 ++}; ++ ++struct JBoltReorderPhase { ++ static const int Waiting = -1; // JBolt logic is waiting for something to be done. ++ static const int Available = 0; // JBolt logic is not working or is done (can be reordered again now). ++ static const int Collecting = 1; // Collecting methods in the order file (this phase is for two-phase only). ++ static const int Profiling = 2; // JFR is working (this phase is for one-phase only). ++ static const int Reordering = 3; // Recompiling and re-laying. ++ static const int End = 4; // JBolt is not available anymore (for two-phase, or error happened on one-phase). ++}; ++ ++class JBoltMethodKey : public StackObj { ++ Symbol* _klass; ++ Symbol* _name; ++ Symbol* _sig; ++ ++ void inc_ref_cnt() { ++ Symbol* arr[] = { _klass, _name, _sig }; ++ for (int i = 0; i < (int) (sizeof(arr) / sizeof(arr[0])); ++i) { ++ if (arr[i] != nullptr) arr[i]->increment_refcount(); ++ } ++ } ++ ++ void dec_ref_cnt() { ++ Symbol* arr[] = { _klass, _name, _sig }; ++ for (int i = 0; i < (int) (sizeof(arr) / sizeof(arr[0])); ++i) { ++ if (arr[i] != nullptr) arr[i]->decrement_refcount(); ++ } ++ } ++public: ++ ++ JBoltMethodKey(Symbol* klass, Symbol* name, Symbol* sig): _klass(klass), _name(name), _sig(sig) { /* no inc_ref_cnt() here for SymbolTable::new_symbol() */ } ++ JBoltMethodKey(Method* method): _klass(method->method_holder()->name()), _name(method->name()), _sig(method->signature()) { inc_ref_cnt(); } ++ JBoltMethodKey(const JBoltMethodKey& other): _klass(other._klass), _name(other._name), _sig(other._sig) { inc_ref_cnt(); } ++ JBoltMethodKey(): _klass(nullptr), _name(nullptr), _sig(nullptr) {} ++ ~JBoltMethodKey() { dec_ref_cnt(); } ++ ++ JBoltMethodKey& operator = (const JBoltMethodKey& other) { ++ dec_ref_cnt(); ++ _klass = other._klass; ++ _name = other._name; ++ _sig = other._sig; ++ inc_ref_cnt(); ++ return *this; ++ } ++ ++ unsigned hash() const { ++ int v = primitive_hash(_klass); ++ v = v * 31 + primitive_hash(_name); ++ v = v * 31 + primitive_hash(_sig); ++ return v; ++ } ++ bool equals(const JBoltMethodKey& other) const { ++ return _klass == other._klass && _name == other._name && _sig == other._sig; ++ } ++ ++ static unsigned calc_hash(const JBoltMethodKey& k) { ++ return k.hash(); ++ } ++ static bool calc_equals(const JBoltMethodKey& k1, const JBoltMethodKey& k2) { ++ return k1.equals(k2); ++ } ++ ++ Symbol* klass() const { return _klass; } ++ Symbol* name() const { return _name; } ++ Symbol* sig() const { return _sig; } ++}; ++ ++class JBoltMethodValue : public StackObj { ++private: ++ CompileTaskInfo* volatile _comp_info; ++ ++public: ++ JBoltMethodValue(): _comp_info(nullptr) {} ++ ~JBoltMethodValue(); ++ ++ CompileTaskInfo* get_comp_info(); ++ bool set_comp_info(CompileTaskInfo* info); ++ void clear_comp_info_but_not_release(); ++}; ++ ++class CompileTaskInfo : public CHeapObj { ++ Method* const _method; ++ jobject _method_holder; ++ const int _osr_bci; ++ const int _comp_level; ++ const int _comp_reason; ++ Method* const _hot_method; ++ jobject _hot_method_holder; ++ const int _hot_count; ++ ++public: ++ CompileTaskInfo(Method* method, int osr_bci, int comp_level, int comp_reason, Method* hot_method, int hot_cnt); ++ ~CompileTaskInfo(); ++ ++ bool try_select(); ++ ++ Method* method() const { return _method; } ++ int osr_bci() const { return _osr_bci; } ++ int comp_level() const { return _comp_level; } ++ int comp_reason() const { return _comp_reason; } ++ Method* hot_method() const { return _hot_method; } ++ int hot_count() const { return _hot_count; } ++}; ++ ++class JBoltStackFrameKey : public StackObj { ++ InstanceKlass* _klass; ++ traceid _methodid; ++ ++public: ++ JBoltStackFrameKey(InstanceKlass* klass, traceid methodid): _klass(klass), _methodid(methodid) {} ++ JBoltStackFrameKey(const JBoltStackFrameKey& other): _klass(other._klass), _methodid(other._methodid) {} ++ JBoltStackFrameKey(): _klass(NULL), _methodid(0) {} ++ ~JBoltStackFrameKey() { /* nothing to do as _klass is a softcopy of JfrStackFrame::_klass */ } ++ ++ ++ JBoltStackFrameKey& operator = (const JBoltStackFrameKey& other) { ++ _klass = other._klass; ++ _methodid = other._methodid; ++ return *this; ++ } ++ ++ unsigned hash() const { ++ int v = primitive_hash(_klass); ++ v = v * 31 + primitive_hash(_methodid); ++ return v; ++ } ++ ++ bool equals(const JBoltStackFrameKey& other) const { ++ return _klass == other._klass && _methodid == other._methodid; ++ } ++ ++ static unsigned calc_hash(const JBoltStackFrameKey& k) { ++ return k.hash(); ++ } ++ ++ static bool calc_equals(const JBoltStackFrameKey& k1, const JBoltStackFrameKey& k2) { ++ return k1.equals(k2); ++ } ++}; ++ ++class JBoltStackFrameValue : public StackObj { ++private: ++ jobject _method_holder; ++ ++public: ++ JBoltStackFrameValue(jobject method_holder): _method_holder(method_holder) {} ++ ~JBoltStackFrameValue(); ++ ++ jobject get_method_holder(); ++ void clear_method_holder_but_not_release(); ++}; ++ ++class JBoltManager : public AllStatic { ++ friend class JBoltControlThread; ++ ++ typedef ResourceHashtable MethodKeyMap; ++ ++ typedef ResourceHashtable StackFrameKeyMap; ++ ++ static GrowableArray* _hot_methods_sorted; ++ static MethodKeyMap* _hot_methods_vis; ++ static int _reorder_method_threshold_cnt; ++ ++ static volatile int _reorder_phase; ++ static volatile int _reorderable_method_cnt; ++ static Method* volatile _cur_reordering_method; ++ ++ // the CompilerThread to start the new JBoltControlThread ++ static Thread* _start_reordering_thread; ++ ++ static StackFrameKeyMap* _sampled_methods_refs; ++ ++ // when not set JBoltDumpMode or JBoltLoadMode, JBolt will be in one-step auto mode. ++ static bool _auto_mode; ++ ++ // use MethodJBoltHot and MethodJBoltTmp as two semi hot space. ++ // each time restart a schedule, we exchange primary and secondary ++ static volatile int _primary_hot_seg; ++ static volatile int _secondary_hot_seg; ++ ++ static volatile int _gc_should_sweep_code_heaps_now; ++ ++private: ++ // Used in dump mode. ++ static methodHandle lookup_method(InstanceKlass* klass, traceid method_id); ++ static void construct_stacktrace(const JfrStackTrace &stacktrace); ++ ++ // Used in init phase 1. ++ static void check_mode(); ++ static void check_order_file(); ++ static void check_dependency(); ++ static size_t calc_nmethod_size_with_padding(size_t nmethod_size); ++ static size_t calc_segment_size_with_padding(size_t segment_size); ++ static void load_order_file_phase1(int* method_cnt , size_t* total_nmethod_size); ++ static void init_load_mode_phase1(); ++ ++ // Used in init phase 2. ++ static bool parse_method_line_phase2(char* const line, const int len); ++ static bool parse_connected_component_line_phase2(char* const line, const int len); ++ static void load_order_file_phase2(TRAPS); ++ static void init_load_mode_phase2(TRAPS); ++ static void init_dump_mode_phase2(TRAPS); ++ ++ // Used in auto mode. ++ static CodeBlobType primary_hot_seg(); ++ static CodeBlobType secondary_hot_seg(); ++ ++ // Used in auto mode prev_control_schedule ++ static int clear_last_sample_datas(); ++ static void swap_semi_jbolt_segs(); ++ static int clear_manager(); ++ ++ // Used in auto mode control_schedule ++ static void init_auto_transition(size_t* segment_size, TRAPS); ++ ++ // Used in auto mode post_control_schedule ++ static void clear_secondary_hot_seg(TRAPS); ++ ++ // JBolt phases ++ ++ static int reorder_phase(); ++ ++ static bool reorder_phase_available_to_collecting(); ++ static bool reorder_phase_collecting_to_reordering(); ++ ++ static bool reorder_phase_available_to_profiling(); ++ static bool reorder_phase_profiling_to_reordering(); ++ static bool reorder_phase_reordering_to_available(); ++ static bool reorder_phase_profiling_to_available(); ++ static bool reorder_phase_profiling_to_waiting(); ++ static bool reorder_phase_waiting_to_reordering(); ++ static bool reorder_phase_waiting_to_available(); ++ ++ static bool reorder_phase_reordering_to_end(); ++ ++ static Method* cur_reordering_method(); ++ static void set_cur_reordering_method(Method* method); ++ static int inc_reorderable_method_cnt(); ++ ++ // Used in reordering phase. ++ static CompileTask* create_a_task_instance(CompileTaskInfo* cti, methodHandle& method, methodHandle& hot_method, TRAPS); ++ static void check_compiled_result(Method* method, CodeBlobType check_blob_type, TRAPS); ++ static bool enqueue_recompile_task(CompileTaskInfo* cti, methodHandle& method, methodHandle& hot_method, TRAPS); ++ static bool recompile_one(CompileTaskInfo* cti, methodHandle& method, methodHandle& hot_method, TRAPS); ++ ++ static void print_code_heap(outputStream& ls, CodeHeap* heap, const char* name); ++public: ++ static void log_stacktrace(const JfrStackTrace &stacktrace); ++ static void construct_cg_once(); ++ static void dump_order_in_manual(); ++ static JBoltErrorCode dump_order_in_jcmd(const char* filename); ++ ++ static void check_arguments_not_set(); ++ static void init_phase1(); ++ static void init_phase2(TRAPS); ++ static void init_code_heaps(size_t non_nmethod_size, size_t profiled_size, size_t non_profiled_size, size_t cache_size, size_t ps, size_t alignment); ++ ++ static bool auto_mode() { return _auto_mode; } ++ ++ static bool reorder_phase_is_waiting(); ++ static bool reorder_phase_is_available(); ++ static bool reorder_phase_is_collecting(); ++ static bool reorder_phase_is_profiling(); ++ static bool reorder_phase_is_reordering(); ++ static bool reorder_phase_is_profiling_or_waiting(); ++ static bool reorder_phase_is_collecting_or_reordering(); ++ ++ static bool can_reorder_now(); ++ static bool should_reorder_now(); ++ ++ static bool gc_should_sweep_code_heaps_now(); ++ ++ static CodeBlobType calc_code_blob_type(Method* method, CompileTask* task, TRAPS); ++ ++ static void check_start_reordering(TRAPS); ++ static void reorder_all_methods(TRAPS); ++ static void clear_structures(); ++ ++ static void print_code_heaps(); ++}; ++ ++#endif // SHARE_JBOLT_JBOLTMANAGER_HPP +diff --git a/src/hotspot/share/jbolt/jBoltUtils.cpp b/src/hotspot/share/jbolt/jBoltUtils.cpp +new file mode 100644 +index 000000000..e48d3b046 +--- /dev/null ++++ b/src/hotspot/share/jbolt/jBoltUtils.cpp +@@ -0,0 +1,38 @@ ++/* ++ * Copyright (c) 2020, 2024, 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 "jbolt/jBoltUtils.hpp" ++ ++JBoltUtils::MetaDataKeepAliveMark::MetaDataKeepAliveMark(Thread* thread) : _thread(thread), _kept() { ++ assert(thread == Thread::current(), "Must be current thread"); ++ assert(_thread->is_in_live_stack((address)this), "not on stack?"); ++} ++ ++JBoltUtils::MetaDataKeepAliveMark::~MetaDataKeepAliveMark() { ++ for (int i = _kept.length() - 1; i >= 0; --i) { ++ Metadata* md = _kept.at(i); ++ int idx = _thread->metadata_handles()->find_from_end(md); ++ assert(idx != -1, "not in metadata_handles list"); ++ _thread->metadata_handles()->remove_at(idx); ++ } ++} +diff --git a/src/hotspot/share/jbolt/jBoltUtils.hpp b/src/hotspot/share/jbolt/jBoltUtils.hpp +new file mode 100644 +index 000000000..c2da35519 +--- /dev/null ++++ b/src/hotspot/share/jbolt/jBoltUtils.hpp +@@ -0,0 +1,55 @@ ++/* ++ * Copyright (c) 2020, 2024, 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_JBOLT_JBOLTUTILS_HPP ++#define SHARE_JBOLT_JBOLTUTILS_HPP ++ ++#include "memory/allocation.hpp" ++#include "oops/metadata.hpp" ++#include "runtime/thread.hpp" ++#include "utilities/growableArray.hpp" ++ ++class JBoltUtils : public AllStatic { ++public: ++ /** ++ * Keep the metadata alive. ++ * ++ * @see KeepAliveRegistrar ++ * @see methodHandle ++ */ ++ class MetaDataKeepAliveMark : public StackObj { ++ private: ++ Thread* _thread; ++ GrowableArray _kept; ++ ++ public: ++ MetaDataKeepAliveMark(Thread* thread); ++ ~MetaDataKeepAliveMark(); ++ ++ void add(Metadata* md); ++ ++ const GrowableArray& kept() { return _kept; } ++ }; ++}; ++ ++#endif // SHARE_JBOLT_JBOLTUTILS_HPP +diff --git a/src/hotspot/share/jbolt/jBoltUtils.inline.hpp b/src/hotspot/share/jbolt/jBoltUtils.inline.hpp +new file mode 100644 +index 000000000..abd7501ca +--- /dev/null ++++ b/src/hotspot/share/jbolt/jBoltUtils.inline.hpp +@@ -0,0 +1,38 @@ ++/* ++ * Copyright (c) 2020, 2024, 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_JBOLT_JBOLTUTILS_INLINE_HPP ++#define SHARE_JBOLT_JBOLTUTILS_INLINE_HPP ++ ++#include "jbolt/jBoltUtils.hpp" ++ ++// Register a metadata as 'in-use' by the thread. It's fine to register a ++// metadata multiple times (though perhaps inefficient). ++inline void JBoltUtils::MetaDataKeepAliveMark::add(Metadata* md) { ++ assert(md->is_valid(), "obj is valid"); ++ assert(_thread == Thread::current(), "thread must be current"); ++ _kept.push(md); ++ _thread->metadata_handles()->push(md); ++} ++ ++#endif // SHARE_JBOLT_JBOLTUTILS_INLINE_HPP +diff --git a/src/hotspot/share/jbolt/jbolt_globals.hpp b/src/hotspot/share/jbolt/jbolt_globals.hpp +new file mode 100644 +index 000000000..355e79672 +--- /dev/null ++++ b/src/hotspot/share/jbolt/jbolt_globals.hpp +@@ -0,0 +1,62 @@ ++/* ++ * Copyright (c) 2020, 2024, 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_JBOLT_JBOLT_GLOBALS_HPP ++#define SHARE_JBOLT_JBOLT_GLOBALS_HPP ++ ++#include "runtime/globals_shared.hpp" ++ ++#define JBOLT_FLAGS(develop, \ ++ develop_pd, \ ++ product, \ ++ product_pd, \ ++ notproduct, \ ++ range, \ ++ constraint) \ ++ \ ++ product(bool, UseJBolt, false, EXPERIMENTAL, \ ++ "Enable JBolt feature.") \ ++ \ ++ product(bool, JBoltDumpMode, false, EXPERIMENTAL, \ ++ "Trial run of JBolt. Collect profiling and dump it.") \ ++ \ ++ product(bool, JBoltLoadMode, false, EXPERIMENTAL, \ ++ "Second run of JBolt. Load the profiling and reorder nmethods.") \ ++ \ ++ product(ccstr, JBoltOrderFile, NULL, EXPERIMENTAL, \ ++ "The JBolt method order file to dump or load.") \ ++ \ ++ product(intx, JBoltSampleInterval, 600, EXPERIMENTAL, \ ++ "Sample interval(second) of JBolt dump mode" \ ++ "only useful in auto mode.") \ ++ range(0, max_jint) \ ++ \ ++ product(uintx, JBoltCodeHeapSize, 8*M , EXPERIMENTAL, \ ++ "Code heap size of MethodJBoltHot and MethodJBoltTmp heaps.") \ ++ \ ++ ++// end of JBOLT_FLAGS ++ ++DECLARE_FLAGS(JBOLT_FLAGS) ++ ++#endif // SHARE_JBOLT_JBOLT_GLOBALS_HPP +diff --git a/src/hotspot/share/jfr/metadata/metadata.xml b/src/hotspot/share/jfr/metadata/metadata.xml +index 80ea101a7..3045a46e1 100644 +--- a/src/hotspot/share/jfr/metadata/metadata.xml ++++ b/src/hotspot/share/jfr/metadata/metadata.xml +@@ -988,6 +988,8 @@ + + + ++ ++ + + + +diff --git a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp +index ff52b4104..0582f91eb 100644 +--- a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp ++++ b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp +@@ -75,6 +75,9 @@ + #if INCLUDE_SHENANDOAHGC + #include "gc/shenandoah/shenandoahJfrSupport.hpp" + #endif ++#if INCLUDE_JBOLT ++#include "jbolt/jbolt_globals.hpp" ++#endif // INCLUDE_JBOLT + + /** + * JfrPeriodic class +@@ -692,6 +695,10 @@ TRACE_REQUEST_FUNC(CodeCacheConfiguration) { + event.set_nonNMethodSize(NonNMethodCodeHeapSize); + event.set_profiledSize(ProfiledCodeHeapSize); + event.set_nonProfiledSize(NonProfiledCodeHeapSize); ++#if INCLUDE_JBOLT ++ event.set_jboltHotSize(JBoltCodeHeapSize); ++ event.set_jboltTmpSize(JBoltCodeHeapSize); ++#endif + event.set_expansionSize(CodeCacheExpansionSize); + event.set_minBlockLength(CodeCacheMinBlockLength); + event.set_startAddress((u8)CodeCache::low_bound()); +diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp +index bda6acbc8..1270a5c63 100644 +--- a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp ++++ b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp +@@ -46,6 +46,9 @@ + #include "runtime/threadCrashProtection.hpp" + #include "runtime/threadSMR.hpp" + #include "utilities/systemMemoryBarrier.hpp" ++#if INCLUDE_JBOLT ++#include "jbolt/jBoltManager.hpp" ++#endif + + enum JfrSampleType { + NO_SAMPLE = 0, +@@ -273,7 +276,15 @@ bool JfrThreadSampleClosure::sample_thread_in_java(JavaThread* thread, JfrStackF + return false; + } + EventExecutionSample *event = &_events[_added_java - 1]; +- traceid id = JfrStackTraceRepository::add(sampler.stacktrace()); ++ traceid id = 0; ++#if INCLUDE_JBOLT ++ if (UseJBolt && JBoltManager::reorder_phase_is_profiling()) { ++ id = JfrStackTraceRepository::add_jbolt(sampler.stacktrace()); ++ } else ++#endif ++ { ++ id = JfrStackTraceRepository::add(sampler.stacktrace()); ++ } + assert(id != 0, "Stacktrace id should not be 0"); + event->set_stackTrace(id); + return true; +diff --git a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.cpp b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.cpp +index 48db2fd87..a68d6ea22 100644 +--- a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.cpp ++++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.cpp +@@ -62,7 +62,11 @@ JfrStackTrace::JfrStackTrace(JfrStackFrame* frames, u4 max_frames) : + _frames_ownership(false), + _reached_root(false), + _lineno(false), +- _written(false) {} ++ _written(false) ++#if INCLUDE_JBOLT ++ , _hotcount(1) ++#endif ++ {} + + JfrStackTrace::JfrStackTrace(traceid id, const JfrStackTrace& trace, const JfrStackTrace* next) : + _next(next), +@@ -74,7 +78,11 @@ JfrStackTrace::JfrStackTrace(traceid id, const JfrStackTrace& trace, const JfrSt + _frames_ownership(true), + _reached_root(trace._reached_root), + _lineno(trace._lineno), +- _written(false) { ++ _written(false) ++#if INCLUDE_JBOLT ++ , _hotcount(trace._hotcount) ++#endif ++{ + copy_frames(&_frames, trace._nr_of_frames, trace._frames); + } + +diff --git a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.hpp b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.hpp +index 49e7d7a19..a89b09359 100644 +--- a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.hpp ++++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.hpp +@@ -52,6 +52,17 @@ class JfrStackFrame { + void write(JfrCheckpointWriter& cpw) const; + void resolve_lineno() const; + ++#if INCLUDE_JBOLT ++ const InstanceKlass* get_klass() const { return _klass; } ++ traceid get_methodId() const { return _methodid; } ++ int get_byteCodeIndex() const { return _bci; } ++ u1 get_type() const { return _type; } ++ ++ static ByteSize klass_offset() { return byte_offset_of(JfrStackFrame, _klass ); } ++ static ByteSize methodid_offset() { return byte_offset_of(JfrStackFrame, _methodid ); } ++ static ByteSize bci_offset() { return byte_offset_of(JfrStackFrame, _bci ); } ++ static ByteSize type_offset() { return byte_offset_of(JfrStackFrame, _type ); } ++#endif + enum { + FRAME_INTERPRETER = 0, + FRAME_JIT, +@@ -68,6 +79,10 @@ class JfrStackTrace : public JfrCHeapObj { + friend class ObjectSampler; + friend class OSThreadSampler; + friend class StackTraceResolver; ++#if INCLUDE_JBOLT ++ friend class JBoltManager; ++#endif ++ + private: + const JfrStackTrace* _next; + JfrStackFrame* _frames; +@@ -79,6 +94,9 @@ class JfrStackTrace : public JfrCHeapObj { + bool _reached_root; + mutable bool _lineno; + mutable bool _written; ++#if INCLUDE_JBOLT ++ u4 _hotcount; ++#endif + + const JfrStackTrace* next() const { return _next; } + +@@ -107,6 +125,17 @@ class JfrStackTrace : public JfrCHeapObj { + public: + unsigned int hash() const { return _hash; } + traceid id() const { return _id; } ++#if INCLUDE_JBOLT ++ u4 hotcount() const { return _hotcount; } ++ const JfrStackFrame* get_frames() const { return _frames; } ++ u4 get_framesCount() const { return _nr_of_frames; } ++ ++ static ByteSize hash_offset() { return byte_offset_of(JfrStackTrace, _hash ); } ++ static ByteSize id_offset() { return byte_offset_of(JfrStackTrace, _id ); } ++ static ByteSize hotcount_offset() { return byte_offset_of(JfrStackTrace, _hotcount ); } ++ static ByteSize frames_offset() { return byte_offset_of(JfrStackTrace, _frames ); } ++ static ByteSize frames_count_offset() { return byte_offset_of(JfrStackTrace, _nr_of_frames ); } ++#endif + }; + + #endif // SHARE_JFR_RECORDER_STACKTRACE_JFRSTACKTRACE_HPP +diff --git a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp +index 7caada5ab..250535803 100644 +--- a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp ++++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp +@@ -29,6 +29,9 @@ + #include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" + #include "jfr/support/jfrThreadLocal.hpp" + #include "runtime/mutexLocker.hpp" ++#if INCLUDE_JBOLT ++#include "jbolt/jBoltManager.hpp" ++#endif + + /* + * There are two separate repository instances. +@@ -51,9 +54,16 @@ static JfrStackTraceRepository& leak_profiler_instance() { + return *_leak_profiler_instance; + } + ++#if INCLUDE_JBOLT ++JfrStackTraceRepository::JfrStackTraceRepository() : _last_entries(0), _entries(0), _last_entries_jbolt(0), _entries_jbolt(0) { ++ memset(_table, 0, sizeof(_table)); ++ memset(_table_jbolt, 0, sizeof(_table_jbolt)); ++} ++#else + JfrStackTraceRepository::JfrStackTraceRepository() : _last_entries(0), _entries(0) { + memset(_table, 0, sizeof(_table)); + } ++#endif + + JfrStackTraceRepository* JfrStackTraceRepository::create() { + assert(_instance == nullptr, "invariant"); +@@ -98,6 +108,11 @@ bool JfrStackTraceRepository::is_modified() const { + } + + size_t JfrStackTraceRepository::write(JfrChunkWriter& sw, bool clear) { ++#if INCLUDE_JBOLT ++ if (clear && (UseJBolt && JBoltManager::reorder_phase_is_profiling_or_waiting())) { ++ JBoltManager::construct_cg_once(); ++ } ++#endif + MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag); + if (_entries == 0) { + return 0; +@@ -120,6 +135,21 @@ size_t JfrStackTraceRepository::write(JfrChunkWriter& sw, bool clear) { + if (clear) { + memset(_table, 0, sizeof(_table)); + _entries = 0; ++#if INCLUDE_JBOLT ++ for (u4 i = 0; i < TABLE_SIZE; ++i) { ++ JfrStackTrace* stacktrace = _table_jbolt[i]; ++ while (stacktrace != NULL) { ++ JfrStackTrace* next = const_cast(stacktrace->next()); ++ delete stacktrace; ++ stacktrace = next; ++ } ++ } ++ memset(_table_jbolt, 0, sizeof(_table_jbolt)); ++ _entries_jbolt = 0; ++ } ++ _last_entries_jbolt = _entries_jbolt; ++ { ++#endif + } + _last_entries = _entries; + return count; +@@ -142,6 +172,21 @@ size_t JfrStackTraceRepository::clear(JfrStackTraceRepository& repo) { + const size_t processed = repo._entries; + repo._entries = 0; + repo._last_entries = 0; ++#if INCLUDE_JBOLT ++ if (repo._entries_jbolt != 0) { ++ for (u4 i = 0; i < TABLE_SIZE; ++i) { ++ JfrStackTrace* stacktrace = repo._table_jbolt[i]; ++ while (stacktrace != NULL) { ++ JfrStackTrace* next = const_cast(stacktrace->next()); ++ delete stacktrace; ++ stacktrace = next; ++ } ++ } ++ memset(repo._table_jbolt, 0, sizeof(repo._table_jbolt)); ++ repo._entries_jbolt = 0; ++ repo._last_entries_jbolt = 0; ++ } ++#endif + return processed; + } + +@@ -233,6 +278,75 @@ const JfrStackTrace* JfrStackTraceRepository::lookup_for_leak_profiler(unsigned + return trace; + } + ++#if INCLUDE_JBOLT ++size_t JfrStackTraceRepository::clear_jbolt(JfrStackTraceRepository& repo) { ++ MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag); ++ if (repo._entries_jbolt == 0) { ++ return 0; ++ } ++ ++ for (u4 i = 0; i < TABLE_SIZE; ++i) { ++ JfrStackTrace* stacktrace = repo._table_jbolt[i]; ++ while (stacktrace != NULL) { ++ JfrStackTrace* next = const_cast(stacktrace->next()); ++ delete stacktrace; ++ stacktrace = next; ++ } ++ } ++ memset(repo._table_jbolt, 0, sizeof(repo._table_jbolt)); ++ const size_t processed = repo._entries; ++ repo._entries_jbolt = 0; ++ repo._last_entries_jbolt = 0; ++ ++ return processed; ++} ++ ++size_t JfrStackTraceRepository::clear_jbolt() { ++ clear_jbolt(leak_profiler_instance()); ++ return clear_jbolt(instance()); ++} ++ ++traceid JfrStackTraceRepository::add_jbolt(JfrStackTraceRepository& repo, const JfrStackTrace& stacktrace) { ++ traceid tid = repo.add_trace_jbolt(stacktrace); ++ if (tid == 0) { ++ stacktrace.resolve_linenos(); ++ tid = repo.add_trace_jbolt(stacktrace); ++ } ++ assert(tid != 0, "invariant"); ++ return tid; ++} ++ ++traceid JfrStackTraceRepository::add_jbolt(const JfrStackTrace& stacktrace) { ++ JBoltManager::log_stacktrace(stacktrace); ++ return add_jbolt(instance(), stacktrace); ++} ++ ++traceid JfrStackTraceRepository::add_trace_jbolt(const JfrStackTrace& stacktrace) { ++ traceid id = add_trace(stacktrace); ++ MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag); ++ const size_t index = stacktrace._hash % TABLE_SIZE; ++ ++ if (UseJBolt && JBoltManager::reorder_phase_is_profiling()) { ++ const JfrStackTrace* table_jbolt_entry = _table_jbolt[index]; ++ while (table_jbolt_entry != NULL) { ++ if (table_jbolt_entry->equals(stacktrace)) { ++ // [jbolt]: each time add an old trace, inc its hotcount ++ const_cast(table_jbolt_entry)->_hotcount++; ++ return table_jbolt_entry->id(); ++ } ++ table_jbolt_entry = table_jbolt_entry->next(); ++ } ++ } ++ ++ if (id != 0 && UseJBolt && JBoltManager::reorder_phase_is_profiling()) { ++ _table_jbolt[index] = new JfrStackTrace(id, stacktrace, _table_jbolt[index]); ++ ++_entries_jbolt; ++ } ++ ++ return id; ++} ++#endif ++ + void JfrStackTraceRepository::clear_leak_profiler() { + clear(leak_profiler_instance()); + } +diff --git a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp +index ba0f966ed..dbedb947f 100644 +--- a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp ++++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp +@@ -42,6 +42,9 @@ class JfrStackTraceRepository : public JfrCHeapObj { + friend class RecordStackTrace; + friend class StackTraceBlobInstaller; + friend class StackTraceRepository; ++#if INCLUDE_JBOLT ++ friend class JBoltManager; ++#endif + + private: + static const u4 TABLE_SIZE = 2053; +@@ -49,6 +52,19 @@ class JfrStackTraceRepository : public JfrCHeapObj { + u4 _last_entries; + u4 _entries; + ++#if INCLUDE_JBOLT ++ // [jbolt]: an exclusive table for jbolt. It should be a subset of _table ++ JfrStackTrace* _table_jbolt[TABLE_SIZE]; ++ u4 _last_entries_jbolt; ++ u4 _entries_jbolt; ++ ++ static size_t clear_jbolt(); ++ static size_t clear_jbolt(JfrStackTraceRepository& repo); ++ traceid add_trace_jbolt(const JfrStackTrace& stacktrace); ++ static traceid add_jbolt(JfrStackTraceRepository& repo, const JfrStackTrace& stacktrace); ++ static traceid add_jbolt(const JfrStackTrace& stacktrace); ++#endif ++ + JfrStackTraceRepository(); + static JfrStackTraceRepository& instance(); + static JfrStackTraceRepository* create(); +@@ -71,6 +87,13 @@ class JfrStackTraceRepository : public JfrCHeapObj { + + public: + static traceid record(Thread* current_thread, int skip = 0); ++#if INCLUDE_JBOLT ++ const JfrStackTrace* const * get_stacktrace_table() const { return _table; } ++ u4 get_entries_count() const { return _entries; } ++ ++ const JfrStackTrace* const * get_stacktrace_table_jbolt() const { return _table_jbolt; } ++ u4 get_entries_count_jbolt() const { return _entries_jbolt; } ++#endif + }; + + #endif // SHARE_JFR_RECORDER_STACKTRACE_JFRSTACKTRACEREPOSITORY_HPP +diff --git a/src/hotspot/share/logging/logTag.hpp b/src/hotspot/share/logging/logTag.hpp +index 99adff484..4ea67e3ff 100644 +--- a/src/hotspot/share/logging/logTag.hpp ++++ b/src/hotspot/share/logging/logTag.hpp +@@ -98,6 +98,7 @@ class outputStream; + LOG_TAG(install) \ + LOG_TAG(interpreter) \ + LOG_TAG(itables) \ ++ JBOLT_ONLY(LOG_TAG(jbolt)) \ + LOG_TAG(jfr) \ + LOG_TAG(jit) \ + LOG_TAG(jni) \ +diff --git a/src/hotspot/share/opto/doCall.cpp b/src/hotspot/share/opto/doCall.cpp +index c852316f8..9a0d64a33 100644 +--- a/src/hotspot/share/opto/doCall.cpp ++++ b/src/hotspot/share/opto/doCall.cpp +@@ -1053,8 +1053,8 @@ void Parse::catch_inline_exceptions(SafePointNode* ex_map) { + + #ifndef PRODUCT + void Parse::count_compiled_calls(bool at_method_entry, bool is_inline) { +- if( CountCompiledCalls ) { +- if( at_method_entry ) { ++ if(CountCompiledCalls) { ++ if(at_method_entry) { + // bump invocation counter if top method (for statistics) + if (CountCompiledCalls && depth() == 1) { + const TypePtr* addr_type = TypeMetadataPtr::make(method()); +diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp +index 4d71e552e..6e8fb2e67 100644 +--- a/src/hotspot/share/opto/parse1.cpp ++++ b/src/hotspot/share/opto/parse1.cpp +@@ -1216,7 +1216,7 @@ void Parse::do_method_entry() { + set_parse_bci(InvocationEntryBci); // Pseudo-BCP + set_sp(0); // Java Stack Pointer + +- NOT_PRODUCT( count_compiled_calls(true/*at_method_entry*/, false/*is_inline*/); ) ++ NOT_PRODUCT(count_compiled_calls(true/* at_method_entry */, false/* is_inline */);) + + if (C->env()->dtrace_method_probes()) { + make_dtrace_method_entry(method()); +diff --git a/src/hotspot/share/runtime/flags/allFlags.hpp b/src/hotspot/share/runtime/flags/allFlags.hpp +index 03a51891e..19292dcb2 100644 +--- a/src/hotspot/share/runtime/flags/allFlags.hpp ++++ b/src/hotspot/share/runtime/flags/allFlags.hpp +@@ -31,6 +31,9 @@ + #include "gc/shared/tlab_globals.hpp" + #include "runtime/flags/debug_globals.hpp" + #include "runtime/globals.hpp" ++#if INCLUDE_JBOLT ++#include "jbolt/jbolt_globals.hpp" ++#endif // INCLUDE_JBOLT + + // Put LP64/ARCH/JVMCI/COMPILER1/COMPILER2 at the top, + // as they are processed by jvmFlag.cpp in that order. +@@ -148,7 +151,17 @@ + product_pd, \ + notproduct, \ + range, \ +- constraint) ++ constraint) \ ++ \ ++ JBOLT_ONLY( \ ++ JBOLT_FLAGS( \ ++ develop, \ ++ develop_pd, \ ++ product, \ ++ product_pd, \ ++ notproduct, \ ++ range, \ ++ constraint)) + + #define ALL_CONSTRAINTS(f) \ + COMPILER_CONSTRAINTS(f) \ +diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp +index a92a1c5d2..52551446c 100644 +--- a/src/hotspot/share/runtime/java.cpp ++++ b/src/hotspot/share/runtime/java.cpp +@@ -97,6 +97,9 @@ + #if INCLUDE_JVMCI + #include "jvmci/jvmci.hpp" + #endif ++#if INCLUDE_JBOLT ++#include "jbolt/jBoltManager.hpp" ++#endif + + GrowableArray* collected_profiled_methods; + +@@ -522,6 +525,12 @@ void before_exit(JavaThread* thread, bool halt) { + // Note: we don't wait until it actually dies. + os::terminate_signal_thread(); + ++#if INCLUDE_JBOLT ++ if (UseJBolt && JBoltDumpMode) { ++ JBoltManager::dump_order_in_manual(); ++ } ++#endif ++ + print_statistics(); + Universe::heap()->print_tracing_info(); + +diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp +index ac435317d..8b7058405 100644 +--- a/src/hotspot/share/runtime/threads.cpp ++++ b/src/hotspot/share/runtime/threads.cpp +@@ -113,6 +113,10 @@ + #if INCLUDE_JFR + #include "jfr/jfr.hpp" + #endif ++#if INCLUDE_JBOLT ++#include "jbolt/jBoltDcmds.hpp" ++#include "jbolt/jBoltManager.hpp" ++#endif // INCLUDE_JBOLT + + // Initialization after module runtime initialization + void universe_post_module_init(); // must happen after call_initPhase2 +@@ -547,6 +551,14 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { + ObjectMonitor::Initialize(); + ObjectSynchronizer::initialize(); + ++#if INCLUDE_JBOLT ++ if (UseJBolt) { ++ JBoltManager::init_phase1(); ++ } else { ++ JBoltManager::check_arguments_not_set(); ++ } ++#endif // INCLUDE_JBOLT ++ + // Initialize global modules + jint status = init_globals(); + if (status != JNI_OK) { +@@ -818,6 +830,13 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { + ShouldNotReachHere(); + } + ++#if INCLUDE_JBOLT ++ register_jbolt_dcmds(); ++ if (UseJBolt) { ++ JBoltManager::init_phase2(CATCH); ++ } ++#endif // INCLUDE_JBOLT ++ + return JNI_OK; + } + +diff --git a/src/hotspot/share/utilities/growableArray.hpp b/src/hotspot/share/utilities/growableArray.hpp +index 63dbf668e..4731624cf 100644 +--- a/src/hotspot/share/utilities/growableArray.hpp ++++ b/src/hotspot/share/utilities/growableArray.hpp +@@ -31,6 +31,9 @@ + #include "utilities/globalDefinitions.hpp" + #include "utilities/ostream.hpp" + #include "utilities/powerOfTwo.hpp" ++#if INCLUDE_JBOLT ++#include "utilities/sizes.hpp" ++#endif // INCLUDE_JBOLT + + // A growable array. + +@@ -126,6 +129,10 @@ protected: + public: + const static GrowableArrayView EMPTY; + ++#if INCLUDE_JBOLT ++ static ByteSize data_offset() { return byte_offset_of(GrowableArrayView, _data); } ++#endif // INCLUDE_JBOLT ++ + bool operator==(const GrowableArrayView& rhs) const { + if (_len != rhs._len) + return false; +diff --git a/src/hotspot/share/utilities/macros.hpp b/src/hotspot/share/utilities/macros.hpp +index 244b18ecd..8526d95fb 100644 +--- a/src/hotspot/share/utilities/macros.hpp ++++ b/src/hotspot/share/utilities/macros.hpp +@@ -129,6 +129,18 @@ + #define NOT_CDS_RETURN_(code) { return code; } + #endif // INCLUDE_CDS + ++#ifndef INCLUDE_JBOLT ++#define INCLUDE_JBOLT 1 ++#endif ++ ++#if INCLUDE_JBOLT ++#define JBOLT_ONLY(x) x ++#define NOT_JBOLT(x) ++#else ++#define JBOLT_ONLY(x) ++#define NOT_JBOLT(x) x ++#endif // INCLUDE_JBOLT ++ + #ifndef INCLUDE_MANAGEMENT + #define INCLUDE_MANAGEMENT 1 + #endif // INCLUDE_MANAGEMENT +@@ -263,6 +275,9 @@ + #define JFR_ONLY(code) + #define NOT_JFR_RETURN() {} + #define NOT_JFR_RETURN_(code) { return code; } ++#if INCLUDE_JBOLT ++#define INCLUDE_JBOLT 0 // INCLUDE_JBOLT depends on INCLUDE_JFR ++#endif + #endif + + #ifndef INCLUDE_JVMCI +diff --git a/test/hotspot/jtreg/compiler/codecache/cli/common/CodeCacheCLITestCase.java b/test/hotspot/jtreg/compiler/codecache/cli/common/CodeCacheCLITestCase.java +index eca5c70e0..39f633361 100644 +--- a/test/hotspot/jtreg/compiler/codecache/cli/common/CodeCacheCLITestCase.java ++++ b/test/hotspot/jtreg/compiler/codecache/cli/common/CodeCacheCLITestCase.java +@@ -68,12 +68,12 @@ public class CodeCacheCLITestCase { + * Verifies that in interpreted mode PrintCodeCache output contains + * the whole code cache. Int mode disables SegmentedCodeCache with a warning. + */ +- INT_MODE(ONLY_SEGMENTED, EnumSet.of(BlobType.All), USE_INT_MODE), ++ INT_MODE(ONLY_SEGMENTED, EnumSet.copyOf(CodeCacheOptions.NON_SEGMENTED_HEAPS), USE_INT_MODE), + /** + * Verifies that with disabled SegmentedCodeCache PrintCodeCache output + * contains only CodeCache's entry. + */ +- NON_SEGMENTED(options -> !options.segmented, EnumSet.of(BlobType.All), ++ NON_SEGMENTED(options -> !options.segmented, EnumSet.copyOf(CodeCacheOptions.NON_SEGMENTED_HEAPS), + CommandLineOptionTest.prepareBooleanFlag(SEGMENTED_CODE_CACHE, + false)), + /** +@@ -82,7 +82,7 @@ public class CodeCacheCLITestCase { + * profiled-nmethods heap and non-segmented CodeCache. + */ + NON_TIERED(ONLY_SEGMENTED, +- EnumSet.of(BlobType.NonNMethod, BlobType.MethodNonProfiled), ++ EnumSet.copyOf(CodeCacheOptions.SEGMENTED_HEAPS_WO_PROFILED), + CommandLineOptionTest.prepareBooleanFlag(TIERED_COMPILATION, + false)), + /** +@@ -91,7 +91,7 @@ public class CodeCacheCLITestCase { + * heaps only. + */ + TIERED_LEVEL_0(SEGMENTED_SERVER, +- EnumSet.of(BlobType.All), ++ EnumSet.copyOf(CodeCacheOptions.NON_SEGMENTED_HEAPS), + CommandLineOptionTest.prepareBooleanFlag(TIERED_COMPILATION, + true), + CommandLineOptionTest.prepareNumericFlag(TIERED_STOP_AT, 0)), +@@ -101,7 +101,7 @@ public class CodeCacheCLITestCase { + * heaps only. + */ + TIERED_LEVEL_1(SEGMENTED_SERVER, +- EnumSet.of(BlobType.NonNMethod, BlobType.MethodNonProfiled), ++ EnumSet.copyOf(CodeCacheOptions.SEGMENTED_HEAPS_WO_PROFILED), + CommandLineOptionTest.prepareBooleanFlag(TIERED_COMPILATION, + true), + CommandLineOptionTest.prepareNumericFlag(TIERED_STOP_AT, 1)), +@@ -110,7 +110,7 @@ public class CodeCacheCLITestCase { + * contain information about all three code heaps. + */ + TIERED_LEVEL_4(SEGMENTED_SERVER, +- EnumSet.complementOf(EnumSet.of(BlobType.All)), ++ EnumSet.copyOf(CodeCacheOptions.ALL_SEGMENTED_HEAPS), + CommandLineOptionTest.prepareBooleanFlag(TIERED_COMPILATION, + true), + CommandLineOptionTest.prepareNumericFlag(TIERED_STOP_AT, 4)); +diff --git a/test/hotspot/jtreg/compiler/codecache/cli/common/CodeCacheOptions.java b/test/hotspot/jtreg/compiler/codecache/cli/common/CodeCacheOptions.java +index f5243aaa4..1830911a9 100644 +--- a/test/hotspot/jtreg/compiler/codecache/cli/common/CodeCacheOptions.java ++++ b/test/hotspot/jtreg/compiler/codecache/cli/common/CodeCacheOptions.java +@@ -33,20 +33,27 @@ import java.util.List; + public class CodeCacheOptions { + public static final String SEGMENTED_CODE_CACHE = "SegmentedCodeCache"; + +- private static final EnumSet NON_SEGMENTED_HEAPS ++ public static final EnumSet NON_SEGMENTED_HEAPS + = EnumSet.of(BlobType.All); +- private static final EnumSet ALL_SEGMENTED_HEAPS +- = EnumSet.complementOf(NON_SEGMENTED_HEAPS); +- private static final EnumSet SEGMENTED_HEAPS_WO_PROFILED ++ public static final EnumSet JBOLT_HEAPS ++ = EnumSet.of(BlobType.MethodJBoltHot, BlobType.MethodJBoltTmp); ++ public static final EnumSet ALL_SEGMENTED_HEAPS ++ = EnumSet.complementOf(union(NON_SEGMENTED_HEAPS, JBOLT_HEAPS)); ++ public static final EnumSet ALL_SEGMENTED_HEAPS_WITH_JBOLT ++ = union(ALL_SEGMENTED_HEAPS, JBOLT_HEAPS); ++ public static final EnumSet SEGMENTED_HEAPS_WO_PROFILED + = EnumSet.of(BlobType.NonNMethod, BlobType.MethodNonProfiled); +- private static final EnumSet ONLY_NON_METHODS_HEAP ++ public static final EnumSet ONLY_NON_METHODS_HEAP + = EnumSet.of(BlobType.NonNMethod); + + public final long reserved; + public final long nonNmethods; + public final long nonProfiled; + public final long profiled; ++ public final long jboltHot; ++ public final long jboltTmp; + public final boolean segmented; ++ public final boolean useJBolt; + + public static long mB(long val) { + return CodeCacheOptions.kB(val) * 1024L; +@@ -56,12 +63,21 @@ public class CodeCacheOptions { + return val * 1024L; + } + ++ public static > EnumSet union(EnumSet e1, EnumSet e2) { ++ EnumSet res = EnumSet.copyOf(e1); ++ res.addAll(e2); ++ return res; ++ } ++ + public CodeCacheOptions(long reserved) { + this.reserved = reserved; + this.nonNmethods = 0; + this.nonProfiled = 0; + this.profiled = 0; ++ this.jboltHot = 0; ++ this.jboltTmp = 0; + this.segmented = false; ++ this.useJBolt = false; + } + + public CodeCacheOptions(long reserved, long nonNmethods, long nonProfiled, +@@ -70,7 +86,25 @@ public class CodeCacheOptions { + this.nonNmethods = nonNmethods; + this.nonProfiled = nonProfiled; + this.profiled = profiled; ++ this.jboltHot = 0; ++ this.jboltTmp = 0; + this.segmented = true; ++ this.useJBolt = false; ++ } ++ ++ /** ++ * No tests for JBolt yet as the related VM options are experimental now. ++ */ ++ public CodeCacheOptions(long reserved, long nonNmethods, long nonProfiled, ++ long profiled, long jboltHot, long jboltTmp) { ++ this.reserved = reserved; ++ this.nonNmethods = nonNmethods; ++ this.nonProfiled = nonProfiled; ++ this.profiled = profiled; ++ this.jboltHot = jboltHot; ++ this.jboltTmp = jboltTmp; ++ this.segmented = true; ++ this.useJBolt = true; + } + + public long sizeForHeap(BlobType heap) { +@@ -83,6 +117,10 @@ public class CodeCacheOptions { + return this.nonProfiled; + case MethodProfiled: + return this.profiled; ++ case MethodJBoltHot: ++ return this.jboltHot; ++ case MethodJBoltTmp: ++ return this.jboltTmp; + default: + throw new Error("Unknown heap: " + heap.name()); + } +@@ -107,14 +145,26 @@ public class CodeCacheOptions { + CommandLineOptionTest.prepareNumericFlag( + BlobType.MethodProfiled.sizeOptionName, profiled)); + } ++ ++ if (useJBolt) { ++ Collections.addAll(options, ++ CommandLineOptionTest.prepareNumericFlag( ++ BlobType.MethodJBoltHot.sizeOptionName, jboltHot), ++ CommandLineOptionTest.prepareNumericFlag( ++ BlobType.MethodJBoltTmp.sizeOptionName, jboltTmp)); ++ } ++ + return options.toArray(new String[options.size()]); + } + + public CodeCacheOptions mapOptions(EnumSet involvedCodeHeaps) { + if (involvedCodeHeaps.isEmpty() + || involvedCodeHeaps.equals(NON_SEGMENTED_HEAPS) +- || involvedCodeHeaps.equals(ALL_SEGMENTED_HEAPS)) { ++ || involvedCodeHeaps.equals(ALL_SEGMENTED_HEAPS_WITH_JBOLT)) { + return this; ++ } else if (involvedCodeHeaps.equals(ALL_SEGMENTED_HEAPS)) { ++ return new CodeCacheOptions(reserved, nonNmethods, ++ nonProfiled + jboltHot + jboltTmp, profiled); + } else if (involvedCodeHeaps.equals(SEGMENTED_HEAPS_WO_PROFILED)) { + return new CodeCacheOptions(reserved, nonNmethods, + profiled + nonProfiled, 0L); +diff --git a/test/hotspot/jtreg/compiler/codecache/jbolt/JBoltDumpModeTest.java b/test/hotspot/jtreg/compiler/codecache/jbolt/JBoltDumpModeTest.java +new file mode 100644 +index 000000000..b5bdb19bc +--- /dev/null ++++ b/test/hotspot/jtreg/compiler/codecache/jbolt/JBoltDumpModeTest.java +@@ -0,0 +1,187 @@ ++/* ++ * Copyright (c) 2020, 2024, 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 Test JBolt dump mode functions. ++ * @library /test/lib ++ * @requires vm.flagless ++ * ++ * @run driver compiler.codecache.jbolt.JBoltDumpModeTest ++ */ ++ ++package compiler.codecache.jbolt; ++ ++import java.io.File; ++import java.io.IOException; ++import jdk.test.lib.process.OutputAnalyzer; ++import jdk.test.lib.process.ProcessTools; ++import jdk.test.lib.Utils; ++ ++public class JBoltDumpModeTest { ++ public static final String SRC_DIR = Utils.TEST_SRC; ++ public static final String ORDER_FILE = SRC_DIR + "/order.log"; ++ ++ private static void createOrderFile() { ++ try { ++ File order = new File(ORDER_FILE); ++ if (!order.exists()) { ++ order.createNewFile(); ++ } ++ } ++ catch (IOException e) { ++ e.printStackTrace(); ++ } ++ } ++ ++ private static void clearOrderFile() { ++ File order = new File(ORDER_FILE); ++ if (order.exists()) { ++ order.delete(); ++ } ++ } ++ ++ private static void OrderFileShouldExist() throws Exception { ++ File order = new File(ORDER_FILE); ++ if (order.exists()) { ++ order.delete(); ++ } ++ else { ++ throw new RuntimeException(ORDER_FILE + " doesn't exist as expect."); ++ } ++ } ++ ++ private static void OrderFileShouldNotExist() throws Exception { ++ File order = new File(ORDER_FILE); ++ if (order.exists()) { ++ throw new RuntimeException(ORDER_FILE + " exists while expect not."); ++ } ++ } ++ ++ private static void testNormalUse() throws Exception { ++ ProcessBuilder pb1 = ProcessTools.createJavaProcessBuilder( ++ "-XX:+UnlockExperimentalVMOptions", ++ "-XX:+UseJBolt", ++ "-XX:JBoltOrderFile=" + ORDER_FILE, ++ "-XX:+JBoltDumpMode", ++ "-Xlog:jbolt*=trace", ++ "--version" ++ ); ++ ++ ProcessBuilder pb2 = ProcessTools.createJavaProcessBuilder( ++ "-XX:+UnlockExperimentalVMOptions", ++ "-XX:+UseJBolt", ++ "-XX:JBoltOrderFile=" + ORDER_FILE, ++ "-XX:+JBoltDumpMode", ++ "-XX:StartFlightRecording", ++ "-Xlog:jbolt*=trace", ++ "--version" ++ ); ++ ++ ProcessBuilder pb3 = ProcessTools.createJavaProcessBuilder( ++ "-XX:+UnlockExperimentalVMOptions", ++ "-XX:+UseJBolt", ++ "-XX:JBoltOrderFile=" + ORDER_FILE, ++ "-XX:+JBoltDumpMode", ++ "-Xlog:jbolt*=trace", ++ "--version" ++ ); ++ ++ clearOrderFile(); ++ ++ String stdout; ++ ++ OutputAnalyzer out1 = new OutputAnalyzer(pb1.start()); ++ stdout = out1.getStdout(); ++ if (!stdout.contains("JBolt in dump mode now, start a JFR recording named \"jbolt-jfr\".")) { ++ throw new RuntimeException(stdout); ++ } ++ out1.shouldHaveExitValue(0); ++ OrderFileShouldExist(); ++ ++ OutputAnalyzer out2 = new OutputAnalyzer(pb2.start()); ++ stdout = out2.getStdout(); ++ if (!stdout.contains("JBolt in dump mode now, start a JFR recording named \"jbolt-jfr\".")) { ++ throw new RuntimeException(stdout); ++ } ++ out2.shouldHaveExitValue(0); ++ OrderFileShouldExist(); ++ ++ createOrderFile(); ++ OutputAnalyzer out3 = new OutputAnalyzer(pb3.start()); ++ stdout = out3.getStdout(); ++ if (!stdout.contains("JBoltOrderFile to dump already exists and will be overwritten:")) { ++ throw new RuntimeException(stdout); ++ } ++ out3.shouldHaveExitValue(0); ++ OrderFileShouldExist(); ++ } ++ ++ private static void testUnabletoRun() throws Exception { ++ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( ++ "-XX:+UnlockExperimentalVMOptions", ++ "-XX:+UseJBolt", ++ "-XX:JBoltOrderFile=" + ORDER_FILE, ++ "-XX:+JBoltDumpMode", ++ "-XX:-FlightRecorder", ++ "-Xlog:jbolt*=trace", ++ "--version" ++ ); ++ ++ clearOrderFile(); ++ ++ String stdout; ++ OutputAnalyzer out = new OutputAnalyzer(pb.start()); ++ ++ stdout = out.getStdout(); ++ if (!stdout.contains("JBolt depends on JFR!")) { ++ throw new RuntimeException(stdout); ++ } ++ OrderFileShouldNotExist(); ++ } ++ ++ private static void testFatalError() throws Exception { ++ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( ++ "-XX:+UnlockExperimentalVMOptions", ++ "-XX:+UseJBolt", ++ "-XX:JBoltOrderFile=" + ORDER_FILE, ++ "-XX:+JBoltDumpMode", ++ "-XX:foo", ++ "-Xlog:jbolt*=trace", ++ "--version" ++ ); ++ ++ clearOrderFile(); ++ ++ OutputAnalyzer out = new OutputAnalyzer(pb.start()); ++ ++ out.stderrShouldContain("Could not create the Java Virtual Machine"); ++ OrderFileShouldNotExist(); ++ } ++ ++ public static void main(String[] args) throws Exception { ++ testNormalUse(); ++ testUnabletoRun(); ++ testFatalError(); ++ } ++} +\ No newline at end of file +diff --git a/test/hotspot/jtreg/compiler/codecache/jbolt/JBoltVMOptionsTest.java b/test/hotspot/jtreg/compiler/codecache/jbolt/JBoltVMOptionsTest.java +new file mode 100644 +index 000000000..4b45a585b +--- /dev/null ++++ b/test/hotspot/jtreg/compiler/codecache/jbolt/JBoltVMOptionsTest.java +@@ -0,0 +1,291 @@ ++/* ++ * Copyright (c) 2020, 2024, 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 Test JBolt VM options. ++ * @library /test/lib ++ * @requires vm.flagless ++ * ++ * @run driver compiler.codecache.jbolt.JBoltVMOptionsTest ++ */ ++ ++package compiler.codecache.jbolt; ++ ++import java.io.File; ++import jdk.test.lib.process.OutputAnalyzer; ++import jdk.test.lib.process.ProcessTools; ++import jdk.test.lib.Utils; ++ ++public class JBoltVMOptionsTest { ++ public static final String SRC_DIR = Utils.TEST_SRC; ++ public static final String TEMP_FILE = SRC_DIR + "/tmp.log"; ++ ++ public static void main(String[] args) throws Exception { ++ test1(); ++ test2(); ++ test3(); ++ test4(); ++ } ++ ++ private static void clearTmpFile() { ++ File tmp = new File(TEMP_FILE); ++ if (tmp.exists()) { ++ tmp.delete(); ++ } ++ } ++ ++ private static void test1() throws Exception { ++ ProcessBuilder pb1 = ProcessTools.createJavaProcessBuilder( ++ "-XX:+UnlockExperimentalVMOptions", ++ "-XX:+UseJBolt", ++ "-XX:+JBoltDumpMode", ++ "-Xlog:jbolt*=trace", ++ "--version" ++ ); ++ ProcessBuilder pb2 = ProcessTools.createJavaProcessBuilder( ++ "-XX:+UnlockExperimentalVMOptions", ++ "-XX:+UseJBolt", ++ "-XX:+JBoltLoadMode", ++ "-Xlog:jbolt*=trace", ++ "--version" ++ ); ++ ProcessBuilder pb3 = ProcessTools.createJavaProcessBuilder( ++ "-XX:+UnlockExperimentalVMOptions", ++ "-XX:+UseJBolt", ++ "-XX:+JBoltLoadMode", ++ "-XX:+JBoltDumpMode", ++ "-XX:JBoltOrderFile=" + SRC_DIR + "/o1.log", ++ "-Xlog:jbolt*=trace", ++ "--version" ++ ); ++ ProcessBuilder pb4 = ProcessTools.createJavaProcessBuilder( ++ "-XX:+UnlockExperimentalVMOptions", ++ "-XX:+UseJBolt", ++ "-XX:JBoltOrderFile=" + TEMP_FILE, ++ "-Xlog:jbolt*=trace", ++ "--version" ++ ); ++ ++ OutputAnalyzer out1 = new OutputAnalyzer(pb1.start()); ++ OutputAnalyzer out2 = new OutputAnalyzer(pb2.start()); ++ OutputAnalyzer out3 = new OutputAnalyzer(pb3.start()); ++ OutputAnalyzer out4 = new OutputAnalyzer(pb4.start()); ++ ++ String stdout; ++ ++ stdout = out1.getStdout(); ++ if (!stdout.contains("JBoltOrderFile is not set!")) { ++ throw new RuntimeException(stdout); ++ } ++ ++ stdout = out2.getStdout(); ++ if (!stdout.contains("JBoltOrderFile is not set!")) { ++ throw new RuntimeException(stdout); ++ } ++ ++ stdout = out3.getStdout(); ++ if (!stdout.contains("Do not set both JBoltDumpMode and JBoltLoadMode!")) { ++ throw new RuntimeException(stdout); ++ } ++ ++ stdout = out4.getStdout(); ++ if (!stdout.contains("JBoltOrderFile is ignored because it is in auto mode.")) { ++ throw new RuntimeException(stdout); ++ } ++ } ++ ++ private static void test2() throws Exception { ++ ProcessBuilder pb1 = ProcessTools.createJavaProcessBuilder( ++ "-XX:+UnlockExperimentalVMOptions", ++ "-XX:+UseJBolt", ++ "-XX:+PrintFlagsFinal", ++ "-Xlog:jbolt*=trace", ++ "--version" ++ ); ++ ProcessBuilder pb2 = ProcessTools.createJavaProcessBuilder( ++ "-XX:+UnlockExperimentalVMOptions", ++ "-XX:+UseJBolt", ++ "-XX:+JBoltDumpMode", ++ "-XX:JBoltOrderFile=" + TEMP_FILE, ++ "-XX:+PrintFlagsFinal", ++ "-Xlog:jbolt*=trace", ++ "--version" ++ ); ++ ProcessBuilder pb3 = ProcessTools.createJavaProcessBuilder( ++ "-XX:+UnlockExperimentalVMOptions", ++ "-XX:+UseJBolt", ++ "-XX:+JBoltLoadMode", ++ "-XX:JBoltOrderFile=" + SRC_DIR + "/o1.log", ++ "-XX:+PrintFlagsFinal", ++ "-Xlog:jbolt*=trace", ++ "--version" ++ ); ++ ++ OutputAnalyzer out1 = new OutputAnalyzer(pb1.start()); ++ OutputAnalyzer out2 = new OutputAnalyzer(pb2.start()); ++ OutputAnalyzer out3 = new OutputAnalyzer(pb3.start()); ++ ++ String stdout; ++ ++ stdout = out1.getStdout().replaceAll(" +", ""); ++ if (!stdout.contains("JBoltDumpMode=false") || !stdout.contains("JBoltLoadMode=false")) { ++ throw new RuntimeException(stdout); ++ } ++ ++ stdout = out2.getStdout().replaceAll(" +", ""); ++ if (!stdout.contains("JBoltDumpMode=true") || !stdout.contains("JBoltLoadMode=false")) { ++ throw new RuntimeException(stdout); ++ } ++ ++ clearTmpFile(); ++ ++ stdout = out3.getStdout().replaceAll(" +", ""); ++ if (!stdout.contains("JBoltDumpMode=false") || !stdout.contains("JBoltLoadMode=true")) { ++ throw new RuntimeException(stdout); ++ } ++ } ++ ++ private static void test3() throws Exception { ++ ProcessBuilder pbF0 = ProcessTools.createJavaProcessBuilder( ++ "-XX:+UnlockExperimentalVMOptions", ++ "-XX:+UseJBolt", ++ "-XX:+JBoltLoadMode", ++ "-XX:JBoltOrderFile=" + TEMP_FILE, ++ "-Xlog:jbolt*=trace", ++ "--version" ++ ); ++ ProcessBuilder pbF1 = ProcessTools.createJavaProcessBuilder( ++ "-XX:+UnlockExperimentalVMOptions", ++ "-XX:+UseJBolt", ++ "-XX:+JBoltLoadMode", ++ "-XX:JBoltOrderFile=" + SRC_DIR + "/o1.log", ++ "-Xlog:jbolt*=trace", ++ "--version" ++ ); ++ ProcessBuilder pbF2 = ProcessTools.createJavaProcessBuilder( ++ "-XX:+UnlockExperimentalVMOptions", ++ "-XX:+UseJBolt", ++ "-XX:+JBoltLoadMode", ++ "-XX:JBoltOrderFile=" + SRC_DIR + "/o2.log", ++ "-Xlog:jbolt*=trace", ++ "--version" ++ ); ++ ProcessBuilder pbF3 = ProcessTools.createJavaProcessBuilder( ++ "-XX:+UnlockExperimentalVMOptions", ++ "-XX:+UseJBolt", ++ "-XX:+JBoltLoadMode", ++ "-XX:JBoltOrderFile=" + SRC_DIR + "/o3.log", ++ "-Xlog:jbolt*=trace", ++ "--version" ++ ); ++ ProcessBuilder pbF4 = ProcessTools.createJavaProcessBuilder( ++ "-XX:+UnlockExperimentalVMOptions", ++ "-XX:+UseJBolt", ++ "-XX:+JBoltLoadMode", ++ "-XX:JBoltOrderFile=" + SRC_DIR + "/o4.log", ++ "-Xlog:jbolt*=trace", ++ "--version" ++ ); ++ ++ OutputAnalyzer outF0 = new OutputAnalyzer(pbF0.start()); ++ OutputAnalyzer outF1 = new OutputAnalyzer(pbF1.start()); ++ OutputAnalyzer outF2 = new OutputAnalyzer(pbF2.start()); ++ OutputAnalyzer outF3 = new OutputAnalyzer(pbF3.start()); ++ OutputAnalyzer outF4 = new OutputAnalyzer(pbF4.start()); ++ ++ String stdout; ++ ++ stdout = outF0.getStdout(); ++ if (!stdout.contains("JBoltOrderFile does not exist or cannot be accessed!")) { ++ throw new RuntimeException(stdout); ++ } ++ ++ stdout = outF1.getStdout(); ++ if (!stdout.contains("Wrong format of JBolt order line! line=\"X 123 aa bb cc\".")) { ++ throw new RuntimeException(stdout); ++ } ++ ++ stdout = outF2.getStdout(); ++ if (!stdout.contains("Wrong format of JBolt order line! line=\"M aa/bb/C dd ()V\".")) { ++ throw new RuntimeException(stdout); ++ } ++ ++ stdout = outF3.getStdout(); ++ if (!stdout.contains("Duplicated method: {aa/bb/CC dd ()V}!")) { ++ throw new RuntimeException(stdout); ++ } ++ ++ stdout = outF4.getStdout(); ++ if (stdout.contains("Error occurred during initialization of VM")) { ++ throw new RuntimeException(stdout); ++ } ++ outF4.shouldHaveExitValue(0); ++ ++ clearTmpFile(); ++ } ++ ++ private static void test4() throws Exception { ++ ProcessBuilder pb1 = ProcessTools.createJavaProcessBuilder( ++ "-XX:+UnlockExperimentalVMOptions", ++ "-XX:+JBoltDumpMode", ++ "-Xlog:jbolt*=trace", ++ "--version" ++ ); ++ ProcessBuilder pb2 = ProcessTools.createJavaProcessBuilder( ++ "-XX:+UnlockExperimentalVMOptions", ++ "-XX:+JBoltLoadMode", ++ "-Xlog:jbolt*=trace", ++ "--version" ++ ); ++ ProcessBuilder pb3 = ProcessTools.createJavaProcessBuilder( ++ "-XX:+UnlockExperimentalVMOptions", ++ "-XX:JBoltOrderFile=" + TEMP_FILE, ++ "-Xlog:jbolt*=trace", ++ "--version" ++ ); ++ ++ OutputAnalyzer out1 = new OutputAnalyzer(pb1.start()); ++ OutputAnalyzer out2 = new OutputAnalyzer(pb2.start()); ++ OutputAnalyzer out3 = new OutputAnalyzer(pb3.start()); ++ ++ String stdout; ++ ++ stdout = out1.getStdout(); ++ if (!stdout.contains("Do not set VM option JBoltDumpMode without UseJBolt enabled.")) { ++ throw new RuntimeException(stdout); ++ } ++ ++ stdout = out2.getStdout(); ++ if (!stdout.contains("Do not set VM option JBoltLoadMode without UseJBolt enabled.")) { ++ throw new RuntimeException(stdout); ++ } ++ ++ stdout = out3.getStdout(); ++ if (!stdout.contains("Do not set VM option JBoltOrderFile without UseJBolt enabled.")) { ++ throw new RuntimeException(stdout); ++ } ++ ++ clearTmpFile(); ++ } ++} +diff --git a/test/hotspot/jtreg/compiler/codecache/jbolt/o1.log b/test/hotspot/jtreg/compiler/codecache/jbolt/o1.log +new file mode 100644 +index 000000000..f0ef01586 +--- /dev/null ++++ b/test/hotspot/jtreg/compiler/codecache/jbolt/o1.log +@@ -0,0 +1,2 @@ ++M 123 aa/bb/C dd ()V ++X 123 aa bb cc +diff --git a/test/hotspot/jtreg/compiler/codecache/jbolt/o2.log b/test/hotspot/jtreg/compiler/codecache/jbolt/o2.log +new file mode 100644 +index 000000000..ef348a6ab +--- /dev/null ++++ b/test/hotspot/jtreg/compiler/codecache/jbolt/o2.log +@@ -0,0 +1,2 @@ ++M aa/bb/C dd ()V ++M 123 aa/bb/CC dd ()V +\ No newline at end of file +diff --git a/test/hotspot/jtreg/compiler/codecache/jbolt/o3.log b/test/hotspot/jtreg/compiler/codecache/jbolt/o3.log +new file mode 100644 +index 000000000..fe6906b47 +--- /dev/null ++++ b/test/hotspot/jtreg/compiler/codecache/jbolt/o3.log +@@ -0,0 +1,4 @@ ++# this is a comment ++C ++M 123 aa/bb/CC dd ()V ++M 123 aa/bb/CC dd ()V +\ No newline at end of file +diff --git a/test/hotspot/jtreg/compiler/codecache/jbolt/o4.log b/test/hotspot/jtreg/compiler/codecache/jbolt/o4.log +new file mode 100644 +index 000000000..13e96dbab +--- /dev/null ++++ b/test/hotspot/jtreg/compiler/codecache/jbolt/o4.log +@@ -0,0 +1,12 @@ ++M 123 aa/bb/CC dd ()V ++# asdfadsfadfs ++C ++M 456 aa/bb/CC ddd ()V ++M 456 aa/bb/CCC dd ()V ++ ++C ++ ++ ++ ++ ++M 456 aa/bb/CCCCCC ddddddd ()V +diff --git a/test/hotspot/jtreg/runtime/cds/appcds/ClassLoaderTest.java b/test/hotspot/jtreg/runtime/cds/appcds/ClassLoaderTest.java +index 4fba6584f..6f4cc5a83 100644 +--- a/test/hotspot/jtreg/runtime/cds/appcds/ClassLoaderTest.java ++++ b/test/hotspot/jtreg/runtime/cds/appcds/ClassLoaderTest.java +@@ -57,7 +57,7 @@ public class ClassLoaderTest { + String bootClassPath = "-Xbootclasspath/a:" + appJar + + File.pathSeparator + whiteBoxJar; + +- TestCommon.dump(appJar, appClasses, bootClassPath); ++ TestCommon.dump(appJar, appClasses, bootClassPath).shouldHaveExitValue(0); + + TestCommon.run( + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", +diff --git a/test/lib/jdk/test/whitebox/code/BlobType.java b/test/lib/jdk/test/whitebox/code/BlobType.java +index 24ce9d96a..59039bbbe 100644 +--- a/test/lib/jdk/test/whitebox/code/BlobType.java ++++ b/test/lib/jdk/test/whitebox/code/BlobType.java +@@ -46,8 +46,24 @@ public enum BlobType { + || type == BlobType.MethodNonProfiled; + } + }, ++ // Execution hot non-profiled nmethods ++ MethodJBoltHot(2, "CodeHeap 'jbolt hot nmethods'", "JBoltCodeHeapSize") { ++ @Override ++ public boolean allowTypeWhenOverflow(BlobType type) { ++ return super.allowTypeWhenOverflow(type) ++ || type == BlobType.MethodNonProfiled; ++ } ++ }, ++ // Execution tmp non-profiled nmethods ++ MethodJBoltTmp(3, "CodeHeap 'jbolt tmp nmethods'", "JBoltCodeHeapSize") { ++ @Override ++ public boolean allowTypeWhenOverflow(BlobType type) { ++ return super.allowTypeWhenOverflow(type) ++ || type == BlobType.MethodNonProfiled; ++ } ++ }, + // Non-nmethods like Buffers, Adapters and Runtime Stubs +- NonNMethod(2, "CodeHeap 'non-nmethods'", "NonNMethodCodeHeapSize") { ++ NonNMethod(4, "CodeHeap 'non-nmethods'", "NonNMethodCodeHeapSize") { + @Override + public boolean allowTypeWhenOverflow(BlobType type) { + return super.allowTypeWhenOverflow(type) +@@ -56,7 +72,7 @@ public enum BlobType { + } + }, + // All types (No code cache segmentation) +- All(3, "CodeCache", "ReservedCodeCacheSize"); ++ All(5, "CodeCache", "ReservedCodeCacheSize"); + + public final int id; + public final String sizeOptionName; +@@ -99,6 +115,10 @@ public enum BlobType { + // there is no MethodProfiled in non tiered world or pure C1 + result.remove(MethodProfiled); + } ++ if (!whiteBox.getBooleanVMFlag("UseJBolt") || whiteBox.getBooleanVMFlag("JBoltDumpMode")) { ++ result.remove(MethodJBoltHot); ++ result.remove(MethodJBoltTmp); ++ } + return result; + } + +-- +2.47.0.windows.2 + diff --git a/huawei-Adapt-to-clang-build-toolchain.patch b/huawei-Adapt-to-clang-build-toolchain.patch new file mode 100644 index 0000000000000000000000000000000000000000..6e8bbd0f13e464cec24dd1c833d03d21c0b86e23 --- /dev/null +++ b/huawei-Adapt-to-clang-build-toolchain.patch @@ -0,0 +1,616 @@ +Date: Fri, 21 Mar 2025 15:22:01 +0800 +Subject: Adapt to clang build toolchain + +--- + make/autoconf/flags-cflags.m4 | 9 ++++++ + make/autoconf/flags-ldflags.m4 | 8 +++-- + make/autoconf/flags-other.m4 | 2 +- + make/autoconf/lib-std.m4 | 6 +++- + make/autoconf/toolchain.m4 | 6 +++- + make/common/NativeCompilation.gmk | 13 ++++++-- + src/hotspot/cpu/arm/icache_arm.cpp | 2 +- + src/hotspot/cpu/riscv/vm_version_riscv.cpp | 2 -- + src/hotspot/cpu/s390/vm_version_s390.cpp | 8 +++++ + src/hotspot/cpu/x86/vm_version_x86.cpp | 9 +++--- + src/hotspot/os/linux/os_linux.cpp | 1 - + src/hotspot/os/linux/os_linux.hpp | 2 +- + src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp | 13 ++++++-- + src/hotspot/share/code/codeBlob.hpp | 3 +- + src/hotspot/share/jbolt/jBoltManager.cpp | 4 +-- + src/hotspot/share/prims/unsafe.cpp | 3 +- + src/hotspot/share/services/heapDumper.cpp | 6 ++-- + .../hotspot/utilities/HeapHprofBinWriter.java | 6 ++-- + .../share/classes/sun/tools/jmap/JMap.java | 2 +- + .../unix/native/libsctp/SctpChannelImpl.c | 2 +- + .../codecache/jbolt/JBoltDumpModeTest.java | 10 +++---- + .../codecache/jbolt/JBoltVMOptionsTest.java | 30 +++++++++---------- + 22 files changed, 93 insertions(+), 54 deletions(-) + +diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4 +index ffbf61ea5..891455dee 100644 +--- a/make/autoconf/flags-cflags.m4 ++++ b/make/autoconf/flags-cflags.m4 +@@ -599,6 +599,7 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_HELPER], + # Restrict the debug information created by Clang to avoid + # too big object files and speed the build up a little bit + # (see http://llvm.org/bugs/show_bug.cgi?id=7554) ++ TOOLCHAIN_CFLAGS_JVM_ARM="$TOOLCHAIN_CFLAGS_JVM" + TOOLCHAIN_CFLAGS_JVM="$TOOLCHAIN_CFLAGS_JVM -flimit-debug-info" + + # In principle the stack alignment below is cpu- and ABI-dependent and +@@ -618,6 +619,10 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_HELPER], + TOOLCHAIN_CFLAGS_JDK_CONLY="-fno-strict-aliasing" # technically NOT for CXX + fi + ++ if test "x$OPENJDK_TARGET_CPU" = xarm; then ++ TOOLCHAIN_CFLAGS_JVM="$TOOLCHAIN_CFLAGS_JVM_ARM -mno-omit-leaf-frame-pointer" ++ fi ++ + elif test "x$TOOLCHAIN_TYPE" = xxlc; then + # Suggested additions: -qsrcmsg to get improved error reporting + # set -qtbtable=full for a better traceback table/better stacks in hs_err when xlc16 is used +@@ -834,6 +839,10 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_CPU_DEP], + if test "x$FLAGS_CPU_ARCH" != xarm && test "x$FLAGS_CPU_ARCH" != xppc; then + # for all archs except arm and ppc, prevent gcc to omit frame pointer + $1_CFLAGS_CPU_JDK="${$1_CFLAGS_CPU_JDK} -fno-omit-frame-pointer" ++ elif test "x$FLAGS_CPU_ARCH" = xarm; then ++ # -Wno-psabi to get rid of annoying "note: the mangling of 'va_list' has changed in GCC 4.4" ++ $1_CFLAGS_CPU="-fsigned-char -Wno-psabi $ARM_ARCH_TYPE_FLAGS $ARM_FLOAT_TYPE_FLAGS -DJDK_ARCH_ABI_PROP_NAME='\"\$(JDK_ARCH_ABI_PROP_NAME)\"'" ++ $1_CFLAGS_CPU_JVM="-DARM" + fi + if test "x$FLAGS_CPU" = xppc64le; then + # Little endian machine uses ELFv2 ABI. +diff --git a/make/autoconf/flags-ldflags.m4 b/make/autoconf/flags-ldflags.m4 +index e5c345fde..397aee925 100644 +--- a/make/autoconf/flags-ldflags.m4 ++++ b/make/autoconf/flags-ldflags.m4 +@@ -73,8 +73,12 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER], + BASIC_LDFLAGS_JVM_ONLY="" + + elif test "x$TOOLCHAIN_TYPE" = xclang; then +- BASIC_LDFLAGS_JVM_ONLY="-mno-omit-leaf-frame-pointer -mstack-alignment=16 \ ++ if test "x$OPENJDK_TARGET_CPU" = xarm; then ++ BASIC_LDFLAGS_JVM_ONLY="-mno-omit-leaf-frame-pointer -fPIC" ++ else ++ BASIC_LDFLAGS_JVM_ONLY="-mno-omit-leaf-frame-pointer -mstack-alignment=16 \ + -fPIC" ++ fi + if test "x$OPENJDK_TARGET_OS" = xaix; then + BASIC_LDFLAGS="-Wl,-b64 -Wl,-brtl -Wl,-bnorwexec -Wl,-bnolibpath -Wl,-bnoexpall \ + -Wl,-bernotok -Wl,-bdatapsize:64k -Wl,-btextpsize:64k -Wl,-bstackpsize:64k" +@@ -171,7 +175,7 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_CPU_DEP], + [ + # Setup CPU-dependent basic LDFLAGS. These can differ between the target and + # build toolchain. +- if test "x$TOOLCHAIN_TYPE" = xgcc; then ++ if test "x$TOOLCHAIN_TYPE" = xgcc || test "x$TOOLCHAIN_TYPE" = xclang; then + if test "x${OPENJDK_$1_CPU}" = xx86; then + $1_CPU_LDFLAGS_JVM_ONLY="-march=i586" + elif test "x$OPENJDK_$1_CPU" = xarm; then +diff --git a/make/autoconf/flags-other.m4 b/make/autoconf/flags-other.m4 +index 0af7c02cf..044dfb4a0 100644 +--- a/make/autoconf/flags-other.m4 ++++ b/make/autoconf/flags-other.m4 +@@ -136,7 +136,7 @@ AC_DEFUN([FLAGS_SETUP_ASFLAGS_CPU_DEP], + $2JVM_ASFLAGS="$JVM_BASIC_ASFLAGS ${$2EXTRA_CFLAGS}" + + if test "x$1" = "xTARGET" && \ +- test "x$TOOLCHAIN_TYPE" = xgcc && \ ++ (test "x$TOOLCHAIN_TYPE" = xgcc || test "x$TOOLCHAIN_TYPE" = xclang) && \ + test "x$OPENJDK_TARGET_CPU" = xarm; then + $2JVM_ASFLAGS="${$2JVM_ASFLAGS} $ARM_ARCH_TYPE_ASFLAGS $ARM_FLOAT_TYPE_ASFLAGS" + fi +diff --git a/make/autoconf/lib-std.m4 b/make/autoconf/lib-std.m4 +index e470cdbe6..c7fb81386 100644 +--- a/make/autoconf/lib-std.m4 ++++ b/make/autoconf/lib-std.m4 +@@ -47,7 +47,11 @@ AC_DEFUN_ONCE([LIB_SETUP_STD_LIBS], + if test "x$OPENJDK_TARGET_OS" = xlinux; then + # Test if stdc++ can be linked statically. + AC_MSG_CHECKING([if static link of stdc++ is possible]) +- STATIC_STDCXX_FLAGS="-static-libstdc++ -static-libgcc" ++ if test "x$TOOLCHAIN_TYPE" = xclang && test "x$FLAGS_CPU_ARCH" = xarm; then ++ STATIC_STDCXX_FLAGS="" ++ else ++ STATIC_STDCXX_FLAGS="-static-libstdc++ -static-libgcc" ++ fi + AC_LANG_PUSH(C++) + OLD_LIBS="$LIBS" + LIBS="$STATIC_STDCXX_FLAGS" +diff --git a/make/autoconf/toolchain.m4 b/make/autoconf/toolchain.m4 +index 069ced05f..b7ae22c00 100644 +--- a/make/autoconf/toolchain.m4 ++++ b/make/autoconf/toolchain.m4 +@@ -483,7 +483,11 @@ AC_DEFUN([TOOLCHAIN_EXTRACT_COMPILER_VERSION], + if test $? -ne 0; then + AC_MSG_NOTICE([The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler.]) + AC_MSG_NOTICE([The result from running with --version was: "$COMPILER_VERSION_OUTPUT"]) +- AC_MSG_ERROR([A $TOOLCHAIN_TYPE compiler is required. Try setting --with-tools-dir.]) ++ if test "x$OPENJDK_TARGET_CPU" = xarm; then ++ AC_MSG_NOTICE([A $TOOLCHAIN_TYPE compiler is required. Try setting --with-tools-dir.]) ++ else ++ AC_MSG_ERROR([A $TOOLCHAIN_TYPE compiler is required. Try setting --with-tools-dir.]) ++ fi + fi + # Collapse compiler output into a single line + COMPILER_VERSION_STRING=`$ECHO $COMPILER_VERSION_OUTPUT` +diff --git a/make/common/NativeCompilation.gmk b/make/common/NativeCompilation.gmk +index 3b96222e4..fbf2f62e0 100644 +--- a/make/common/NativeCompilation.gmk ++++ b/make/common/NativeCompilation.gmk +@@ -795,6 +795,10 @@ define SetupNativeCompilationBody + ifeq ($$($1_COMPILE_WITH_DEBUG_SYMBOLS), true) + $1_EXTRA_CFLAGS += $$(CFLAGS_DEBUG_SYMBOLS) + $1_EXTRA_CXXFLAGS += $$(CFLAGS_DEBUG_SYMBOLS) ++ ifeq ($(findstring $(OPENJDK_TARGET_CPU), arm), ) ++ $1_EXTRA_CFLAGS := $(filter-out -gdwarf-aranges,$(1_EXTRA_CFLAGS)) ++ $1_EXTRA_CXXFLAGS := $(filter-out -gdwarf-aranges,$(1_EXTRA_CXXFLAGS)) ++ endif + $1_EXTRA_ASFLAGS += $$(ASFLAGS_DEBUG_SYMBOLS) + endif + +@@ -900,8 +904,13 @@ define SetupNativeCompilationBody + $1_PCH_FILE := $$($1_OBJECT_DIR)/precompiled/$$(notdir $$($1_PRECOMPILED_HEADER)).gch + $1_USE_PCH_FLAGS := -I$$($1_OBJECT_DIR)/precompiled + else ifeq ($(TOOLCHAIN_TYPE), clang) +- $1_PCH_FILE := $$($1_OBJECT_DIR)/precompiled/$$(notdir $$($1_PRECOMPILED_HEADER)).pch +- $1_USE_PCH_FLAGS := -include-pch $$($1_PCH_FILE) ++ ifeq ($(findstring $(OPENJDK_TARGET_CPU), arm), ) ++ $1_PCH_FILE := $$($1_OBJECT_DIR)/precompiled/$$(notdir $$($1_PRECOMPILED_HEADER)).gch ++ $1_USE_PCH_FLAGS := -I$$($1_OBJECT_DIR)/precompiled ++ else ++ $1_PCH_FILE := $$($1_OBJECT_DIR)/precompiled/$$(notdir $$($1_PRECOMPILED_HEADER)).pch ++ $1_USE_PCH_FLAGS := -include-pch $$($1_PCH_FILE) ++ endif + endif + $1_PCH_DEPS_FILE := $$($1_PCH_FILE).d + $1_PCH_DEPS_TARGETS_FILE := $$($1_PCH_FILE).d.targets +diff --git a/src/hotspot/cpu/arm/icache_arm.cpp b/src/hotspot/cpu/arm/icache_arm.cpp +index 61fcb8a35..9054b536c 100644 +--- a/src/hotspot/cpu/arm/icache_arm.cpp ++++ b/src/hotspot/cpu/arm/icache_arm.cpp +@@ -31,7 +31,7 @@ + + + static int icache_flush(address addr, int lines, int magic) { +- __builtin___clear_cache(addr, addr + (lines << ICache::log2_line_size)); ++ __builtin___clear_cache((char *)addr, (char *)addr + (lines << ICache::log2_line_size)); + return magic; + } + +diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.cpp b/src/hotspot/cpu/riscv/vm_version_riscv.cpp +index a740b0301..10fde8688 100644 +--- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp ++++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp +@@ -232,12 +232,10 @@ void VM_Version::initialize() { + FLAG_SET_DEFAULT(UseHashMapIntegerCache, false); + warning("HashMap optimization is not supported in this VM."); + } +- + if (UnlockExperimentalVMOptions && UseFastSerializer && !FLAG_IS_DEFAULT(UseFastSerializer)) { + FLAG_SET_DEFAULT(UseFastSerializer, false); + warning("Serializer optimization is not supported in this VM."); + } +- + #ifdef COMPILER2 + c2_initialize(); + #endif // COMPILER2 +diff --git a/src/hotspot/cpu/s390/vm_version_s390.cpp b/src/hotspot/cpu/s390/vm_version_s390.cpp +index 44a0275e4..12fcb3773 100644 +--- a/src/hotspot/cpu/s390/vm_version_s390.cpp ++++ b/src/hotspot/cpu/s390/vm_version_s390.cpp +@@ -272,6 +272,14 @@ void VM_Version::initialize() { + FLAG_SET_DEFAULT(UseSHA, false); + } + ++ if (UnlockExperimentalVMOptions && UseHashMapIntegerCache && !FLAG_IS_DEFAULT(UseHashMapIntegerCache)) { ++ FLAG_SET_DEFAULT(UseHashMapIntegerCache, false); ++ warning("HashMap optimization is not supported in this VM."); ++ } ++ if (UnlockExperimentalVMOptions && UseFastSerializer && !FLAG_IS_DEFAULT(UseFastSerializer)) { ++ FLAG_SET_DEFAULT(UseFastSerializer, false); ++ warning("Serializer optimization is not supported in this VM."); ++ } + #ifdef COMPILER2 + if (FLAG_IS_DEFAULT(UseMultiplyToLenIntrinsic)) { + FLAG_SET_DEFAULT(UseMultiplyToLenIntrinsic, true); +diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp +index 6f35090cc..b312635b4 100644 +--- a/src/hotspot/cpu/x86/vm_version_x86.cpp ++++ b/src/hotspot/cpu/x86/vm_version_x86.cpp +@@ -1200,15 +1200,14 @@ void VM_Version::get_processor_features() { + } + + #ifdef _LP64 +- if (UnlockExperimentalVMOptions && UseFastSerializer && !FLAG_IS_DEFAULT(UseFastSerializer)) { +- FLAG_SET_DEFAULT(UseFastSerializer, false); +- warning("Serializer optimization is not supported in this VM."); +- } + if (UnlockExperimentalVMOptions && UseHashMapIntegerCache && !FLAG_IS_DEFAULT(UseHashMapIntegerCache)) { + FLAG_SET_DEFAULT(UseHashMapIntegerCache, false); + warning("HashMap optimization is not supported in this VM."); + } +- ++ if (UnlockExperimentalVMOptions && UseFastSerializer && !FLAG_IS_DEFAULT(UseFastSerializer)) { ++ FLAG_SET_DEFAULT(UseFastSerializer, false); ++ warning("Serializer optimization is not supported in this VM."); ++ } + #endif + + #ifdef _LP64 +diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp +index 43ee4b7db..990e38b07 100644 +--- a/src/hotspot/os/linux/os_linux.cpp ++++ b/src/hotspot/os/linux/os_linux.cpp +@@ -135,7 +135,6 @@ + __asm__(".symver fcntl64,fcntl@GLIBC_2.2.5"); + #endif + +- + // if RUSAGE_THREAD for getrusage() has not been defined, do it here. The code calling + // getrusage() is prepared to handle the associated failure. + #ifndef RUSAGE_THREAD +diff --git a/src/hotspot/os/linux/os_linux.hpp b/src/hotspot/os/linux/os_linux.hpp +index 871581f58..d25c2dda0 100644 +--- a/src/hotspot/os/linux/os_linux.hpp ++++ b/src/hotspot/os/linux/os_linux.hpp +@@ -486,7 +486,7 @@ class os::Linux { + return NULL; + } + return _heap_dict_lookup(key, heap_dict, deletable); +- }; ++ } + + static void heap_dict_free(void* heap_dict, bool is_nested) { + if(_heap_dict_free != NULL) { +diff --git a/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp b/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp +index 86e8ed256..375925105 100644 +--- a/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp ++++ b/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp +@@ -90,8 +90,14 @@ enum { + #endif + + address os::current_stack_pointer() { +- register address sp __asm__ (SPELL_REG_SP); +- return sp; ++ #if defined(__clang__) || defined(__llvm__) ++ void *sp; ++ __asm__("mov %0, " SPELL_REG_SP : "=r"(sp)); ++ return (address) sp; ++ #else ++ register address sp __asm__ (SPELL_REG_SP); ++ return sp; ++ #endif + } + + char* os::non_memory_address_word() { +@@ -99,7 +105,7 @@ char* os::non_memory_address_word() { + return (char*) -1; + } + +- ++#ifndef __arm__ + #if NGREG == 16 + // These definitions are based on the observation that until + // the certain version of GCC mcontext_t was defined as +@@ -111,6 +117,7 @@ char* os::non_memory_address_word() { + #define arm_fp gregs[11] + #define arm_r0 gregs[0] + #endif ++#endif + + #define ARM_REGS_IN_CONTEXT 16 + +diff --git a/src/hotspot/share/code/codeBlob.hpp b/src/hotspot/share/code/codeBlob.hpp +index 8392499d3..03e7df9c8 100644 +--- a/src/hotspot/share/code/codeBlob.hpp ++++ b/src/hotspot/share/code/codeBlob.hpp +@@ -48,8 +48,7 @@ enum class CodeBlobType { + MethodJBoltTmp = 3, // Temporary storage of JBolt hot methods + NonNMethod = 4, // Non-nmethods like Buffers, Adapters and Runtime Stubs + All = 5, // All types (No code cache segmentation) +- AOT = 6, // AOT methods +- NumTypes = 7 // Number of CodeBlobTypes ++ NumTypes = 6 // Number of CodeBlobTypes + }; + + // CodeBlob - superclass for all entries in the CodeCache. +diff --git a/src/hotspot/share/jbolt/jBoltManager.cpp b/src/hotspot/share/jbolt/jBoltManager.cpp +index 4cb6f4d1a..76be3f6ed 100644 +--- a/src/hotspot/share/jbolt/jBoltManager.cpp ++++ b/src/hotspot/share/jbolt/jBoltManager.cpp +@@ -947,7 +947,7 @@ bool JBoltManager::reorder_phase_collecting_to_reordering() { + } + + bool JBoltManager::reorder_phase_available_to_profiling() { +- assert(auto_mode(), "one-phase only"); ++ assert(JBoltDumpMode || auto_mode(), "for two-phase dump mode & one-phase"); + return Atomic::cmpxchg(&_reorder_phase, JBoltReorderPhase::Available, JBoltReorderPhase::Profiling) == JBoltReorderPhase::Available; + } + +@@ -1002,7 +1002,7 @@ bool JBoltManager::reorder_phase_is_collecting() { + + bool JBoltManager::reorder_phase_is_profiling() { + bool res = (Atomic::load_acquire(&_reorder_phase) == JBoltReorderPhase::Profiling); +- assert(!res || auto_mode(), "for two-phase dump mode & one-phase"); ++ assert(!res || (JBoltDumpMode || auto_mode()), "for two-phase dump mode & one-phase"); + return res; + } + +diff --git a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp +index 2fd9ed1d0..4eb1c96a6 100644 +--- a/src/hotspot/share/prims/unsafe.cpp ++++ b/src/hotspot/share/prims/unsafe.cpp +@@ -910,11 +910,10 @@ static JNINativeMethod jdk_internal_misc_Unsafe_methods[] = { + {CC "writebackPostSync0", CC "()V", FN_PTR(Unsafe_WriteBackPostSync0)}, + {CC "setMemory0", CC "(" OBJ "JJB)V", FN_PTR(Unsafe_SetMemory0)}, + +- {CC "getUseHashMapIntegerCache", CC "()Z", FN_PTR(Unsafe_GetUseHashMapIntegerCache)} +- , + {CC "shouldBeInitialized0", CC "(" CLS ")Z", FN_PTR(Unsafe_ShouldBeInitialized0)}, + + {CC "fullFence", CC "()V", FN_PTR(Unsafe_FullFence)}, ++ {CC "getUseHashMapIntegerCache", CC "()Z", FN_PTR(Unsafe_GetUseHashMapIntegerCache)}, + {CC "getUseFastSerializer", CC "()Z", FN_PTR(Unsafe_GetUseFastSerializer)}, + }; + +diff --git a/src/hotspot/share/services/heapDumper.cpp b/src/hotspot/share/services/heapDumper.cpp +index 82e721c17..e44ba085b 100644 +--- a/src/hotspot/share/services/heapDumper.cpp ++++ b/src/hotspot/share/services/heapDumper.cpp +@@ -1053,7 +1053,7 @@ public: + int field_count() { return _entries; } + char sig_start(int field_idx) { return _sigs_start.at(field_idx); } + int offset(int field_idx) { return _offsets.at(field_idx); } +- address name_symbol_addrs(int field_idx) { return _name_symbol_addrs.at(field_idx); } ++ address name_symbol_addr(int field_idx) { return _name_symbol_addrs.at(field_idx); } + u4 instance_size() { return _instance_size; } + }; + +@@ -1391,7 +1391,7 @@ void DumperSupport::dump_instance_redact_fields(AbstractDumpWriter* writer, oop + int offset = class_cache_entry->offset(idx); + + ResourceMark rm; +- address field_adr = class_cache_entry->name_symbol_addrs(idx); ++ address field_adr = class_cache_entry->name_symbol_addr(idx); + void* replace_value = writer->heapRedactor()->lookup_value(field_adr, replace_value_table, false); + + if (replace_value != nullptr) { +@@ -1560,7 +1560,7 @@ void DumperSupport::dump_redact_instance(AbstractDumpWriter* writer, oop o, Dump + + DumperClassCacheTableEntry* cache_entry = class_cache->lookup_or_create(ik); + +- u4 is = instance_size(ik); ++ u4 is = instance_size(ik, cache_entry); + u4 size = 1 + sizeof(address) + 4 + sizeof(address) + 4 + is; + + writer->start_sub_record(HPROF_GC_INSTANCE_DUMP, size); +diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java +index 8501f4d93..b8f3e182b 100644 +--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java ++++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java +@@ -1591,11 +1591,11 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + level == HeapRedactor.HeapDumpRedactLevel.REDACT_FULL); + byte[] buf = null; + if(shouldRedact) { +- Optional redactField = lookupRedactName(symbolStr); +- buf = redactField.isPresent() ? redactField.get().getBytes("UTF-8") : null; ++ Optional redactFiled = lookupRedactName(symbolStr); ++ buf = redactFiled.isPresent() ? redactFiled.get().getBytes("UTF-8") : null; + } + if(buf == null) { +- buf = symbolStr.getBytes(UTF_8); ++ buf = symbolStr.getBytes("UTF-8"); + } + writeHeader(HPROF_UTF8, buf.length + OBJ_ID_SIZE); + writeSymbolID(sym); +diff --git a/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java b/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java +index 50757d2d7..a48d27bab 100644 +--- a/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java ++++ b/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java +@@ -343,7 +343,7 @@ public class JMap { + String flag = "RedactPassword"; + try (InputStream in = hvm.printFlag(flag); BufferedInputStream bis = new BufferedInputStream(in); + InputStreamReader isr = new InputStreamReader(bis, "UTF-8")) { +- char c[] = new char[256]; ++ char[] c = new char[256]; + int n; + do { + n = isr.read(c); +diff --git a/src/jdk.sctp/unix/native/libsctp/SctpChannelImpl.c b/src/jdk.sctp/unix/native/libsctp/SctpChannelImpl.c +index 83f3519fb..3720caccd 100644 +--- a/src/jdk.sctp/unix/native/libsctp/SctpChannelImpl.c ++++ b/src/jdk.sctp/unix/native/libsctp/SctpChannelImpl.c +@@ -389,7 +389,7 @@ void handleMessage + (JNIEnv* env, jobject resultContainerObj, struct msghdr* msg,int read, + jboolean isEOR, struct sockaddr* sap) { + jobject isa, resultObj; +- struct controlData cdata[1]; ++ struct controlData cdata[1] = {0}; + + if (read == 0) { + /* we reached EOF */ +diff --git a/test/hotspot/jtreg/compiler/codecache/jbolt/JBoltDumpModeTest.java b/test/hotspot/jtreg/compiler/codecache/jbolt/JBoltDumpModeTest.java +index b5bdb19bc..76a426838 100644 +--- a/test/hotspot/jtreg/compiler/codecache/jbolt/JBoltDumpModeTest.java ++++ b/test/hotspot/jtreg/compiler/codecache/jbolt/JBoltDumpModeTest.java +@@ -79,7 +79,7 @@ public class JBoltDumpModeTest { + } + + private static void testNormalUse() throws Exception { +- ProcessBuilder pb1 = ProcessTools.createJavaProcessBuilder( ++ ProcessBuilder pb1 = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseJBolt", + "-XX:JBoltOrderFile=" + ORDER_FILE, +@@ -88,7 +88,7 @@ public class JBoltDumpModeTest { + "--version" + ); + +- ProcessBuilder pb2 = ProcessTools.createJavaProcessBuilder( ++ ProcessBuilder pb2 = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseJBolt", + "-XX:JBoltOrderFile=" + ORDER_FILE, +@@ -98,7 +98,7 @@ public class JBoltDumpModeTest { + "--version" + ); + +- ProcessBuilder pb3 = ProcessTools.createJavaProcessBuilder( ++ ProcessBuilder pb3 = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseJBolt", + "-XX:JBoltOrderFile=" + ORDER_FILE, +@@ -138,7 +138,7 @@ public class JBoltDumpModeTest { + } + + private static void testUnabletoRun() throws Exception { +- ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( ++ ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseJBolt", + "-XX:JBoltOrderFile=" + ORDER_FILE, +@@ -161,7 +161,7 @@ public class JBoltDumpModeTest { + } + + private static void testFatalError() throws Exception { +- ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( ++ ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseJBolt", + "-XX:JBoltOrderFile=" + ORDER_FILE, +diff --git a/test/hotspot/jtreg/compiler/codecache/jbolt/JBoltVMOptionsTest.java b/test/hotspot/jtreg/compiler/codecache/jbolt/JBoltVMOptionsTest.java +index 4b45a585b..c17e2c13e 100644 +--- a/test/hotspot/jtreg/compiler/codecache/jbolt/JBoltVMOptionsTest.java ++++ b/test/hotspot/jtreg/compiler/codecache/jbolt/JBoltVMOptionsTest.java +@@ -56,21 +56,21 @@ public class JBoltVMOptionsTest { + } + + private static void test1() throws Exception { +- ProcessBuilder pb1 = ProcessTools.createJavaProcessBuilder( ++ ProcessBuilder pb1 = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseJBolt", + "-XX:+JBoltDumpMode", + "-Xlog:jbolt*=trace", + "--version" + ); +- ProcessBuilder pb2 = ProcessTools.createJavaProcessBuilder( ++ ProcessBuilder pb2 = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseJBolt", + "-XX:+JBoltLoadMode", + "-Xlog:jbolt*=trace", + "--version" + ); +- ProcessBuilder pb3 = ProcessTools.createJavaProcessBuilder( ++ ProcessBuilder pb3 = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseJBolt", + "-XX:+JBoltLoadMode", +@@ -79,7 +79,7 @@ public class JBoltVMOptionsTest { + "-Xlog:jbolt*=trace", + "--version" + ); +- ProcessBuilder pb4 = ProcessTools.createJavaProcessBuilder( ++ ProcessBuilder pb4 = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseJBolt", + "-XX:JBoltOrderFile=" + TEMP_FILE, +@@ -116,14 +116,14 @@ public class JBoltVMOptionsTest { + } + + private static void test2() throws Exception { +- ProcessBuilder pb1 = ProcessTools.createJavaProcessBuilder( ++ ProcessBuilder pb1 = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseJBolt", + "-XX:+PrintFlagsFinal", + "-Xlog:jbolt*=trace", + "--version" + ); +- ProcessBuilder pb2 = ProcessTools.createJavaProcessBuilder( ++ ProcessBuilder pb2 = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseJBolt", + "-XX:+JBoltDumpMode", +@@ -132,7 +132,7 @@ public class JBoltVMOptionsTest { + "-Xlog:jbolt*=trace", + "--version" + ); +- ProcessBuilder pb3 = ProcessTools.createJavaProcessBuilder( ++ ProcessBuilder pb3 = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseJBolt", + "-XX:+JBoltLoadMode", +@@ -167,7 +167,7 @@ public class JBoltVMOptionsTest { + } + + private static void test3() throws Exception { +- ProcessBuilder pbF0 = ProcessTools.createJavaProcessBuilder( ++ ProcessBuilder pbF0 = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseJBolt", + "-XX:+JBoltLoadMode", +@@ -175,7 +175,7 @@ public class JBoltVMOptionsTest { + "-Xlog:jbolt*=trace", + "--version" + ); +- ProcessBuilder pbF1 = ProcessTools.createJavaProcessBuilder( ++ ProcessBuilder pbF1 = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseJBolt", + "-XX:+JBoltLoadMode", +@@ -183,7 +183,7 @@ public class JBoltVMOptionsTest { + "-Xlog:jbolt*=trace", + "--version" + ); +- ProcessBuilder pbF2 = ProcessTools.createJavaProcessBuilder( ++ ProcessBuilder pbF2 = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseJBolt", + "-XX:+JBoltLoadMode", +@@ -191,7 +191,7 @@ public class JBoltVMOptionsTest { + "-Xlog:jbolt*=trace", + "--version" + ); +- ProcessBuilder pbF3 = ProcessTools.createJavaProcessBuilder( ++ ProcessBuilder pbF3 = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseJBolt", + "-XX:+JBoltLoadMode", +@@ -199,7 +199,7 @@ public class JBoltVMOptionsTest { + "-Xlog:jbolt*=trace", + "--version" + ); +- ProcessBuilder pbF4 = ProcessTools.createJavaProcessBuilder( ++ ProcessBuilder pbF4 = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseJBolt", + "-XX:+JBoltLoadMode", +@@ -246,19 +246,19 @@ public class JBoltVMOptionsTest { + } + + private static void test4() throws Exception { +- ProcessBuilder pb1 = ProcessTools.createJavaProcessBuilder( ++ ProcessBuilder pb1 = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", + "-XX:+JBoltDumpMode", + "-Xlog:jbolt*=trace", + "--version" + ); +- ProcessBuilder pb2 = ProcessTools.createJavaProcessBuilder( ++ ProcessBuilder pb2 = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", + "-XX:+JBoltLoadMode", + "-Xlog:jbolt*=trace", + "--version" + ); +- ProcessBuilder pb3 = ProcessTools.createJavaProcessBuilder( ++ ProcessBuilder pb3 = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", + "-XX:JBoltOrderFile=" + TEMP_FILE, + "-Xlog:jbolt*=trace", +-- +2.47.0.windows.2 + diff --git a/huawei-Add-Fast-Serialzer.patch b/huawei-Add-Fast-Serialzer.patch new file mode 100644 index 0000000000000000000000000000000000000000..663849432fe5b50e9d838fceabb6e9f77e7f43ae --- /dev/null +++ b/huawei-Add-Fast-Serialzer.patch @@ -0,0 +1,680 @@ +Date: Thu, 13 Mar 2025 14:14:43 +0800 +Subject: Add Fast Serialzer + +--- + src/hotspot/cpu/x86/vm_version_x86.cpp | 7 + + src/hotspot/share/prims/unsafe.cpp | 5 + + src/hotspot/share/runtime/globals.hpp | 4 + + .../classes/java/io/ObjectInputStream.java | 222 +++++++++++++++--- + .../classes/java/io/ObjectOutputStream.java | 83 ++++++- + .../classes/java/io/ObjectStreamClass.java | 81 +++++++ + .../classes/jdk/internal/misc/Unsafe.java | 2 +- + 7 files changed, 362 insertions(+), 42 deletions(-) + +diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp +index b7f5a2bc9..d71d3a93f 100644 +--- a/src/hotspot/cpu/x86/vm_version_x86.cpp ++++ b/src/hotspot/cpu/x86/vm_version_x86.cpp +@@ -1199,6 +1199,13 @@ void VM_Version::get_processor_features() { + FLAG_SET_DEFAULT(UseSHA256Intrinsics, false); + } + ++#ifdef _LP64 ++ if (UnlockExperimentalVMOptions && UseFastSerializer && !FLAG_IS_DEFAULT(UseFastSerializer)) { ++ FLAG_SET_DEFAULT(UseFastSerializer, false); ++ warning("Serializer optimization is not supported in this VM."); ++ } ++#endif ++ + #ifdef _LP64 + // These are only supported on 64-bit + if (UseSHA && supports_avx2() && supports_bmi2()) { +diff --git a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp +index 3de8962c0..921693473 100644 +--- a/src/hotspot/share/prims/unsafe.cpp ++++ b/src/hotspot/share/prims/unsafe.cpp +@@ -825,6 +825,10 @@ UNSAFE_ENTRY(jint, Unsafe_GetLoadAverage0(JNIEnv *env, jobject unsafe, jdoubleAr + return ret; + } UNSAFE_END + ++UNSAFE_ENTRY(jboolean, Unsafe_GetUseFastSerializer(JNIEnv *env, jobject unsafe)) { ++ return UseFastSerializer; ++} ++UNSAFE_END + + /// JVM_RegisterUnsafeMethods + +@@ -904,6 +908,7 @@ static JNINativeMethod jdk_internal_misc_Unsafe_methods[] = { + {CC "shouldBeInitialized0", CC "(" CLS ")Z", FN_PTR(Unsafe_ShouldBeInitialized0)}, + + {CC "fullFence", CC "()V", FN_PTR(Unsafe_FullFence)}, ++ {CC "getUseFastSerializer", CC "()Z", FN_PTR(Unsafe_GetUseFastSerializer)}, + }; + + #undef CC +diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp +index b422d730a..ac33876d8 100644 +--- a/src/hotspot/share/runtime/globals.hpp ++++ b/src/hotspot/share/runtime/globals.hpp +@@ -1959,6 +1959,10 @@ const int ObjectAlignmentInBytes = 8; + JFR_ONLY(product(ccstr, StartFlightRecording, nullptr, \ + "Start flight recording with options")) \ + \ ++ product(bool, UseFastSerializer, false, EXPERIMENTAL, \ ++ "Cache-based serialization.It is extremely fast, but it" \ ++ "can only be effective in certain scenarios.") \ ++ \ + product(bool, UseFastUnorderedTimeStamps, false, EXPERIMENTAL, \ + "Use platform unstable time where supported for timestamps only") \ + \ +diff --git a/src/java.base/share/classes/java/io/ObjectInputStream.java b/src/java.base/share/classes/java/io/ObjectInputStream.java +index 10b7f7715..80b316004 100644 +--- a/src/java.base/share/classes/java/io/ObjectInputStream.java ++++ b/src/java.base/share/classes/java/io/ObjectInputStream.java +@@ -41,6 +41,7 @@ import java.security.PrivilegedExceptionAction; + import java.util.Arrays; + import java.util.Map; + import java.util.Objects; ++import java.util.concurrent.ConcurrentHashMap; + + import jdk.internal.access.SharedSecrets; + import jdk.internal.event.DeserializationEvent; +@@ -329,6 +330,23 @@ public class ObjectInputStream + filterLogger = (filterLog.isLoggable(Logger.Level.DEBUG) + || filterLog.isLoggable(Logger.Level.TRACE)) ? filterLog : null; + } ++ ++ /* ++ * Logger for FastSerializer. ++ * Setup the FastSerializer logger if it is set to DEBUG. ++ * (Assuming it will not change). ++ */ ++ static final System.Logger fastSerLogger; ++ ++ static { ++ if (printFastSerializer) { ++ Logger fastSerLog = System.getLogger("fastSerializer"); ++ fastSerLogger = (fastSerLog.isLoggable(Logger.Level.DEBUG)) ++ ? fastSerLog : null; ++ } else { ++ fastSerLogger = null; ++ } ++ } + } + + /** filter stream for handling block data conversion */ +@@ -354,6 +372,9 @@ public class ObjectInputStream + /** if true, invoke resolveObject() */ + private boolean enableResolve; + ++ /** Used to get the commandline option: useFastSerializer */ ++ private static final Unsafe UNSAFE = Unsafe.getUnsafe(); ++ + /** + * Context during upcalls to class-defined readObject methods; holds + * object currently being deserialized and descriptor for current class. +@@ -367,6 +388,40 @@ public class ObjectInputStream + */ + private ObjectInputFilter serialFilter; + ++ /** ++ * value of "useFastSerializer" property ++ */ ++ private static final boolean defaultFastSerializer = UNSAFE.getUseFastSerializer(); ++ ++ /** ++ * true or false for open FastSerilizer ++ * May be changed in readStreamHeader ++ */ ++ private boolean useFastSerializer = defaultFastSerializer; ++ ++ /** ++ * Value of "fastSerializerEscapeMode" property. It can be turned on ++ * when useFastSerializer is true. ++ */ ++ @SuppressWarnings("removal") ++ private static final boolean fastSerializerEscapeMode = java.security.AccessController.doPrivileged( ++ new sun.security.action.GetBooleanAction( ++ "fastSerializerEscapeMode")).booleanValue(); ++ ++ /** ++ * Magic number that is written to the stream header when using fastserilizer. ++ */ ++ private static final short STREAM_MAGIC_FAST = (short)0xdeca; ++ ++ /** ++ * value of "printFastSerializer" property, ++ * as true or false for printing FastSerializer logs. ++ */ ++ @SuppressWarnings("removal") ++ private static final boolean printFastSerializer = java.security.AccessController.doPrivileged( ++ new sun.security.action.GetBooleanAction( ++ "printFastSerializer")).booleanValue(); ++ + /** + * True if the stream-specific filter has been set; initially false. + */ +@@ -468,6 +523,9 @@ public class ObjectInputStream + * transitively so that a complete equivalent graph of objects is + * reconstructed by readObject. + * ++ * The difference between fastSerialzation and default serialization is the ++ * descriptor serialization. The data serialization is same with each other. ++ * + *

The root object is completely restored when all of its fields and the + * objects it references are completely restored. At this point the object + * validation callbacks are executed in order based on their registered +@@ -757,11 +815,20 @@ public class ObjectInputStream + vlist.register(obj, prio); + } + ++ /** ++ * Cache the class meta during serialization. ++ * Only used in FastSerilizer. ++ */ ++ private static ConcurrentHashMap> nameToClass = new ConcurrentHashMap<>(); ++ + /** + * Load the local class equivalent of the specified stream class + * description. Subclasses may implement this method to allow classes to + * be fetched from an alternate source. + * ++ * When fastSerializer is turned on, fields of desc will be null except ++ * name. When resolveClass is override, this may cause null pointer exception. ++ * + *

The corresponding method in {@code ObjectOutputStream} is + * {@code annotateClass}. This method will be invoked only once for + * each unique class in the stream. This method can be implemented by +@@ -800,16 +867,29 @@ public class ObjectInputStream + throws IOException, ClassNotFoundException + { + String name = desc.getName(); +- try { +- return Class.forName(name, false, latestUserDefinedLoader()); +- } catch (ClassNotFoundException ex) { +- Class cl = primClasses.get(name); ++ Class cl = null; ++ ++ if (useFastSerializer) { ++ cl = nameToClass.get(name); + if (cl != null) { + return cl; +- } else { ++ } ++ } ++ ++ try { ++ cl = Class.forName(name, false, latestUserDefinedLoader()); ++ } catch (ClassNotFoundException ex) { ++ cl = primClasses.get(name); ++ if (cl == null) { + throw ex; + } + } ++ ++ if (useFastSerializer) { ++ nameToClass.put(name, cl); ++ } ++ ++ return cl; + } + + /** +@@ -984,9 +1064,33 @@ public class ObjectInputStream + { + short s0 = bin.readShort(); + short s1 = bin.readShort(); +- if (s0 != STREAM_MAGIC || s1 != STREAM_VERSION) { +- throw new StreamCorruptedException( +- String.format("invalid stream header: %04X%04X", s0, s1)); ++ if (useFastSerializer) { ++ if (s0 != STREAM_MAGIC_FAST || s1 != STREAM_VERSION) { ++ if (s0 != STREAM_MAGIC) { ++ throw new StreamCorruptedException( ++ String.format("invalid stream header: %04X%04X, and FastSerializer is activated", s0, s1)); ++ } ++ ++ if (!fastSerializerEscapeMode) { ++ throw new StreamCorruptedException( ++ String.format("invalid stream header: %04X%04X.Fast serialization does not support " + ++ "original serialized files", s0, s1)); ++ } ++ ++ // Escape to default serialization ++ useFastSerializer = false; ++ if (Logging.fastSerLogger != null) { ++ Logging.fastSerLogger.log(Logger.Level.DEBUG, "[Deserialize]: Escape and disable FastSerializer"); ++ } ++ } ++ } else if (s0 != STREAM_MAGIC || s1 != STREAM_VERSION) { ++ if (s0 == STREAM_MAGIC_FAST && s1 == STREAM_VERSION) { ++ throw new StreamCorruptedException( ++ String.format("invalid stream header: %04X%04X, and it is a FastSerializer stream", s0, s1)); ++ } else { ++ throw new StreamCorruptedException( ++ String.format("invalid stream header: %04X%04X", s0, s1)); ++ } + } + } + +@@ -1000,6 +1104,11 @@ public class ObjectInputStream + * this method reads class descriptors according to the format defined in + * the Object Serialization specification. + * ++ * In fastSerialize mode, the descriptor is obtained by lookup method. And ++ * the resolveClass method is called here to get the classmeta. Since the ++ * descriptor is obtained by lookup, the descriptor is same as localdesc. ++ * So we cann't distinguish the receiver desc and local desc. ++ * + * @return the class descriptor read + * @throws IOException If an I/O error has occurred. + * @throws ClassNotFoundException If the Class of a serialized object used +@@ -1010,6 +1119,29 @@ public class ObjectInputStream + protected ObjectStreamClass readClassDescriptor() + throws IOException, ClassNotFoundException + { ++ // fastSerializer ++ if (useFastSerializer) { ++ String name = readUTF(); ++ Class cl = null; ++ ObjectStreamClass desc = new ObjectStreamClass(name); ++ try { ++ // In order to match this method, we add an annotateClass method in ++ // writeClassDescriptor. ++ cl = resolveClass(desc); ++ } catch (ClassNotFoundException ex) { ++ // resolveClass is just used to obtain Class which required by lookup method ++ // and it will be called again later, so we don't throw ClassNotFoundException here. ++ return desc; ++ } ++ if (cl != null) { ++ // This desc is localDesc. It may be different from the descriptor ++ // obtained from the stream. ++ desc = ObjectStreamClass.lookup(cl, true); ++ } ++ return desc; ++ } ++ ++ // Default deserialization. If the Class cannot be found, throw ClassNotFoundException. + ObjectStreamClass desc = new ObjectStreamClass(); + desc.readNonProxy(this); + return desc; +@@ -2072,41 +2204,63 @@ public class ObjectInputStream + + skipCustomData(); + +- try { +- totalObjectRefs++; +- depth++; +- desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false)); ++ totalObjectRefs++; ++ depth++; + +- if (cl != null) { +- // Check that serial filtering has been done on the local class descriptor's superclass, +- // in case it does not appear in the stream. +- +- // Find the next super descriptor that has a local class descriptor. +- // Descriptors for which there is no local class are ignored. +- ObjectStreamClass superLocal = null; +- for (ObjectStreamClass sDesc = desc.getSuperDesc(); sDesc != null; sDesc = sDesc.getSuperDesc()) { +- if ((superLocal = sDesc.getLocalDesc()) != null) { +- break; ++ if (useFastSerializer) { ++ desc.initNonProxyFast(readDesc, resolveEx); ++ ObjectStreamClass superDesc = desc.getSuperDesc(); ++ long originDepth = depth - 1; ++ // Since desc is obtained from the lookup method, we will lose the depth and ++ // totalObjectRefs of superDesc. So we add a loop here to compute the depth ++ // and objectRef of superDesc. ++ while (superDesc != null && superDesc.forClass() != null) { ++ filterCheck(superDesc.forClass(), -1); ++ superDesc = superDesc.getSuperDesc(); ++ totalObjectRefs++; ++ depth++; ++ } ++ depth = originDepth; ++ } else { ++ try { ++ desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false)); ++ ++ if (cl != null) { ++ // Check that serial filtering has been done on the local class descriptor's superclass, ++ // in case it does not appear in the stream. ++ // Find the next super descriptor that has a local class descriptor. ++ // Descriptors for which there is no local class are ignored. ++ ObjectStreamClass superLocal = null; ++ for (ObjectStreamClass sDesc = desc.getSuperDesc(); sDesc != null; sDesc = sDesc.getSuperDesc()) { ++ if ((superLocal = sDesc.getLocalDesc()) != null) { ++ break; ++ } + } +- } + +- // Scan local descriptor superclasses for a match with the local descriptor of the super found above. +- // For each super descriptor before the match, invoke the serial filter on the class. +- // The filter is invoked for each class that has not already been filtered +- // but would be filtered if the instance had been serialized by this Java runtime. +- for (ObjectStreamClass lDesc = desc.getLocalDesc().getSuperDesc(); +- lDesc != null && lDesc != superLocal; +- lDesc = lDesc.getSuperDesc()) { +- filterCheck(lDesc.forClass(), -1); ++ // Scan local descriptor superclasses for a match with the local descriptor of the super found above. ++ // For each super descriptor before the match, invoke the serial filter on the class. ++ // The filter is invoked for each class that has not already been filtered ++ // but would be filtered if the instance had been serialized by this Java runtime. ++ for (ObjectStreamClass lDesc = desc.getLocalDesc().getSuperDesc(); ++ lDesc != null && lDesc != superLocal; ++ lDesc = lDesc.getSuperDesc()) { ++ filterCheck(lDesc.forClass(), -1); ++ } + } ++ } finally { ++ depth--; + } +- } finally { +- depth--; + } + + handles.finish(descHandle); + passHandle = descHandle; + ++ if (Logging.fastSerLogger != null) { ++ Logging.fastSerLogger.log(Logger.Level.DEBUG, ++ "[Deserialize] useFastSerializer:{0}, Class name:{1}, SerialVersionUID:{2}, flags:{3}", ++ useFastSerializer, desc.getName(), desc.getSerialVersionUID(), desc.getFlags(this)); ++ } ++ + return desc; + } + +@@ -2964,8 +3118,6 @@ public class ObjectInputStream + } + } + +- private static final Unsafe UNSAFE = Unsafe.getUnsafe(); +- + /** + * Performs a "freeze" action, required to adhere to final field semantics. + * +diff --git a/src/java.base/share/classes/java/io/ObjectOutputStream.java b/src/java.base/share/classes/java/io/ObjectOutputStream.java +index eb5407a94..adb24a8dc 100644 +--- a/src/java.base/share/classes/java/io/ObjectOutputStream.java ++++ b/src/java.base/share/classes/java/io/ObjectOutputStream.java +@@ -33,6 +33,7 @@ import java.util.List; + import java.util.Objects; + import java.util.StringJoiner; + ++import jdk.internal.misc.Unsafe; + import jdk.internal.util.ByteArray; + import sun.reflect.misc.ReflectUtil; + +@@ -181,6 +182,25 @@ public class ObjectOutputStream + }; + } + ++ private static class Logging { ++ /* ++ * Logger for FastSerializer. ++ * Setup the FastSerializer logger if it is set to DEBUG. ++ * (Assuming it will not change). ++ */ ++ static final System.Logger fastSerLogger; ++ ++ static { ++ if (printFastSerializer) { ++ System.Logger fastSerLog = System.getLogger("fastSerializer"); ++ fastSerLogger = (fastSerLog.isLoggable(System.Logger.Level.DEBUG)) ++ ? fastSerLog : null; ++ } else { ++ fastSerLogger = null; ++ } ++ } ++ } ++ + /** filter stream for handling block data conversion */ + private final BlockDataOutputStream bout; + /** obj -> wire handle map */ +@@ -199,7 +219,6 @@ public class ObjectOutputStream + private final boolean enableOverride; + /** if true, invoke replaceObject() */ + private boolean enableReplace; +- + // values below valid only during upcalls to writeObject()/writeExternal() + /** + * Context during upcalls to class-defined writeObject methods; holds +@@ -223,6 +242,28 @@ public class ObjectOutputStream + new sun.security.action.GetBooleanAction( + "sun.io.serialization.extendedDebugInfo")).booleanValue(); + ++ private static final Unsafe UNSAFE = Unsafe.getUnsafe(); ++ ++ /** ++ * Value of "UseFastSerializer" property, The fastSerializer is turned ++ * on when it is true. ++ */ ++ private static final boolean useFastSerializer = UNSAFE.getUseFastSerializer(); ++ ++ /** ++ * value of "printFastSerializer" property, ++ * as true or false for printing FastSerializer logs. ++ */ ++ @SuppressWarnings("removal") ++ private static final boolean printFastSerializer = java.security.AccessController.doPrivileged( ++ 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 +@@ -337,6 +378,9 @@ public class ObjectOutputStream + * object are written transitively so that a complete equivalent graph of + * objects can be reconstructed by an ObjectInputStream. + * ++ * The difference between fastSerialzation and default serialization is the ++ * descriptor serialization. The data serialization is same with each other. ++ * + *

Exceptions are thrown for problems with the OutputStream and for + * classes that should not be serialized. All exceptions are fatal to the + * OutputStream, which is left in an indeterminate state, and it is up to +@@ -646,7 +690,11 @@ public class ObjectOutputStream + * stream + */ + protected void writeStreamHeader() throws IOException { +- bout.writeShort(STREAM_MAGIC); ++ if (useFastSerializer) { ++ bout.writeShort(STREAM_MAGIC_FAST); ++ } else { ++ bout.writeShort(STREAM_MAGIC); ++ } + bout.writeShort(STREAM_VERSION); + } + +@@ -662,6 +710,9 @@ public class ObjectOutputStream + * defined in the + * Java Object Serialization Specification. + * ++ * In fastSerializer mode, we will only write the classname to the stream. ++ * The annotateClass is used to match the resolveClass in readClassDescriptor. ++ * + *

Note that this method will only be called if the ObjectOutputStream + * is not using the old serialization stream format (set by calling + * ObjectOutputStream's {@code useProtocolVersion} method). If this +@@ -680,7 +731,14 @@ public class ObjectOutputStream + protected void writeClassDescriptor(ObjectStreamClass desc) + throws IOException + { +- desc.writeNonProxy(this); ++ if (useFastSerializer) { ++ writeUTF(desc.getName()); ++ // The annotateClass is used to match the resolveClass called in ++ // readClassDescriptor. ++ annotateClass(desc.forClass()); ++ } else { ++ desc.writeNonProxy(this); ++ } + } + + /** +@@ -1291,9 +1349,21 @@ public class ObjectOutputStream + bout.writeByte(TC_CLASSDESC); + handles.assign(unshared ? null : desc); + ++ if (Logging.fastSerLogger != null) { ++ Logging.fastSerLogger.log(System.Logger.Level.DEBUG, ++ "[Serialize] useFastSerializer:{0}, Class name:{1}, SerialVersionUID:{2}, flags:{3}, protocol:{4}", ++ useFastSerializer, desc.getName(), desc.getSerialVersionUID(), desc.getFlags(this), protocol); ++ } ++ + if (protocol == PROTOCOL_VERSION_1) { + // do not invoke class descriptor write hook with old protocol +- desc.writeNonProxy(this); ++ if (useFastSerializer) { ++ // only write name and annotate class when using FastSerializer ++ writeUTF(desc.getName()); ++ annotateClass(desc.forClass()); ++ } else { ++ desc.writeNonProxy(this); ++ } + } else { + writeClassDescriptor(desc); + } +@@ -1306,8 +1376,9 @@ public class ObjectOutputStream + annotateClass(cl); + bout.setBlockDataMode(false); + bout.writeByte(TC_ENDBLOCKDATA); +- +- writeClassDesc(desc.getSuperDesc(), false); ++ if (!useFastSerializer) { ++ writeClassDesc(desc.getSuperDesc(), false); ++ } + } + + /** +diff --git a/src/java.base/share/classes/java/io/ObjectStreamClass.java b/src/java.base/share/classes/java/io/ObjectStreamClass.java +index 58841e55a..63aa16a52 100644 +--- a/src/java.base/share/classes/java/io/ObjectStreamClass.java ++++ b/src/java.base/share/classes/java/io/ObjectStreamClass.java +@@ -296,6 +296,40 @@ public final class ObjectStreamClass implements Serializable { + return suid.longValue(); + } + ++ /** ++ * Return the flags for this class described by this descriptor. The flags ++ * means a set of bit masks for ObjectStreamClass, which indicate the status ++ * of SC_WRITE_METHOD, SC_SERIALIZABLE, SC_EXTERNALIZABLE, SC_BLOCK_DATA and ++ * SC_ENUM. ++ * ++ * @param serialStream ObjectOutputStream or ObjectInputStream ++ * ++ * @return the flags for this class described by this descriptor ++ */ ++ byte getFlags(Object serialStream) { ++ byte flags = 0; ++ if (externalizable) { ++ flags |= ObjectStreamConstants.SC_EXTERNALIZABLE; ++ if (serialStream instanceof ObjectOutputStream) { ++ int protocol = ((ObjectOutputStream)serialStream).getProtocolVersion(); ++ if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) { ++ flags |= ObjectStreamConstants.SC_BLOCK_DATA; ++ } ++ } else if (serialStream instanceof ObjectInputStream) { ++ flags |= ObjectStreamConstants.SC_BLOCK_DATA; ++ } ++ } else if (serializable) { ++ flags |= ObjectStreamConstants.SC_SERIALIZABLE; ++ } ++ if (hasWriteObjectData) { ++ flags |= ObjectStreamConstants.SC_WRITE_METHOD; ++ } ++ if (isEnum) { ++ flags |= ObjectStreamConstants.SC_ENUM; ++ } ++ return flags; ++ } ++ + /** + * Return the class in the local VM that this version is mapped to. Null + * is returned if there is no corresponding local class. +@@ -468,6 +502,15 @@ public final class ObjectStreamClass implements Serializable { + ObjectStreamClass() { + } + ++ /** ++ * Create a blank class descriptor with name. It is only used ++ * in fastSerialize path. ++ * @param name class name ++ */ ++ ObjectStreamClass(String name) { ++ this.name = name; ++ } ++ + /** + * Creates a PermissionDomain that grants no permission. + */ +@@ -662,6 +705,44 @@ public final class ObjectStreamClass implements Serializable { + initialized = true; + } + ++ /** ++ * Initializes class descriptor representing a non-proxy class. ++ * Used in fast serialization mode. ++ */ ++ void initNonProxyFast(ObjectStreamClass model, ++ ClassNotFoundException resolveEx) ++ { ++ this.cl = model.cl; ++ this.resolveEx = resolveEx; ++ this.superDesc = model.superDesc; ++ name = model.name; ++ this.suid = model.suid; ++ isProxy = false; ++ isEnum = model.isEnum; ++ serializable = model.serializable; ++ externalizable = model.externalizable; ++ hasBlockExternalData = model.hasBlockExternalData; ++ hasWriteObjectData = model.hasWriteObjectData; ++ fields = model.fields; ++ primDataSize = model.primDataSize; ++ numObjFields = model.numObjFields; ++ ++ writeObjectMethod = model.writeObjectMethod; ++ readObjectMethod = model.readObjectMethod; ++ readObjectNoDataMethod = model.readObjectNoDataMethod; ++ writeReplaceMethod = model.writeReplaceMethod; ++ readResolveMethod = model.readResolveMethod; ++ if (deserializeEx == null) { ++ deserializeEx = model.deserializeEx; ++ } ++ domains = model.domains; ++ cons = model.cons; ++ fieldRefl = model.fieldRefl; ++ localDesc = model; ++ ++ initialized = true; ++ } ++ + /** + * Reads non-proxy class descriptor information from given input stream. + * The resulting class descriptor is not fully functional; it can only be +diff --git a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java +index 47e8bf80c..926d30691 100644 +--- a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java ++++ b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java +@@ -3819,7 +3819,7 @@ public final class Unsafe { + private static long convEndian(boolean big, long n) { return big == BIG_ENDIAN ? n : Long.reverseBytes(n) ; } + + +- ++ public native boolean getUseFastSerializer(); + private native long allocateMemory0(long bytes); + private native long reallocateMemory0(long address, long bytes); + private native void freeMemory0(long address); +-- +2.47.0.windows.2 + diff --git a/huawei-Add-Hashmap-frontCache-opt.patch b/huawei-Add-Hashmap-frontCache-opt.patch new file mode 100644 index 0000000000000000000000000000000000000000..490155223fbff409b9248d047067accf983f7a41 --- /dev/null +++ b/huawei-Add-Hashmap-frontCache-opt.patch @@ -0,0 +1,531 @@ +Date: Thu, 13 Mar 2025 17:43:57 +0800 +Subject: Add Hashmap frontCache opt + +--- + src/hotspot/cpu/arm/vm_version_arm_32.cpp | 10 + + src/hotspot/cpu/riscv/vm_version_riscv.cpp | 10 + + src/hotspot/cpu/s390/vm_version_s390.cpp | 10 + + src/hotspot/cpu/x86/vm_version_x86.cpp | 5 + + src/hotspot/cpu/zero/vm_version_zero.cpp | 10 + + src/hotspot/share/prims/unsafe.cpp | 7 + + src/hotspot/share/runtime/globals.hpp | 5 + + .../share/classes/java/util/HashMap.java | 192 +++++++++++++++++- + .../classes/jdk/internal/misc/Unsafe.java | 2 +- + 9 files changed, 249 insertions(+), 2 deletions(-) + +diff --git a/src/hotspot/cpu/arm/vm_version_arm_32.cpp b/src/hotspot/cpu/arm/vm_version_arm_32.cpp +index 44f2179db..0b4bcdee9 100644 +--- a/src/hotspot/cpu/arm/vm_version_arm_32.cpp ++++ b/src/hotspot/cpu/arm/vm_version_arm_32.cpp +@@ -263,6 +263,16 @@ void VM_Version::initialize() { + FLAG_SET_DEFAULT(UseVectorizedMismatchIntrinsic, false); + } + ++ if (UnlockExperimentalVMOptions && UseHashMapIntegerCache && !FLAG_IS_DEFAULT(UseHashMapIntegerCache)) { ++ FLAG_SET_DEFAULT(UseHashMapIntegerCache, false); ++ warning("HashMap optimization is not supported in this VM."); ++ } ++ ++ if (UnlockExperimentalVMOptions && UseFastSerializer && !FLAG_IS_DEFAULT(UseFastSerializer)) { ++ FLAG_SET_DEFAULT(UseFastSerializer, false); ++ warning("Serializer optimization is not supported in this VM."); ++ } ++ + #ifdef COMPILER2 + // C2 is only supported on v7+ VFP at this time + if (_arm_arch < 7 || !has_vfp()) { +diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.cpp b/src/hotspot/cpu/riscv/vm_version_riscv.cpp +index 4ad0b16b6..a740b0301 100644 +--- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp ++++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp +@@ -228,6 +228,16 @@ void VM_Version::initialize() { + FLAG_SET_DEFAULT(UseBlockZeroing, false); + } + ++ if (UnlockExperimentalVMOptions && UseHashMapIntegerCache && !FLAG_IS_DEFAULT(UseHashMapIntegerCache)) { ++ FLAG_SET_DEFAULT(UseHashMapIntegerCache, false); ++ warning("HashMap optimization is not supported in this VM."); ++ } ++ ++ if (UnlockExperimentalVMOptions && UseFastSerializer && !FLAG_IS_DEFAULT(UseFastSerializer)) { ++ FLAG_SET_DEFAULT(UseFastSerializer, false); ++ warning("Serializer optimization is not supported in this VM."); ++ } ++ + #ifdef COMPILER2 + c2_initialize(); + #endif // COMPILER2 +diff --git a/src/hotspot/cpu/s390/vm_version_s390.cpp b/src/hotspot/cpu/s390/vm_version_s390.cpp +index e3741b29c..44a0275e4 100644 +--- a/src/hotspot/cpu/s390/vm_version_s390.cpp ++++ b/src/hotspot/cpu/s390/vm_version_s390.cpp +@@ -287,6 +287,16 @@ void VM_Version::initialize() { + FLAG_SET_DEFAULT(UsePopCountInstruction, true); + } + ++ if (UnlockExperimentalVMOptions && UseHashMapIntegerCache && !FLAG_IS_DEFAULT(UseHashMapIntegerCache)) { ++ FLAG_SET_DEFAULT(UseHashMapIntegerCache, false); ++ warning("HashMap optimization is not supported in this VM."); ++ } ++ ++ if (UnlockExperimentalVMOptions && UseFastSerializer && !FLAG_IS_DEFAULT(UseFastSerializer)) { ++ FLAG_SET_DEFAULT(UseFastSerializer, false); ++ warning("Serializer optimization is not supported in this VM."); ++ } ++ + // z/Architecture supports 8-byte compare-exchange operations + // (see Atomic::cmpxchg) + // and 'atomic long memory ops' (see Unsafe_GetLongVolatile). +diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp +index d71d3a93f..6f35090cc 100644 +--- a/src/hotspot/cpu/x86/vm_version_x86.cpp ++++ b/src/hotspot/cpu/x86/vm_version_x86.cpp +@@ -1204,6 +1204,11 @@ void VM_Version::get_processor_features() { + FLAG_SET_DEFAULT(UseFastSerializer, false); + warning("Serializer optimization is not supported in this VM."); + } ++ if (UnlockExperimentalVMOptions && UseHashMapIntegerCache && !FLAG_IS_DEFAULT(UseHashMapIntegerCache)) { ++ FLAG_SET_DEFAULT(UseHashMapIntegerCache, false); ++ warning("HashMap optimization is not supported in this VM."); ++ } ++ + #endif + + #ifdef _LP64 +diff --git a/src/hotspot/cpu/zero/vm_version_zero.cpp b/src/hotspot/cpu/zero/vm_version_zero.cpp +index a99885a53..94f33f70b 100644 +--- a/src/hotspot/cpu/zero/vm_version_zero.cpp ++++ b/src/hotspot/cpu/zero/vm_version_zero.cpp +@@ -71,6 +71,16 @@ void VM_Version::initialize() { + FLAG_SET_DEFAULT(UseFMA, false); + } + ++ if (UnlockExperimentalVMOptions && UseHashMapIntegerCache && !FLAG_IS_DEFAULT(UseHashMapIntegerCache)) { ++ FLAG_SET_DEFAULT(UseHashMapIntegerCache, false); ++ warning("HashMap optimization is not supported in this VM."); ++ } ++ ++ if (UnlockExperimentalVMOptions && UseFastSerializer && !FLAG_IS_DEFAULT(UseFastSerializer)) { ++ FLAG_SET_DEFAULT(UseFastSerializer, false); ++ warning("Serializer optimization is not supported in this VM."); ++ } ++ + if (UseMD5Intrinsics) { + warning("MD5 intrinsics are not available on this CPU"); + FLAG_SET_DEFAULT(UseMD5Intrinsics, false); +diff --git a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp +index 921693473..2fd9ed1d0 100644 +--- a/src/hotspot/share/prims/unsafe.cpp ++++ b/src/hotspot/share/prims/unsafe.cpp +@@ -825,6 +825,11 @@ UNSAFE_ENTRY(jint, Unsafe_GetLoadAverage0(JNIEnv *env, jobject unsafe, jdoubleAr + return ret; + } UNSAFE_END + ++UNSAFE_ENTRY(jboolean, Unsafe_GetUseHashMapIntegerCache(JNIEnv *env, jobject unsafe)) { ++ return UseHashMapIntegerCache; ++} ++UNSAFE_END ++ + UNSAFE_ENTRY(jboolean, Unsafe_GetUseFastSerializer(JNIEnv *env, jobject unsafe)) { + return UseFastSerializer; + } +@@ -905,6 +910,8 @@ static JNINativeMethod jdk_internal_misc_Unsafe_methods[] = { + {CC "writebackPostSync0", CC "()V", FN_PTR(Unsafe_WriteBackPostSync0)}, + {CC "setMemory0", CC "(" OBJ "JJB)V", FN_PTR(Unsafe_SetMemory0)}, + ++ {CC "getUseHashMapIntegerCache", CC "()Z", FN_PTR(Unsafe_GetUseHashMapIntegerCache)} ++ , + {CC "shouldBeInitialized0", CC "(" CLS ")Z", FN_PTR(Unsafe_ShouldBeInitialized0)}, + + {CC "fullFence", CC "()V", FN_PTR(Unsafe_FullFence)}, +diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp +index ac33876d8..231c2ef16 100644 +--- a/src/hotspot/share/runtime/globals.hpp ++++ b/src/hotspot/share/runtime/globals.hpp +@@ -1966,6 +1966,11 @@ const int ObjectAlignmentInBytes = 8; + product(bool, UseFastUnorderedTimeStamps, false, EXPERIMENTAL, \ + "Use platform unstable time where supported for timestamps only") \ + \ ++ product(bool, UseHashMapIntegerCache, false, EXPERIMENTAL, \ ++ "The integer cache is an array of references to objects of" \ ++ "the HashMap Value type, indexed by the unboxed int key value." \ ++ "faster in execution, higher in memory consumption.") \ ++ \ + product(bool, UseEmptySlotsInSupers, true, \ + "Allow allocating fields in empty slots of super-classes") \ + \ +diff --git a/src/java.base/share/classes/java/util/HashMap.java b/src/java.base/share/classes/java/util/HashMap.java +index 3ec69cfe9..e6f4a0464 100644 +--- a/src/java.base/share/classes/java/util/HashMap.java ++++ b/src/java.base/share/classes/java/util/HashMap.java +@@ -36,6 +36,7 @@ import java.util.function.BiFunction; + import java.util.function.Consumer; + import java.util.function.Function; + import jdk.internal.access.SharedSecrets; ++import jdk.internal.misc.Unsafe; + + /** + * Hash table based implementation of the {@code Map} interface. This +@@ -274,6 +275,28 @@ public class HashMap extends AbstractMap + */ + static final int MIN_TREEIFY_CAPACITY = 64; + ++ /** ++ * Used to get the commandline option: UseHashMapIntegerCache. ++ */ ++ private static final Unsafe UNSAFE = Unsafe.getUnsafe(); ++ ++ /** ++ * Indicate integerCache can be performed. disable if HashMap.Node.setValue ++ * is directly used to update Node value. ++ */ ++ private static boolean enableIntegerCache = UNSAFE.getUseHashMapIntegerCache(); ++ ++ /** ++ * The smallest table size for create integer cache. ++ */ ++ private static final int MIN_INTEGER_CACHE = 2048; ++ ++ /** ++ * The factor used in create integer cache to guarantee most Key are ++ * Integer and in range. ++ */ ++ private static final float INTEGER_CACHE_FACTOR = 0.95f; ++ + /** + * Basic hash bin node, used for most entries. (See below for + * TreeNode subclass, and in LinkedHashMap for its Entry subclass.) +@@ -300,6 +323,10 @@ public class HashMap extends AbstractMap + } + + public final V setValue(V newValue) { ++ // Disable integerCache in all HashMap instance. ++ if (key != null && key instanceof Integer) { ++ enableIntegerCache = false; ++ } + V oldValue = value; + value = newValue; + return oldValue; +@@ -389,6 +416,12 @@ public class HashMap extends AbstractMap + */ + transient Node[] table; + ++ /** ++ * Cache Value> Map ++ * integerCache[key->intValue] = V ++ */ ++ transient Object[] integerCache; ++ + /** + * Holds cached entrySet(). Note that AbstractMap fields are used + * for keySet() and values(). +@@ -559,7 +592,20 @@ public class HashMap extends AbstractMap + * + * @see #put(Object, Object) + */ ++ @SuppressWarnings("unchecked") + public V get(Object key) { ++ if (integerCache != null) { ++ if (enableIntegerCache == false) { ++ integerCache = null; ++ } ++ else if (key != null && key instanceof Integer) { ++ int val = ((Integer)key).intValue(); ++ if (val >= 0 && val < integerCache.length) { ++ return (V)integerCache[val]; ++ } ++ } ++ } ++ + Node e; + return (e = getNode(key)) == null ? null : e.value; + } +@@ -599,7 +645,18 @@ public class HashMap extends AbstractMap + * key. + */ + public boolean containsKey(Object key) { +- return getNode(key) != null; ++ if (integerCache != null) { ++ if (enableIntegerCache == false) { ++ integerCache = null; ++ } ++ else if (key != null && key instanceof Integer) { ++ int val = ((Integer)key).intValue(); ++ if (val >= 0 && val < integerCache.length && integerCache[val] != null) { ++ return true; ++ } ++ } ++ } ++ return getNode(key) != null; + } + + /** +@@ -631,6 +688,11 @@ public class HashMap extends AbstractMap + final V putVal(int hash, K key, V value, boolean onlyIfAbsent, + boolean evict) { + Node[] tab; Node p; int n, i; ++ ++ if (integerCache != null) { ++ updateIntegerCache(key, value, onlyIfAbsent); ++ } ++ + if ((tab = table) == null || (n = tab.length) == 0) + n = (tab = resize()).length; + if ((p = tab[i = (n - 1) & hash]) == null) +@@ -751,6 +813,8 @@ public class HashMap extends AbstractMap + } + } + } ++ ++ createIntegerCache(); + return newTab; + } + +@@ -850,6 +914,10 @@ public class HashMap extends AbstractMap + p.next = node.next; + ++modCount; + --size; ++ ++ if (integerCache != null) { ++ updateIntegerCache(node.key, null, false); ++ } + afterNodeRemoval(node); + return node; + } +@@ -869,6 +937,7 @@ public class HashMap extends AbstractMap + for (int i = 0; i < tab.length; ++i) + tab[i] = null; + } ++ integerCache = null; + } + + /** +@@ -893,6 +962,82 @@ public class HashMap extends AbstractMap + return false; + } + ++ /** ++ * 1. iterator all Keys and statistic ++ * Integer Key count, total count is size ++ * Integer Key count in range [0, table.length], get Max value. ++ * ++ * 2. Create integer cache ++ */ ++ @SuppressWarnings({"unchecked"}) ++ private final void createIntegerCache() { ++ int n = table.length; ++ int intKeyCount = 0; ++ int intKeyCountInrange = 0; ++ int maxIntKey = 0; ++ if (n < MIN_INTEGER_CACHE || (enableIntegerCache == false)) { ++ integerCache = null; ++ return; ++ } ++ Iterator it = this.keySet().iterator(); ++ while (it.hasNext()) { ++ K key = it.next(); ++ if (key != null && key instanceof Integer) { ++ intKeyCount++; ++ int val = ((Integer)key).intValue(); ++ if (val >= 0 && val < n) { ++ intKeyCountInrange++; ++ if (val > maxIntKey) ++ maxIntKey = val; ++ } ++ } ++ } ++ float keyIntRation = ((float)intKeyCount) / size; ++ float keyIntInRangeRation = ((float)intKeyCountInrange) / size; ++ if (keyIntRation >= INTEGER_CACHE_FACTOR && ++ keyIntInRangeRation >= INTEGER_CACHE_FACTOR) { ++ // compute integerCache size ++ int cacheMapSize = n < (2 * maxIntKey) ? n : (2 * maxIntKey); ++ integerCache = new Object[cacheMapSize]; ++ Iterator> entries = this.entrySet().iterator(); ++ while (entries.hasNext()) { ++ Map.Entry thisEntry = entries.next(); ++ K key = thisEntry.getKey(); ++ V value = thisEntry.getValue(); ++ if (key != null && key instanceof Integer) { ++ int val = ((Integer)key).intValue(); ++ if (val >= 0 && val < integerCache.length) { ++ integerCache[val] = value; ++ } ++ } ++ } ++ } else { ++ integerCache = null; ++ } ++ } ++ ++ /** ++ * put if integerCache null check outside of this call ++ * JIT will not inline this method (not hot) when HashMap is not Integer Key intensive. ++ * Otherwise it will always inline updateIntegerCache method. ++ * ++ */ ++ private final void updateIntegerCache(K key, V value, boolean onlyIfAbsent) { ++ if (enableIntegerCache == false) { ++ integerCache = null; ++ return; ++ } ++ if (key != null && key instanceof Integer) { ++ int val = ((Integer)key).intValue(); ++ if (val >= 0 && val < integerCache.length) { ++ if (onlyIfAbsent && integerCache[val] != null) { ++ return; ++ } ++ integerCache[val] = value; ++ } ++ } ++ } ++ + /** + * Returns a {@link Set} view of the keys contained in this map. + * The set is backed by the map, so changes to the map are +@@ -1142,7 +1287,19 @@ public class HashMap extends AbstractMap + // Overrides of JDK8 Map extension methods + + @Override ++ @SuppressWarnings("unchecked") + public V getOrDefault(Object key, V defaultValue) { ++ if (integerCache != null) { ++ if (enableIntegerCache == false) { ++ integerCache = null; ++ } else if (key != null && key instanceof Integer) { ++ V value; ++ int val = ((Integer)key).intValue(); ++ if (val >= 0 && val < integerCache.length && (value = (V)integerCache[val]) != null) { ++ return value; ++ } ++ } ++ } + Node e; + return (e = getNode(key)) == null ? defaultValue : e.value; + } +@@ -1163,6 +1320,9 @@ public class HashMap extends AbstractMap + if ((e = getNode(key)) != null && + ((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) { + e.value = newValue; ++ if (integerCache != null) { ++ updateIntegerCache(key, newValue, false); ++ } + afterNodeAccess(e); + return true; + } +@@ -1175,6 +1335,9 @@ public class HashMap extends AbstractMap + if ((e = getNode(key)) != null) { + V oldValue = e.value; + e.value = value; ++ if (integerCache != null) { ++ updateIntegerCache(key, value, false); ++ } + afterNodeAccess(e); + return oldValue; + } +@@ -1231,6 +1394,9 @@ public class HashMap extends AbstractMap + return null; + } else if (old != null) { + old.value = v; ++ if (integerCache != null) { ++ updateIntegerCache(key, v, false); ++ } + afterNodeAccess(old); + return v; + } +@@ -1241,6 +1407,11 @@ public class HashMap extends AbstractMap + if (binCount >= TREEIFY_THRESHOLD - 1) + treeifyBin(tab, hash); + } ++ ++ if (integerCache != null) { ++ updateIntegerCache(key, v, false); ++ } ++ + modCount = mc + 1; + ++size; + afterNodeInsertion(true); +@@ -1270,6 +1441,9 @@ public class HashMap extends AbstractMap + if (mc != modCount) { throw new ConcurrentModificationException(); } + if (v != null) { + e.value = v; ++ if (integerCache != null) { ++ updateIntegerCache(key, v, false); ++ } + afterNodeAccess(e); + return v; + } +@@ -1326,6 +1500,9 @@ public class HashMap extends AbstractMap + if (old != null) { + if (v != null) { + old.value = v; ++ if (integerCache != null) { ++ updateIntegerCache(key, v, false); ++ } + afterNodeAccess(old); + } + else +@@ -1339,6 +1516,9 @@ public class HashMap extends AbstractMap + if (binCount >= TREEIFY_THRESHOLD - 1) + treeifyBin(tab, hash); + } ++ if (integerCache != null) { ++ updateIntegerCache(key, v, false); ++ } + modCount = mc + 1; + ++size; + afterNodeInsertion(true); +@@ -1397,6 +1577,9 @@ public class HashMap extends AbstractMap + } + if (v != null) { + old.value = v; ++ if (integerCache != null) { ++ updateIntegerCache(key, v, false); ++ } + afterNodeAccess(old); + } + else +@@ -1410,6 +1593,9 @@ public class HashMap extends AbstractMap + if (binCount >= TREEIFY_THRESHOLD - 1) + treeifyBin(tab, hash); + } ++ if (integerCache != null) { ++ updateIntegerCache(key, value, false); ++ } + ++modCount; + ++size; + afterNodeInsertion(true); +@@ -1443,6 +1629,9 @@ public class HashMap extends AbstractMap + for (Node e : tab) { + for (; e != null; e = e.next) { + e.value = function.apply(e.key, e.value); ++ if (integerCache != null) { ++ updateIntegerCache(e.key, e.value, false); ++ } + } + } + if (modCount != mc) +@@ -1935,6 +2124,7 @@ public class HashMap extends AbstractMap + modCount = 0; + threshold = 0; + size = 0; ++ integerCache = null; + } + + // Callbacks to allow LinkedHashMap post-actions +diff --git a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java +index 926d30691..58c218753 100644 +--- a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java ++++ b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java +@@ -3818,7 +3818,7 @@ public final class Unsafe { + private static int convEndian(boolean big, int n) { return big == BIG_ENDIAN ? n : Integer.reverseBytes(n) ; } + private static long convEndian(boolean big, long n) { return big == BIG_ENDIAN ? n : Long.reverseBytes(n) ; } + +- ++ public native boolean getUseHashMapIntegerCache(); + public native boolean getUseFastSerializer(); + private native long allocateMemory0(long bytes); + private native long reallocateMemory0(long address, long bytes); +-- +2.47.0.windows.2 + diff --git a/huawei-Add-KAE-Provider.patch b/huawei-Add-KAE-Provider.patch new file mode 100644 index 0000000000000000000000000000000000000000..b234737ad0ec22dd15687829181ce53b63c8a221 --- /dev/null +++ b/huawei-Add-KAE-Provider.patch @@ -0,0 +1,14479 @@ +Date: Thu, 13 Mar 2025 10:25:51 +0800 +Subject: Add KAE Provider + +--- + make/ZipSecurity.gmk | 1 + + make/autoconf/configure.ac | 1 + + make/autoconf/jdk-options.m4 | 21 + + make/autoconf/spec.gmk.in | 1 + + make/common/Modules.gmk | 13 + + make/conf/module-loader-map.conf | 1 + + .../hotspot/nbproject/configurations.xml | 104 +++ + make/modules/jdk.crypto.kaeprovider/Copy.gmk | 46 + + make/modules/jdk.crypto.kaeprovider/Lib.gmk | 46 + + src/java.base/share/classes/module-info.java | 8 +- + .../share/lib/security/default.policy | 5 + + .../share/classes/module-info.java | 6 + + .../linux/classes/module-info.java | 39 + + .../security/openssl/KAEAESCipher.java | 365 ++++++++ + .../openeuler/security/openssl/KAEConfig.java | 393 +++++++++ + .../security/openssl/KAEDHKeyAgreement.java | 289 +++++++ + .../openssl/KAEDHKeyPairGenerator.java | 183 ++++ + .../openeuler/security/openssl/KAEDigest.java | 264 ++++++ + .../security/openssl/KAEECDHKeyAgreement.java | 146 ++++ + .../openssl/KAEECKeyPairGenerator.java | 151 ++++ + .../openeuler/security/openssl/KAEHMac.java | 227 +++++ + .../openeuler/security/openssl/KAELog.java | 188 +++++ + .../openeuler/security/openssl/KAEMGF1.java | 94 +++ + .../security/openssl/KAEProvider.java | 326 +++++++ + .../security/openssl/KAERSACipher.java | 796 ++++++++++++++++++ + .../openssl/KAERSAKeyPairGenerator.java | 164 ++++ + .../security/openssl/KAERSAPSSSignature.java | 683 +++++++++++++++ + .../security/openssl/KAERSAPaddingType.java | 83 ++ + .../security/openssl/KAERSASignature.java | 365 ++++++++ + .../openssl/KAERSASignatureNative.java | 46 + + .../security/openssl/KAESM4Cipher.java | 370 ++++++++ + .../security/openssl/KAESM4KeyGenerator.java | 74 ++ + .../openssl/KAESymmetricCipherBase.java | 791 +++++++++++++++++ + .../openeuler/security/openssl/KAEUtils.java | 220 +++++ + .../linux/conf/security/kaeprovider.conf | 75 ++ + .../security/openssl/kae_cipher_rsa.c | 471 +++++++++++ + .../openeuler/security/openssl/kae_digest.c | 232 +++++ + .../security/openssl/kae_exception.c | 135 +++ + .../security/openssl/kae_exception.h | 57 ++ + .../org/openeuler/security/openssl/kae_hmac.c | 208 +++++ + .../security/openssl/kae_keyagreement_dh.c | 141 ++++ + .../security/openssl/kae_keyagreement_ecdh.c | 121 +++ + .../openssl/kae_keypairgenerator_dh.c | 132 +++ + .../openssl/kae_keypairgenerator_ec.c | 505 +++++++++++ + .../openssl/kae_keypairgenerator_rsa.c | 173 ++++ + .../org/openeuler/security/openssl/kae_log.h | 33 + + .../openeuler/security/openssl/kae_provider.c | 103 +++ + .../security/openssl/kae_signature_rsa.c | 366 ++++++++ + .../security/openssl/kae_symmetric_cipher.c | 415 +++++++++ + .../org/openeuler/security/openssl/kae_util.c | 246 ++++++ + .../org/openeuler/security/openssl/kae_util.h | 94 +++ + .../openeuler/security/openssl/openssl_ad.h | 10 + + test/jdk/TEST.groups | 3 + + .../KeyAgreement/KeyAgreementTest.java | 11 +- + .../Signature/SignatureGetInstance.java | 6 +- + .../openeuler/security/openssl/AESTest.java | 115 +++ + .../openeuler/security/openssl/DHTest.java | 122 +++ + .../security/openssl/DigestTest.java | 61 ++ + .../openeuler/security/openssl/ECDHTest.java | 114 +++ + .../openeuler/security/openssl/HmacTest.java | 89 ++ + .../security/openssl/KAEConfTest.java | 122 +++ + .../openssl/KAEDisabledAlgorithmsTest.java | 165 ++++ + .../security/openssl/KAEEngineIdTest.java | 77 ++ + .../security/openssl/KAELogTest.java | 127 +++ + .../security/openssl/KAETestHelper.java | 209 +++++ + .../security/openssl/KAEUseEngineTest.java | 263 ++++++ + .../security/openssl/KaeDebugLogTest.java | 89 ++ + .../security/openssl/KaeProviderTest.java | 171 ++++ + .../openeuler/security/openssl/RSATest.java | 115 +++ + .../openeuler/security/openssl/SM3Test.java | 55 ++ + .../openeuler/security/openssl/SM4Test.java | 153 ++++ + .../jca/PreferredProviderNegativeTest.java | 7 +- + .../jdk/sun/security/krb5/auto/BasicProc.java | 4 +- + test/jdk/sun/security/pkcs11/Secmod/policy | 1 + + test/jdk/sun/security/pkcs11/policy | 1 + + .../ssl/CipherSuite/DisabledCurve.java | 4 + + .../SSLSocketImpl/NotifyHandshakeTest.policy | 2 + + .../bench/security/openssl/AESBenchmark.java | 108 +++ + .../security/openssl/AESGCMBenchmark.java | 133 +++ + .../bench/security/openssl/BenchmarkBase.java | 106 +++ + .../openssl/DHKeyAgreementBenchMark.java | 139 +++ + .../openssl/DHKeyPairGeneratorBenchmark.java | 64 ++ + .../security/openssl/DigestBenchmark.java | 69 ++ + .../openssl/ECKeyAgreementBenchmark.java | 88 ++ + .../openssl/ECKeyPairGeneratorBenchmark.java | 63 ++ + .../bench/security/openssl/HMacBenchmark.java | 73 ++ + .../security/openssl/RSACipherBenchmark.java | 107 +++ + .../openssl/RSAKeyPairGeneratorBenchmark.java | 64 ++ + .../openssl/RSAPSSSignatureBenchmark.java | 118 +++ + .../openssl/RSASignatureBenchmark.java | 90 ++ + .../bench/security/openssl/SM3Benchmark.java | 98 +++ + .../bench/security/openssl/SM4Benchmark.java | 157 ++++ + 92 files changed, 13584 insertions(+), 5 deletions(-) + create mode 100644 make/modules/jdk.crypto.kaeprovider/Copy.gmk + create mode 100644 make/modules/jdk.crypto.kaeprovider/Lib.gmk + create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/module-info.java + create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEAESCipher.java + create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEConfig.java + create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDHKeyAgreement.java + create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDHKeyPairGenerator.java + create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDigest.java + create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEECDHKeyAgreement.java + create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEECKeyPairGenerator.java + create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEHMac.java + create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAELog.java + create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEMGF1.java + create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEProvider.java + create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSACipher.java + create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAKeyPairGenerator.java + create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAPSSSignature.java + create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAPaddingType.java + create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSASignature.java + create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSASignatureNative.java + create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESM4Cipher.java + create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESM4KeyGenerator.java + create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESymmetricCipherBase.java + create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEUtils.java + create mode 100644 src/jdk.crypto.kaeprovider/linux/conf/security/kaeprovider.conf + create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_cipher_rsa.c + create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_digest.c + create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_exception.c + create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_exception.h + create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_hmac.c + create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keyagreement_dh.c + create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keyagreement_ecdh.c + create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_dh.c + create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_ec.c + create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_rsa.c + create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_log.h + create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_provider.c + create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_signature_rsa.c + create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_symmetric_cipher.c + create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_util.c + create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_util.h + create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/openssl_ad.h + create mode 100644 test/jdk/org/openeuler/security/openssl/AESTest.java + create mode 100644 test/jdk/org/openeuler/security/openssl/DHTest.java + create mode 100644 test/jdk/org/openeuler/security/openssl/DigestTest.java + create mode 100644 test/jdk/org/openeuler/security/openssl/ECDHTest.java + create mode 100644 test/jdk/org/openeuler/security/openssl/HmacTest.java + create mode 100644 test/jdk/org/openeuler/security/openssl/KAEConfTest.java + create mode 100644 test/jdk/org/openeuler/security/openssl/KAEDisabledAlgorithmsTest.java + create mode 100644 test/jdk/org/openeuler/security/openssl/KAEEngineIdTest.java + create mode 100644 test/jdk/org/openeuler/security/openssl/KAELogTest.java + create mode 100644 test/jdk/org/openeuler/security/openssl/KAETestHelper.java + create mode 100644 test/jdk/org/openeuler/security/openssl/KAEUseEngineTest.java + create mode 100644 test/jdk/org/openeuler/security/openssl/KaeDebugLogTest.java + create mode 100644 test/jdk/org/openeuler/security/openssl/KaeProviderTest.java + create mode 100644 test/jdk/org/openeuler/security/openssl/RSATest.java + create mode 100644 test/jdk/org/openeuler/security/openssl/SM3Test.java + create mode 100644 test/jdk/org/openeuler/security/openssl/SM4Test.java + create mode 100644 test/micro/org/openeuler/bench/security/openssl/AESBenchmark.java + create mode 100644 test/micro/org/openeuler/bench/security/openssl/AESGCMBenchmark.java + create mode 100644 test/micro/org/openeuler/bench/security/openssl/BenchmarkBase.java + create mode 100644 test/micro/org/openeuler/bench/security/openssl/DHKeyAgreementBenchMark.java + create mode 100644 test/micro/org/openeuler/bench/security/openssl/DHKeyPairGeneratorBenchmark.java + create mode 100644 test/micro/org/openeuler/bench/security/openssl/DigestBenchmark.java + create mode 100644 test/micro/org/openeuler/bench/security/openssl/ECKeyAgreementBenchmark.java + create mode 100644 test/micro/org/openeuler/bench/security/openssl/ECKeyPairGeneratorBenchmark.java + create mode 100644 test/micro/org/openeuler/bench/security/openssl/HMacBenchmark.java + create mode 100644 test/micro/org/openeuler/bench/security/openssl/RSACipherBenchmark.java + create mode 100644 test/micro/org/openeuler/bench/security/openssl/RSAKeyPairGeneratorBenchmark.java + create mode 100644 test/micro/org/openeuler/bench/security/openssl/RSAPSSSignatureBenchmark.java + create mode 100644 test/micro/org/openeuler/bench/security/openssl/RSASignatureBenchmark.java + create mode 100644 test/micro/org/openeuler/bench/security/openssl/SM3Benchmark.java + create mode 100644 test/micro/org/openeuler/bench/security/openssl/SM4Benchmark.java + +diff --git a/make/ZipSecurity.gmk b/make/ZipSecurity.gmk +index 489bbef49..170b54df6 100644 +--- a/make/ZipSecurity.gmk ++++ b/make/ZipSecurity.gmk +@@ -42,6 +42,7 @@ $(eval $(call SetupZipArchive,BUILD_SEC_BIN_ZIP, \ + modules/java.base/sun/security/internal/spec \ + modules/java.base/com/sun/crypto/provider \ + modules/jdk.crypto.ec/sun/security/ec \ ++ modules/jdk.crypto.kaeprovider/org/openeuler/security/openssl \ + modules/jdk.crypto.mscapi/sun/security/mscapi \ + modules/jdk.crypto.cryptoki/sun/security/pkcs11 \ + modules/jdk.crypto.cryptoki/sun/security/pkcs11/wrapper \ +diff --git a/make/autoconf/configure.ac b/make/autoconf/configure.ac +index f7e9844a6..55201277c 100644 +--- a/make/autoconf/configure.ac ++++ b/make/autoconf/configure.ac +@@ -256,6 +256,7 @@ HOTSPOT_SETUP_MISC + LIB_TESTS_ENABLE_DISABLE_FAILURE_HANDLER + LIB_TESTS_ENABLE_DISABLE_JTREG_TEST_THREAD_FACTORY + ++JDKOPT_DETECT_KAE + JDKOPT_ENABLE_DISABLE_GENERATE_CLASSLIST + JDKOPT_EXCLUDE_TRANSLATIONS + JDKOPT_ENABLE_DISABLE_MANPAGES +diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4 +index 2cbdf74ae..a858c5e7e 100644 +--- a/make/autoconf/jdk-options.m4 ++++ b/make/autoconf/jdk-options.m4 +@@ -345,6 +345,27 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_DEBUG_SYMBOLS], + AC_SUBST(SHIP_DEBUG_SYMBOLS) + ]) + ++############################################################################### ++# ++# Enable or disable the kae crypto implementation ++# ++AC_DEFUN_ONCE([JDKOPT_DETECT_KAE], ++[ ++ AC_ARG_ENABLE(kae, [AS_HELP_STRING([--enable-kae], ++ [enable KAE support on aarch64 @<:@disabled@:>@])], ++ [ENABLE_KAE="true"], [ENABLE_KAE="false"]) ++ AC_MSG_CHECKING([if kae has been enabled]) ++ if test "x$enable_kae" = "xyes"; then ++ AC_MSG_RESULT([yes]) ++ elif test "x$enable_kae" = "x" || test "x$enable_kae" = "xno"; then ++ AC_MSG_RESULT([no]) ++ else ++ AC_MSG_ERROR([Invalid value for --enable-kae: $enable_kae]) ++ fi ++ ++ AC_SUBST(ENABLE_KAE) ++]) ++ + ################################################################################ + # + # Native and Java code coverage +diff --git a/make/autoconf/spec.gmk.in b/make/autoconf/spec.gmk.in +index f6def153c..8336b287a 100644 +--- a/make/autoconf/spec.gmk.in ++++ b/make/autoconf/spec.gmk.in +@@ -804,6 +804,7 @@ TAR_INCLUDE_PARAM:=@TAR_INCLUDE_PARAM@ + TAR_SUPPORTS_TRANSFORM:=@TAR_SUPPORTS_TRANSFORM@ + + # Build setup ++ENABLE_KAE:=@ENABLE_KAE@ + USE_EXTERNAL_LIBJPEG:=@USE_EXTERNAL_LIBJPEG@ + USE_EXTERNAL_LIBGIF:=@USE_EXTERNAL_LIBGIF@ + USE_EXTERNAL_LIBZ:=@USE_EXTERNAL_LIBZ@ +diff --git a/make/common/Modules.gmk b/make/common/Modules.gmk +index 0eb0fb2dd..c3c6a1bf4 100644 +--- a/make/common/Modules.gmk ++++ b/make/common/Modules.gmk +@@ -312,6 +312,19 @@ define ReadImportMetaData + $$(eval $$(call ReadSingleImportMetaData, $$m))) + endef + ++ifeq ($(ENABLE_KAE), true) ++ ifeq ($(OPENJDK_TARGET_CPU_ARCH), aarch64) ++ PLATFORM_MODULES += jdk.crypto.kaeprovider ++ endif ++endif ++ ++################################################################################ ++# Filter out kae specific modules if kae is disabled or cpu is not aarch64 ++ ++ifneq ($(ENABLE_KAE)-$(OPENJDK_TARGET_CPU_ARCH), true-aarch64) ++ MODULES_FILTER += jdk.crypto.kaeprovider ++endif ++ + ################################################################################ + + endif # _MODULES_GMK +diff --git a/make/conf/module-loader-map.conf b/make/conf/module-loader-map.conf +index 2c95b8570..881a68d5a 100644 +--- a/make/conf/module-loader-map.conf ++++ b/make/conf/module-loader-map.conf +@@ -77,6 +77,7 @@ PLATFORM_MODULES= \ + jdk.accessibility \ + jdk.charsets \ + jdk.crypto.cryptoki \ ++ jdk.crypto.kaeprovide \ + jdk.crypto.ec \ + jdk.dynalink \ + jdk.httpserver \ +diff --git a/make/ide/netbeans/hotspot/nbproject/configurations.xml b/make/ide/netbeans/hotspot/nbproject/configurations.xml +index 4f64db4ec..29c5e668b 100644 +--- a/make/ide/netbeans/hotspot/nbproject/configurations.xml ++++ b/make/ide/netbeans/hotspot/nbproject/configurations.xml +@@ -3,6 +3,110 @@ + + + ++ ++ ++ ++ ++ kae_cipher_rsa.c ++ kae_digest.c ++ kae_exception.c ++ kae_exception.h ++ kae_hmac.c ++ kae_keyagreement_dh.c ++ kae_keyagreement_ecdh.c ++ kae_keypairgenerator_dh.c ++ kae_keypairgenerator_ec.c ++ kae_keypairgenerator_rsa.c ++ kae_provider.c ++ kae_signature_rsa.c ++ kae_symmetric_cipher.c ++ kae_util.c ++ kae_util.h ++ kae_log.h ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + nopadding in sunjce ++ if (mode == Mode.GCM) { ++ this.padding = Padding.NOPADDING; ++ } else if (paddingStr.equalsIgnoreCase("NOPADDING")) { ++ this.padding = Padding.NOPADDING; ++ } else if(paddingStr.equalsIgnoreCase("PKCS5PADDING")) { ++ if (mode == Mode.CTR) { ++ throw new NoSuchPaddingException("PKCS#5 padding not supported with CTR mode"); ++ } ++ this.padding = Padding.PKCS5PADDING; ++ } else { ++ throw new NoSuchPaddingException("Unsupported padding " + paddingStr); ++ } ++ } ++ ++ protected void checkIvBytes(byte[] ivBytes) throws InvalidAlgorithmParameterException { ++ if ((ivBytes == null) || (ivBytes.length != blockSize)) { ++ throw new InvalidAlgorithmParameterException("Wrong IV length: must be " + blockSize + " bytes long."); ++ } ++ } ++} ++ +diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEConfig.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEConfig.java +new file mode 100644 +index 000000000..a4eb57770 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEConfig.java +@@ -0,0 +1,393 @@ ++/* ++ * Copyright (c) 2023, 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 { ++ kaeConfigInit(); ++ } ++ ++ @SuppressWarnings("removal") ++ private static void kaeConfigInit() { ++ 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 + "conf" + 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("\\,"); ++ } ++ ++ @SuppressWarnings("removal") ++ public static String privilegedGetProperty(String key) { ++ if (System.getSecurityManager() == null) { ++ return getProperty(key); ++ } else { ++ return AccessController.doPrivileged((PrivilegedAction) () -> getOverridableProperty(key)); ++ } ++ } ++ ++ @SuppressWarnings("removal") ++ 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/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDHKeyAgreement.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDHKeyAgreement.java +new file mode 100644 +index 000000000..541df07d6 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDHKeyAgreement.java +@@ -0,0 +1,289 @@ ++/* ++ * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2023, 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. 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.openeuler.security.openssl; ++ ++import sun.security.util.KeyUtil; ++import java.math.BigInteger; ++import java.security.KeyFactory; ++import java.security.InvalidKeyException; ++import java.security.SecureRandom; ++import java.security.Key; ++import java.security.InvalidAlgorithmParameterException; ++import java.security.NoSuchAlgorithmException; ++import java.security.AccessController; ++import java.security.ProviderException; ++import java.security.PrivilegedAction; ++import java.security.spec.InvalidKeySpecException; ++import java.security.spec.AlgorithmParameterSpec; ++import javax.crypto.KeyAgreementSpi; ++import javax.crypto.SecretKey; ++import javax.crypto.ShortBufferException; ++import javax.crypto.interfaces.DHPrivateKey; ++import javax.crypto.interfaces.DHPublicKey; ++import javax.crypto.spec.DHParameterSpec; ++import javax.crypto.spec.SecretKeySpec; ++import javax.crypto.spec.DHPublicKeySpec; ++import javax.crypto.SecretKeyFactory; ++import javax.crypto.spec.DESedeKeySpec; ++import javax.crypto.spec.DESKeySpec; ++ ++public class KAEDHKeyAgreement extends KeyAgreementSpi { ++ private boolean generateSecret = false; ++ private BigInteger p; ++ private BigInteger g; ++ private BigInteger x; ++ private BigInteger y; ++ static final int[] AES_KEYSIZES = {16, 24, 32}; ++ static final int BLOWFISH_MAX_KEYSIZE = 56; ++ ++ private static class AllowKDF { ++ private static final boolean VALUE = getValue(); ++ ++ @SuppressWarnings("removal") ++ private static boolean getValue() { ++ return AccessController.doPrivileged( ++ (PrivilegedAction) ++ () -> Boolean.getBoolean("jdk.crypto.KeyAgreement.legacyKDF")); ++ } ++ } ++ ++ public KAEDHKeyAgreement() { ++ } ++ ++ @Override ++ protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException { ++ try { ++ engineInit(key, null, random); ++ } catch (InvalidAlgorithmParameterException e) { ++ // never happens, because we did not pass any parameters ++ } ++ } ++ ++ @Override ++ protected void engineInit(Key key, AlgorithmParameterSpec params, ++ SecureRandom random) ++ throws InvalidKeyException, InvalidAlgorithmParameterException { ++ ++ // ignore "random" parameter, because our implementation does not ++ // require any source of randomness ++ generateSecret = false; ++ p = null; ++ g = null; ++ ++ if ((params != null) && !(params instanceof DHParameterSpec)) { ++ throw new InvalidAlgorithmParameterException("Diffie-Hellman parameters expected"); ++ } ++ ++ if (!(key instanceof DHPrivateKey)) { ++ throw new InvalidKeyException("Diffie-Hellman private key expected"); ++ } ++ ++ DHPrivateKey privateKey = (DHPrivateKey) key; ++ ++ // check if private key parameters are compatible with ++ // initialized ones ++ if (params != null) { ++ p = ((DHParameterSpec) params).getP(); ++ g = ((DHParameterSpec) params).getG(); ++ } ++ ++ BigInteger priv_p = privateKey.getParams().getP(); ++ BigInteger priv_g = privateKey.getParams().getG(); ++ if (p != null && priv_p != null && !(p.equals(priv_p))) { ++ throw new InvalidKeyException("Incompatible parameters"); ++ } ++ if (g != null && priv_g != null && !(g.equals(priv_g))) { ++ throw new InvalidKeyException("Incompatible parameters"); ++ } ++ if ((p == null && priv_p == null) || ++ (g == null) && priv_g == null) { ++ throw new InvalidKeyException("Missing parameters"); ++ } ++ p = priv_p; ++ g = priv_g; ++ ++ // store the x value ++ x = privateKey.getX(); ++ } ++ ++ @Override ++ protected Key engineDoPhase(Key key, boolean lastPhase) ++ throws InvalidKeyException, IllegalStateException { ++ if (!(key instanceof DHPublicKey)) { ++ throw new InvalidKeyException("Diffie-Hellman public " ++ + "expected"); ++ } ++ DHPublicKey publicKey = (DHPublicKey) key; ++ if (p == null || g == null) { ++ throw new IllegalStateException("Not initialized"); ++ } ++ BigInteger pub_p = publicKey.getParams().getP(); ++ BigInteger pub_g = publicKey.getParams().getG(); ++ if (pub_p != null && !(p.equals(pub_p))) { ++ throw new InvalidKeyException("Incompatible parameters"); ++ } ++ if (pub_g != null && !(g.equals(pub_g))) { ++ throw new InvalidKeyException("Incompatible parameters"); ++ } ++ KeyUtil.validate(publicKey); ++ y = publicKey.getY(); ++ generateSecret = true; ++ if (lastPhase == false) { ++ byte[] intermediate = engineGenerateSecret(); ++ try { ++ KeyFactory fk = KeyFactory.getInstance("DH"); ++ DHPublicKey newPublicKey = (DHPublicKey) fk.generatePublic( ++ new DHPublicKeySpec(new BigInteger(1, intermediate), p, g)); ++ return newPublicKey; ++ } catch (NoSuchAlgorithmException noalg) { ++ throw new ProviderException(noalg); ++ } catch (InvalidKeySpecException ikse) { ++ throw new ProviderException(ikse); ++ } ++ } else { ++ return null; ++ } ++ } ++ ++ @Override ++ protected byte[] engineGenerateSecret() ++ throws IllegalStateException { ++ int expectedLen = (p.bitLength() + 7) >>> 3; ++ byte[] result = new byte[expectedLen]; ++ try { ++ engineGenerateSecret(result, 0); ++ } catch (ShortBufferException shortBufferException) { ++ // should never happen since length are identical ++ } ++ return result; ++ } ++ ++ @Override ++ protected int engineGenerateSecret(byte[] sharedSecret, int offset) ++ throws IllegalStateException, ShortBufferException { ++ if (!generateSecret) { ++ throw new IllegalStateException("Key agreement has not bee complated yet"); ++ } ++ if (sharedSecret == null) { ++ throw new ShortBufferException("No buffer provided for shared secret"); ++ } ++ BigInteger modulus = p; ++ int expectedLen = (modulus.bitLength() + 7) >>> 3; ++ if ((sharedSecret.length - offset) < expectedLen) { ++ throw new ShortBufferException("Buffer too short for shared secret"); ++ } ++ generateSecret = false; ++ byte[] secret = nativeComputeKey(y.toByteArray(), x.toByteArray(), ++ p.toByteArray(), g.toByteArray(), modulus.bitLength()); ++ ++ if (secret.length == expectedLen) { ++ System.arraycopy(secret, 0, sharedSecret, offset, ++ secret.length); ++ } else { ++ // Array too short, pad it w/ leading 0s ++ if (secret.length < expectedLen) { ++ System.arraycopy(secret, 0, sharedSecret, ++ offset + (expectedLen - secret.length), ++ secret.length); ++ } else { ++ // Array too long, check and trim off the excess ++ if ((secret.length == (expectedLen + 1)) && secret[0] == 0) { ++ // ignore the leading sign byte ++ System.arraycopy(secret, 1, sharedSecret, offset, expectedLen); ++ } else { ++ throw new ProviderException("Generated secret is out-of-range"); ++ } ++ } ++ } ++ ++ return expectedLen; ++ } ++ ++ @Override ++ protected SecretKey engineGenerateSecret(String algorithm) ++ throws IllegalStateException, NoSuchAlgorithmException, InvalidKeyException { ++ if (algorithm == null) { ++ throw new NoSuchAlgorithmException("null algorithm"); ++ } ++ ++ if (!algorithm.equalsIgnoreCase("TlsPremasterSecret") && ++ !AllowKDF.VALUE) { ++ ++ throw new NoSuchAlgorithmException("Unsupported secret key " ++ + "algorithm: " + algorithm); ++ } ++ ++ byte[] secret = engineGenerateSecret(); ++ if (algorithm.equalsIgnoreCase("DES")) { ++ // DES ++ try { ++ SecretKeyFactory factory = SecretKeyFactory.getInstance("DES"); ++ return factory.generateSecret(new DESKeySpec(secret)); ++ } catch (InvalidKeySpecException e) { ++ throw new ProviderException("Generate DES Secret failed.", e); ++ } ++ } else if (algorithm.equalsIgnoreCase("DESede") ++ || algorithm.equalsIgnoreCase("TripleDES")) { ++ // Triple DES ++ try { ++ SecretKeyFactory factory = SecretKeyFactory.getInstance("DESede"); ++ return factory.generateSecret(new DESedeKeySpec(secret)); ++ } catch (InvalidKeySpecException e) { ++ throw new ProviderException("Generate DESede Secret failed.", e); ++ } ++ } else if (algorithm.equalsIgnoreCase("Blowfish")) { ++ // Blowfish ++ int keysize = secret.length; ++ if (keysize >= BLOWFISH_MAX_KEYSIZE) ++ keysize = BLOWFISH_MAX_KEYSIZE; ++ return new SecretKeySpec(secret, 0, keysize, "Blowfish"); ++ } else if (algorithm.equalsIgnoreCase("AES")) { ++ int idx = AES_KEYSIZES.length - 1; ++ int keysize = secret.length; ++ SecretKeySpec secretKey = null; ++ while (secretKey == null && idx >= 0) { ++ if (keysize >= AES_KEYSIZES[idx]) { ++ keysize = AES_KEYSIZES[idx]; ++ secretKey = new SecretKeySpec(secret, 0, keysize, "AES"); ++ } ++ idx--; ++ } ++ if (secretKey == null) { ++ throw new InvalidKeyException("Key material is too short"); ++ } ++ return secretKey; ++ } else if (algorithm.equals("TlsPremasterSecret")) { ++ // remove leading zero bytes per RFC 5246 Section 8.1.2 ++ return new SecretKeySpec( ++ KeyUtil.trimZeroes(secret), "TlsPremasterSecret"); ++ } else { ++ throw new NoSuchAlgorithmException("Unsupported secret key " ++ + "algorithm: " + algorithm); ++ } ++ } ++ protected native byte[] nativeComputeKey(byte[] y, byte[] x, byte[] p, byte[] g, int pSize); ++} +diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDHKeyPairGenerator.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDHKeyPairGenerator.java +new file mode 100644 +index 000000000..74134eef5 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDHKeyPairGenerator.java +@@ -0,0 +1,183 @@ ++/* ++ * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2023, 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. 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.openeuler.security.openssl; ++ ++import java.math.BigInteger; ++import java.security.spec.AlgorithmParameterSpec; ++import java.security.KeyPairGeneratorSpi; ++import java.security.SecureRandom; ++import java.security.InvalidAlgorithmParameterException; ++import java.security.KeyPair; ++import java.security.NoSuchAlgorithmException; ++import java.security.InvalidParameterException; ++import java.security.GeneralSecurityException; ++import java.security.ProviderException; ++import java.security.KeyFactory; ++import java.security.spec.InvalidKeySpecException; ++import sun.security.jca.JCAUtil; ++import sun.security.provider.ParameterCache; ++import javax.crypto.spec.DHPublicKeySpec; ++import javax.crypto.spec.DHPrivateKeySpec; ++import javax.crypto.spec.DHParameterSpec; ++import javax.crypto.interfaces.DHPublicKey; ++import javax.crypto.interfaces.DHPrivateKey; ++ ++import static sun.security.util.SecurityProviderConstants.DEF_DH_KEY_SIZE; ++ ++public class KAEDHKeyPairGenerator ++ extends KeyPairGeneratorSpi { ++ private DHParameterSpec parameterSpec; ++ // The size in bits of the random exponent (private value) ++ private int pSize; ++ // The size in bits of the random exponent (private value) ++ private int lSize; ++ private SecureRandom random; ++ ++ @SuppressWarnings("this-escape") ++ public KAEDHKeyPairGenerator() { ++ super(); ++ initialize(DEF_DH_KEY_SIZE, null); ++ } ++ ++ private static void checkKeySize(int keySize) { ++ if ((keySize < 512) || (keySize > 8192) || ((keySize & 0x3F) != 0)) { ++ throw new InvalidParameterException( ++ "DH key size must be multiple of 64, and can only range " + ++ "from 512 to 8192(inclusize). " + ++ "The specific key size " + keySize + " is not supported"); ++ } ++ } ++ @Override ++ public void initialize(AlgorithmParameterSpec params, SecureRandom random) ++ throws InvalidAlgorithmParameterException { ++ ++ if (!(params instanceof DHParameterSpec)){ ++ throw new InvalidAlgorithmParameterException ++ ("Inappropriate parameter type"); ++ } ++ ++ parameterSpec = (DHParameterSpec) params; ++ pSize = parameterSpec.getP().bitLength(); ++ ++ try { ++ checkKeySize(pSize); ++ } catch (InvalidParameterException e) { ++ throw new InvalidAlgorithmParameterException(e.getMessage()); ++ } ++ ++ // exponent size is optional, could be 0 ++ lSize = parameterSpec.getL(); ++ ++ // Require exponentSize < primeSize ++ if ((lSize != 0) && (lSize > pSize)) { ++ throw new InvalidAlgorithmParameterException ++ ("Exponent size must not be larger than modulus size"); ++ } ++ ++ this.random = random; ++ } ++ ++ @Override ++ public void initialize(int keysize, SecureRandom random) { ++ checkKeySize(keysize); ++ this.parameterSpec = ParameterCache.getCachedDHParameterSpec(keysize); ++ if ((this.parameterSpec == null) && (keysize > 1024)) { ++ throw new InvalidParameterException("Unsupported " + keysize + "-bit DH parameter generation."); ++ } ++ this.pSize = keysize; ++ this.lSize = 0; ++ this.random = random; ++ } ++ ++ @Override ++ public KeyPair generateKeyPair() { ++ ++ if (random == null) { ++ random = JCAUtil.getSecureRandom(); ++ } ++ ++ if (parameterSpec == null) { ++ try { ++ parameterSpec = ParameterCache.getDHParameterSpec(pSize, random); ++ } catch (GeneralSecurityException e) { ++ // should never happen ++ throw new ProviderException(e); ++ } ++ } ++ ++ BigInteger p = parameterSpec.getP(); ++ BigInteger g = parameterSpec.getG(); ++ ++ if (lSize <= 0) { ++ lSize = pSize >> 1; ++ // use an exponent size of (pSize / 2) but at least 384 bits ++ if (lSize < 384) { ++ lSize = 384; ++ } ++ } ++ byte[][] keys; ++ try { ++ keys = nativeGenerateKeyPair(p.toByteArray(), g.toByteArray(), lSize); ++ } catch (Exception e){ ++ throw new ProviderException("Invoke nativeGenerateKeyPair failed.", e); ++ } ++ ++ // check keys ++ checkKeys(keys); ++ ++ BigInteger pubKey = new BigInteger(keys[0]); ++ BigInteger priKey = new BigInteger(keys[1]); ++ ++ try{ ++ KeyFactory fk = KeyFactory.getInstance("DH"); ++ DHPublicKey publicKey = (DHPublicKey)fk.generatePublic(new DHPublicKeySpec(pubKey, p , g)); ++ DHPrivateKey privateKey = (DHPrivateKey)fk.generatePrivate(new DHPrivateKeySpec(priKey, p, g)); ++ return new KeyPair(publicKey, privateKey); ++ } catch (NoSuchAlgorithmException noalg) { ++ throw new ProviderException(noalg); ++ } catch (InvalidKeySpecException ikse) { ++ throw new ProviderException(ikse); ++ } ++ } ++ ++ private void checkKeys(byte[][] keys) { ++ if (keys == null) { ++ throw new ProviderException("Invalid keys, keys is null."); ++ } ++ // The keys needs to contain at least 2 byte arrays, which are public and private keys. ++ if (keys.length < 2) { ++ throw new ProviderException("Invalid keys, keys length is less than 2."); ++ } ++ for (int i = 0; i < keys.length; i++) { ++ if (keys[i] == null) { ++ throw new ProviderException("Invalid keys, keys[" + i + "]" + "is null."); ++ } ++ } ++ } ++ ++ protected native static byte[][] nativeGenerateKeyPair(byte[] p, byte[] g, int lSize); ++} +diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDigest.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDigest.java +new file mode 100644 +index 000000000..aca5f4b29 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDigest.java +@@ -0,0 +1,264 @@ ++/* ++ * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2023, 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. 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.openeuler.security.openssl; ++ ++import java.lang.ref.PhantomReference; ++import java.lang.ref.ReferenceQueue; ++import java.security.DigestException; ++import java.security.MessageDigestSpi; ++import java.security.ProviderException; ++import java.util.Set; ++import java.util.concurrent.ConcurrentSkipListSet; ++ ++/** ++ * KAE Digest ++ */ ++abstract class KAEDigest extends MessageDigestSpi implements Cloneable { ++ ++ public static final class MD5 extends KAEDigest { ++ private static final long initContext = nativeInit("md5"); ++ ++ public MD5() { ++ super("md5", 16, initContext); ++ } ++ } ++ ++ public static final class SM3 extends KAEDigest { ++ private static final long initContext = nativeInit("sm3"); ++ ++ public SM3() { ++ super("sm3", 32, initContext); ++ } ++ } ++ ++ public static final class SHA256 extends KAEDigest { ++ private static final long initContext = nativeInit("sha256"); ++ ++ public SHA256() { ++ super("sha256", 32, initContext); ++ } ++ } ++ ++ public static final class SHA384 extends KAEDigest { ++ private static final long initContext = nativeInit("sha384"); ++ ++ public SHA384() { ++ super("sha384", 48, initContext); ++ } ++ } ++ ++ private final int digestLength; ++ ++ private final String algorithm; ++ private final long initContext; ++ ++ // field for ensuring native memory is freed ++ private DigestContextRef contextRef = null; ++ ++ KAEDigest(String algorithm, int digestLength, long initContext) { ++ this.algorithm = algorithm; ++ this.digestLength = digestLength; ++ this.initContext = initContext; ++ } ++ ++ 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 final long ctxAddress; ++ ++ DigestContextRef(KAEDigest kaeDigest, long ctxAddress) { ++ super(kaeDigest, referenceQueue); ++ this.ctxAddress = ctxAddress; ++ if (!disableKaeDispose) { ++ referenceList.add(this); ++ drainRefQueueBounded(); ++ } ++ } ++ ++ @Override ++ public int compareTo(DigestContextRef other) { ++ if (this.ctxAddress == other.ctxAddress) { ++ return 0; ++ } else { ++ return (this.ctxAddress < other.ctxAddress) ? -1 : 1; ++ } ++ } ++ ++ private static void drainRefQueueBounded() { ++ while (true) { ++ DigestContextRef next = (DigestContextRef) referenceQueue.poll(); ++ if (next == null) { ++ break; ++ } ++ next.dispose(); ++ } ++ } ++ ++ void dispose() { ++ if (!disableKaeDispose) { ++ referenceList.remove(this); ++ try { ++ nativeFree(ctxAddress); ++ } finally { ++ this.clear(); ++ } ++ } else { ++ nativeFree(ctxAddress); ++ } ++ } ++ } ++ ++ // single byte update. See JCA doc. ++ @Override ++ protected synchronized void engineUpdate(byte input) { ++ byte[] oneByte = new byte[]{input}; ++ engineUpdate(oneByte, 0, 1); ++ } ++ ++ ++ // array update. See JCA doc. ++ @Override ++ protected synchronized void engineUpdate(byte[] input, int offset, int len) { ++ if (len == 0 || input == null) { ++ return; ++ } ++ if ((offset < 0) || (len < 0) || (offset > input.length - len)) { ++ throw new ArrayIndexOutOfBoundsException(); ++ } ++ if (contextRef == null) { ++ contextRef = createDigestContext(this); ++ } ++ ++ try { ++ nativeUpdate(contextRef.ctxAddress, input, offset, len); ++ } catch (Exception e) { ++ engineReset(); ++ throw new ProviderException("nativeUpdate failed for " + algorithm, e); ++ } ++ } ++ ++ ++ // return the digest. See JCA doc. ++ @Override ++ protected synchronized byte[] engineDigest() { ++ final byte[] output = new byte[digestLength]; ++ try { ++ engineDigest(output, 0, digestLength); ++ } catch (Exception e) { ++ throw new ProviderException("Internal error", e); ++ } ++ return output; ++ } ++ ++ // return the digest in the specified array. See JCA doc. ++ @Override ++ protected int engineDigest(byte[] output, int offset, int len) throws DigestException { ++ if (output == null) { ++ return 0; ++ } ++ if (len < digestLength) { ++ throw new DigestException("Length must be at least " ++ + digestLength + " for " + algorithm + " digests"); ++ } ++ if ((offset < 0) || (len < 0) || (offset > output.length - len)) { ++ throw new DigestException("Buffer too short to store digest"); ++ } ++ if (contextRef == null) { ++ contextRef = createDigestContext(this); ++ } ++ try { ++ nativeDigest(contextRef.ctxAddress, output, offset, digestLength); ++ } catch (Exception e) { ++ throw new ProviderException("Invoke nativeDigest failed for " + algorithm, e); ++ } finally { ++ engineReset(); ++ } ++ return digestLength; ++ } ++ ++ // reset this object. See JCA doc. ++ @Override ++ protected synchronized void engineReset() { ++ if (contextRef != null) { ++ contextRef.dispose(); ++ contextRef = null; ++ } ++ } ++ ++ // return digest length. See JCA doc. ++ @Override ++ protected int engineGetDigestLength() { ++ return digestLength; ++ } ++ ++ @Override ++ public synchronized Object clone() throws CloneNotSupportedException { ++ KAEDigest kaeDigest = (KAEDigest) super.clone(); ++ if (kaeDigest.contextRef != null && kaeDigest.contextRef.ctxAddress != 0) { ++ long addr; ++ try { ++ addr = nativeClone(kaeDigest.contextRef.ctxAddress); ++ } catch (Exception e) { ++ throw new ProviderException("Invoke nativeClone failed for " + algorithm, e); ++ } ++ kaeDigest.contextRef = new DigestContextRef(kaeDigest, addr); ++ } ++ return kaeDigest; ++ } ++ ++ private DigestContextRef createDigestContext(KAEDigest kaeDigest) { ++ long addr; ++ try { ++ addr = nativeClone(initContext); ++ } catch (Exception e) { ++ throw new ProviderException("Invoke nativeInit failed for " + algorithm, e); ++ } ++ if (addr == 0) { ++ throw new RuntimeException("Cannot initialize EVP_MD_CTX for " + algorithm); ++ } ++ return new DigestContextRef(kaeDigest, addr); ++ } ++ ++ // return pointer to the context ++ protected native static long nativeInit(String algorithmName); ++ ++ // update the input byte ++ protected native static void nativeUpdate(long ctxAddress, byte[] input, int offset, int inLen); ++ ++ // digest and store the digest message to output ++ protected native static int nativeDigest(long ctxAddress, byte[] output, int offset, int len); ++ ++ // digest clone ++ protected static native long nativeClone(long ctxAddress); ++ ++ // free the specified context ++ protected native static void nativeFree(long ctxAddress); ++} +diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEECDHKeyAgreement.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEECDHKeyAgreement.java +new file mode 100644 +index 000000000..29dc09889 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEECDHKeyAgreement.java +@@ -0,0 +1,146 @@ ++/* ++ * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2023, 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. 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.openeuler.security.openssl; ++ ++import sun.security.ec.ECKeyFactory; ++ ++import java.math.BigInteger; ++import java.security.InvalidAlgorithmParameterException; ++import java.security.InvalidParameterException; ++import java.security.InvalidKeyException; ++import java.security.Key; ++import java.security.NoSuchAlgorithmException; ++import java.security.PrivateKey; ++import java.security.SecureRandom; ++import java.security.interfaces.ECPrivateKey; ++import java.security.interfaces.ECPublicKey; ++import java.security.spec.AlgorithmParameterSpec; ++import java.security.spec.ECParameterSpec; ++import java.security.spec.ECPoint; ++import javax.crypto.KeyAgreementSpi; ++import javax.crypto.SecretKey; ++import javax.crypto.ShortBufferException; ++import javax.crypto.spec.SecretKeySpec; ++ ++public class KAEECDHKeyAgreement extends KeyAgreementSpi { ++ private ECPrivateKey privateKey; ++ private ECPublicKey publicKey; ++ ++ // Length of the secret to be derived. ++ private int expectedSecretLen; ++ private String curveName; ++ ++ @Override ++ protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException { ++ if (!(key instanceof PrivateKey)) { ++ throw new InvalidKeyException("Key must be instance of PrivateKey"); ++ } ++ privateKey = (ECPrivateKey) ECKeyFactory.toECKey(key); ++ publicKey = null; ++ } ++ ++ @Override ++ protected void engineInit(Key key, AlgorithmParameterSpec params, SecureRandom random) ++ throws InvalidKeyException, InvalidAlgorithmParameterException { ++ if (params != null) { ++ throw new InvalidAlgorithmParameterException("Parameters not supported"); ++ } ++ engineInit(key, random); ++ } ++ ++ @Override ++ protected Key engineDoPhase(Key key, boolean lastPhase) throws InvalidKeyException, IllegalStateException { ++ if (privateKey == null) { ++ throw new IllegalStateException("Not initialized"); ++ } ++ if (publicKey != null) { ++ throw new IllegalStateException("Phase already executed"); ++ } ++ if (!lastPhase) { ++ throw new IllegalStateException ++ ("Only two party agreement supported, lastPhase must be true"); ++ } ++ if (!(key instanceof ECPublicKey)) { ++ throw new InvalidKeyException ++ ("Key must be a PublicKey with algorithm EC"); ++ } ++ ++ publicKey = (ECPublicKey) key; ++ ECParameterSpec params = publicKey.getParams(); ++ int keyLenBits = params.getCurve().getField().getFieldSize(); ++ // Bits to bytes. ++ expectedSecretLen = (keyLenBits + 7) >> 3; ++ ++ curveName = KAEUtils.getCurveBySize(keyLenBits); ++ if (curveName == null) { ++ throw new InvalidParameterException("unknown keyLenBits " + keyLenBits); ++ } ++ if (KAEUtils.getCurveByAlias(curveName) != null) { ++ curveName = KAEUtils.getCurveByAlias(curveName); ++ } ++ return null; ++ } ++ ++ @Override ++ protected byte[] engineGenerateSecret() throws IllegalStateException { ++ if ((privateKey == null) || (publicKey == null)) { ++ throw new IllegalStateException("Not initialized correctly"); ++ } ++ ECPoint w = publicKey.getW(); ++ BigInteger wX = w.getAffineX(); ++ BigInteger wY = w.getAffineY(); ++ ++ BigInteger s = privateKey.getS(); ++ byte[] secret = nativeGenerateSecret(curveName, wX.toByteArray(), wY.toByteArray(), s.toByteArray()); ++ if (secret == null || secret.length != expectedSecretLen) { ++ throw new RuntimeException("nativeGenerateSecret error. Expected: " + expectedSecretLen + ", actual: " + (secret == null ? "null" : secret.length)); ++ } ++ return secret; ++ } ++ ++ @Override ++ protected int engineGenerateSecret(byte[] sharedSecret, int offset) throws IllegalStateException, ShortBufferException { ++ if (offset + expectedSecretLen > sharedSecret.length) { ++ throw new ShortBufferException("Need " + expectedSecretLen + ++ " bytes, only " + (sharedSecret.length - offset) + "available"); ++ } ++ byte[] secret = engineGenerateSecret(); ++ System.arraycopy(secret, 0, sharedSecret, offset, secret.length); ++ return secret.length; ++ } ++ ++ @Override ++ protected SecretKey engineGenerateSecret(String algorithm) throws IllegalStateException, ++ NoSuchAlgorithmException, InvalidKeyException { ++ if (algorithm == null) { ++ throw new NoSuchAlgorithmException("Algorithm must not be null"); ++ } ++ return new SecretKeySpec(engineGenerateSecret(), algorithm); ++ } ++ ++ protected static native byte[] nativeGenerateSecret(String curveName, byte[] wX, byte[] wY, byte[] s); ++} +diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEECKeyPairGenerator.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEECKeyPairGenerator.java +new file mode 100644 +index 000000000..73d8551b1 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEECKeyPairGenerator.java +@@ -0,0 +1,151 @@ ++/* ++ * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2023, 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. 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.openeuler.security.openssl; ++ ++import sun.security.ec.ECPrivateKeyImpl; ++import sun.security.ec.ECPublicKeyImpl; ++ ++import java.math.BigInteger; ++import java.security.InvalidAlgorithmParameterException; ++import java.security.InvalidParameterException; ++import java.security.KeyPair; ++import java.security.KeyPairGeneratorSpi; ++import java.security.ProviderException; ++import java.security.SecureRandom; ++import java.security.spec.AlgorithmParameterSpec; ++import java.security.spec.ECFieldFp; ++import java.security.spec.ECField; ++import java.security.spec.ECGenParameterSpec; ++import java.security.spec.ECParameterSpec; ++import java.security.spec.ECPoint; ++import java.security.spec.EllipticCurve; ++ ++import java.lang.reflect.Constructor; ++import java.lang.reflect.InvocationTargetException; ++ ++public class KAEECKeyPairGenerator extends KeyPairGeneratorSpi { ++ private ECParameterSpec param = null; ++ private final int defaultKeySize = 256; ++ ++ @Override ++ public void initialize(int keysize, SecureRandom random) { ++ String curveName = KAEUtils.getCurveBySize(keysize); ++ if (curveName == null) { ++ throw new InvalidParameterException("unknown key size " + keysize); ++ } ++ if (KAEUtils.getCurveByAlias(curveName) != null) { ++ curveName = KAEUtils.getCurveByAlias(curveName); ++ } ++ this.param = getParamsByCurve(curveName); ++ } ++ ++ private ECParameterSpec getParamsByCurve(String curveName) { ++ byte[][] params = nativeGenerateParam(curveName); ++ if (params == null) { ++ throw new InvalidParameterException("unknown curve " + curveName); ++ } ++ BigInteger p = new BigInteger(params[0]); ++ BigInteger a = new BigInteger(params[1]); ++ BigInteger b = new BigInteger(params[2]); ++ BigInteger x = new BigInteger(params[3]); ++ BigInteger y = new BigInteger(params[4]); ++ BigInteger order = new BigInteger(params[5]); ++ BigInteger cofactor = new BigInteger(params[6]); ++ ECField field = new ECFieldFp(p); ++ EllipticCurve curve = new EllipticCurve(field, a, b); ++ ECPoint g = new ECPoint(x, y); ++ ECParameterSpec spec = new ECParameterSpec(curve, g, order, cofactor.intValue()); ++ return spec; ++ } ++ ++ @Override ++ public void initialize(AlgorithmParameterSpec param, SecureRandom random) throws InvalidAlgorithmParameterException { ++ if (param instanceof ECParameterSpec) { ++ this.param = (ECParameterSpec) param; ++ } else if (param instanceof ECGenParameterSpec) { ++ ECGenParameterSpec ecParam = (ECGenParameterSpec)param; ++ String curveName = ecParam.getName(); ++ if (KAEUtils.getCurveByAlias(curveName) != null) { ++ curveName = KAEUtils.getCurveByAlias(curveName); ++ } ++ this.param = getParamsByCurve(curveName); ++ } else { ++ throw new InvalidAlgorithmParameterException("ECParameterSpec or ECGenParameterSpec for EC"); ++ } ++ } ++ ++ @Override ++ public KeyPair generateKeyPair() { ++ if (param == null) { ++ String curveName = KAEUtils.getCurveBySize(defaultKeySize); ++ param = getParamsByCurve(curveName); ++ } ++ EllipticCurve curve = param.getCurve(); ++ ECFieldFp field = (ECFieldFp) curve.getField(); ++ BigInteger p = field.getP(); ++ BigInteger a = curve.getA(); ++ BigInteger b = curve.getB(); ++ ECPoint generator = param.getGenerator(); ++ BigInteger x = generator.getAffineX(); ++ BigInteger y = generator.getAffineY(); ++ BigInteger order = param.getOrder(); ++ int cofactor = param.getCofactor(); ++ ++ byte[][] keys = nativeGenerateKeyPair(p.toByteArray(), a.toByteArray(), ++ b.toByteArray(), x.toByteArray(), y.toByteArray(), order.toByteArray(), cofactor); ++ if (keys == null) { ++ throw new RuntimeException("nativeGenerateKeyPair failed"); ++ } ++ BigInteger wX = new BigInteger(keys[0]); ++ BigInteger wY = new BigInteger(keys[1]); ++ BigInteger s = new BigInteger(keys[2]); ++ ECPoint w = new ECPoint(wX, wY); ++ ++ ECPrivateKeyImpl privateKey = null; ++ ECPublicKeyImpl publicKey = null; ++ try { ++ Class pubKeyImpl = Class.forName("sun.security.ec.ECPublicKeyImpl"); ++ Constructor conPubKeyImpl = pubKeyImpl.getDeclaredConstructor(ECPoint.class, ECParameterSpec.class); ++ conPubKeyImpl.setAccessible(true); ++ publicKey = (ECPublicKeyImpl) conPubKeyImpl.newInstance(w, param); ++ ++ Class priKeyImpl = Class.forName("sun.security.ec.ECPrivateKeyImpl"); ++ Constructor conPriKeyImpl = priKeyImpl.getDeclaredConstructor(BigInteger.class, ECParameterSpec.class); ++ conPriKeyImpl.setAccessible(true); ++ privateKey = (ECPrivateKeyImpl) conPriKeyImpl.newInstance(s, param); ++ } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | ++ IllegalAccessException | InvocationTargetException e) { ++ throw new ProviderException(e); ++ } ++ return new KeyPair(publicKey, privateKey); ++ } ++ ++ protected static native byte[][] nativeGenerateParam(String curveName); ++ ++ protected static native byte[][] nativeGenerateKeyPair(byte[] p, byte[] a, byte[] b, byte[] x, byte[] y, ++ byte[] order, int cofactor); ++} +diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEHMac.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEHMac.java +new file mode 100644 +index 000000000..3ca909aa1 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEHMac.java +@@ -0,0 +1,227 @@ ++/* ++ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2023, 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 javax.crypto.MacSpi; ++import javax.crypto.SecretKey; ++import java.lang.ref.PhantomReference; ++import java.lang.ref.ReferenceQueue; ++import java.security.*; ++import java.security.spec.AlgorithmParameterSpec; ++import java.util.Set; ++import java.util.concurrent.ConcurrentSkipListSet; ++ ++public abstract class KAEHMac extends MacSpi { ++ ++ private final String algorithm; ++ ++ /** ++ * The secret key used in this keyed HMAC. ++ */ ++ private byte[] keyBytes; ++ ++ /** ++ * Holds the output size of the message digest. ++ */ ++ private final int digestSize; ++ ++ /** ++ * Holds a dummy buffer for writing single bytes to the digest. ++ */ ++ private final byte[] singleByte = new byte[1]; ++ ++ private HmacContextRef contextRef = null; ++ ++ private KAEHMac(String algo, int size) { ++ this.algorithm = algo; ++ this.digestSize = size; ++ } ++ ++ private static class HmacContextRef 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 final long address; ++ ++ HmacContextRef(KAEHMac kaeHMac, long address) { ++ super(kaeHMac, referenceQueue); ++ this.address = address; ++ if (!disableKaeDispose) { ++ referenceList.add(this); ++ drainRefQueueBounded(); ++ } ++ } ++ ++ @Override ++ public int compareTo(HmacContextRef other) { ++ if (this.address == other.address) { ++ return 0; ++ } else { ++ return (this.address < other.address) ? -1 : 1; ++ } ++ } ++ ++ private static void drainRefQueueBounded() { ++ while (true) { ++ HmacContextRef next = (HmacContextRef) referenceQueue.poll(); ++ if (next == null) break; ++ next.dispose(true); ++ } ++ } ++ ++ void dispose(boolean needFree) { ++ if (!disableKaeDispose) { ++ referenceList.remove(this); ++ try { ++ if (needFree) { ++ nativeFree(address); ++ } ++ } finally { ++ this.clear(); ++ } ++ } else { ++ nativeFree(address); ++ } ++ } ++ } ++ ++ private void checkAndInitHmacContext () { ++ try { ++ if (contextRef == null) { ++ long ctxAddr = nativeInit(keyBytes, keyBytes.length, algorithm); ++ contextRef = new HmacContextRef(this, ctxAddr); ++ } ++ } ++ catch (Exception e) { ++ throw new ProviderException(e.getMessage()) ; ++ } ++ } ++ ++ @Override ++ protected int engineGetMacLength() { ++ return digestSize; ++ } ++ ++ @Override ++ protected void engineInit(Key key, AlgorithmParameterSpec params) ++ throws InvalidKeyException, InvalidAlgorithmParameterException { ++ if (!(key instanceof SecretKey)) { ++ throw new InvalidKeyException("key must be a SecretKey"); ++ } ++ if (params != null) { ++ throw new InvalidAlgorithmParameterException("unknown parameter type"); ++ } ++ keyBytes = key.getEncoded(); ++ if (keyBytes == null) { ++ throw new InvalidKeyException("key cannot be encoded"); ++ } ++ } ++ ++ @Override ++ protected void engineUpdate(byte input) { ++ singleByte[0] = input; ++ engineUpdate(singleByte, 0, 1); ++ } ++ ++ @Override ++ protected void engineUpdate(byte[] input, int offset, int len) { ++ checkAndInitHmacContext(); ++ try { ++ nativeUpdate(contextRef.address, input, offset, len); ++ } ++ catch (Exception e) { ++ engineReset(); ++ throw new ProviderException(e.getMessage()); ++ } ++ } ++ ++ ++ @Override ++ protected byte[] engineDoFinal() { ++ final byte[] output = new byte[digestSize]; ++ checkAndInitHmacContext(); ++ final byte[] res; ++ try { ++ int bytesWritten = nativeFinal(contextRef.address, output, 0, digestSize); ++ res = new byte[bytesWritten]; ++ System.arraycopy(output, 0, res, 0, bytesWritten); ++ } ++ catch (Exception e) { ++ engineReset(); ++ throw new ProviderException(e.getMessage()); ++ } ++ return res; ++ } ++ ++ @Override ++ protected void engineReset() { ++ if (contextRef != null) { ++ contextRef.dispose(true); ++ contextRef = null; ++ } ++ } ++ ++ public static final class HmacMD5 extends KAEHMac { ++ public HmacMD5() { ++ super("MD5", 16); ++ } ++ } ++ public static final class HmacSHA1 extends KAEHMac { ++ public HmacSHA1() { ++ super("SHA1", 20); ++ } ++ } ++ public static final class HmacSHA224 extends KAEHMac { ++ public HmacSHA224() throws NoSuchAlgorithmException { ++ super("SHA224", 28); ++ } ++ } ++ public static final class HmacSHA256 extends KAEHMac { ++ public HmacSHA256() throws NoSuchAlgorithmException { ++ super("SHA256", 32); ++ } ++ } ++ public static final class HmacSHA384 extends KAEHMac { ++ public HmacSHA384() throws NoSuchAlgorithmException { ++ super("SHA384", 48); ++ } ++ } ++ public static final class HmacSHA512 extends KAEHMac { ++ public HmacSHA512() throws NoSuchAlgorithmException { ++ super("SHA512", 64); ++ } ++ } ++ ++ protected static native long nativeInit(byte[] key, int len, String algo); ++ ++ protected static native void nativeUpdate(long ctxAddr, byte[] input, int inOffset, int inLen); ++ ++ protected static native int nativeFinal(long ctxAddr, byte[] output, int outOffset, int inLen); ++ ++ protected static native void nativeFree(long ctxAddr); ++} +diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAELog.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAELog.java +new file mode 100644 +index 000000000..33c1c430f +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAELog.java +@@ -0,0 +1,188 @@ ++/* ++ * Copyright (c) 2023, 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 { ++ kaeLogInit(); ++ } ++ ++ @SuppressWarnings("removal") ++ private static void kaeLogInit() { ++ 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); ++ } ++ } ++} +\ No newline at end of file +diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEMGF1.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEMGF1.java +new file mode 100644 +index 000000000..dc79a3f62 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEMGF1.java +@@ -0,0 +1,94 @@ ++/* ++ * Copyright (c) 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. 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.openeuler.security.openssl; ++ ++import java.security.*; ++ ++/** ++ * This class implements the MGF1 mask generation function defined in PKCS#1 ++ * v2.2 B.2.1 (https://tools.ietf.org/html/rfc8017#appendix-B.2.1). A mask ++ * generation function takes an octet string of variable length and a ++ * desired output length as input and outputs an octet string of the ++ * desired length. MGF1 is a mask generation function based on a hash ++ * function, i.e. message digest algorithm. ++ * ++ * @since 11 ++ */ ++final class KAEMGF1 { ++ ++ private final MessageDigest md; ++ ++ /** ++ * Construct an instance of MGF1 based on the specified digest algorithm. ++ */ ++ KAEMGF1(String mdAlgo) throws NoSuchAlgorithmException { ++ this.md = MessageDigest.getInstance(mdAlgo); ++ } ++ ++ /** ++ * Using the specified seed bytes, generate the mask, xor the mask ++ * with the specified output buffer and store the result into the ++ * output buffer (essentially replaced in place). ++ * ++ * @param seed the buffer holding the seed bytes ++ * @param seedOfs the index of the seed bytes ++ * @param seedLen the length of the seed bytes to be used by MGF1 ++ * @param maskLen the intended length of the generated mask ++ * @param out the output buffer holding the mask ++ * @param outOfs the index of the output buffer for the mask ++ */ ++ void generateAndXor(byte[] seed, int seedOfs, int seedLen, int maskLen, ++ byte[] out, int outOfs) throws RuntimeException { ++ byte[] C = new byte[4]; // 32 bit counter ++ byte[] digest = new byte[md.getDigestLength()]; ++ while (maskLen > 0) { ++ md.update(seed, seedOfs, seedLen); ++ md.update(C); ++ try { ++ md.digest(digest, 0, digest.length); ++ } catch (DigestException e) { ++ // should never happen ++ throw new RuntimeException(e.toString()); ++ } ++ for (int i = 0; (i < digest.length) && (maskLen > 0); maskLen--) { ++ out[outOfs++] ^= digest[i++]; ++ } ++ if (maskLen > 0) { ++ // increment counter ++ for (int i = C.length - 1; (++C[i] == 0) && (i > 0); i--) { ++ // empty ++ } ++ } ++ } ++ } ++ ++ /** ++ * Returns the name of this MGF1 instance, i.e. "MGF1" followed by the ++ * digest algorithm it based on. ++ */ ++ String getName() { ++ return "KAEMGF1" + md.getAlgorithm(); ++ } ++} +diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEProvider.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEProvider.java +new file mode 100644 +index 000000000..b3c1fb022 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEProvider.java +@@ -0,0 +1,326 @@ ++/* ++ * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2023, 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.security.AccessController; ++import java.security.PrivilegedAction; ++import java.security.Provider; ++import static sun.security.util.SecurityConstants.PROVIDER_VER; ++ ++/** ++ * KAE Provider ++ */ ++@SuppressWarnings("serial") ++public class KAEProvider extends Provider { ++ private static final Debug kaeDebug = Debug.getInstance("kae"); ++ ++ // default engine id ++ private static final String DEFAULT_ENGINE_ID = "kae"; ++ ++ static { ++ initialize(); ++ } ++ ++ private static void initialize() { ++ loadLibrary(); ++ initOpenssl(); ++ } ++ ++ // load kae.so ++ @SuppressWarnings("removal") ++ private static void loadLibrary() { ++ AccessController.doPrivileged(new PrivilegedAction() { ++ @Override ++ public Object run() { ++ System.loadLibrary("j2kae"); ++ return null; ++ } ++ }); ++ } ++ ++ // 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()); ++ } ++ } ++ 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); ++ } ++ ++ @SuppressWarnings({"deprecation", "this-escape"}) ++ public KAEProvider() { ++ super("KAEProvider", PROVIDER_VER, "KAE provider"); ++ if (KAEConfig.useKaeProvider("kae.md5")) { ++ putMD5(); ++ } ++ if (KAEConfig.useKaeProvider("kae.sha256")) { ++ putSHA256(); ++ } ++ if (KAEConfig.useKaeProvider("kae.sha384")) { ++ putSHA384(); ++ } ++ if (KAEConfig.useKaeProvider("kae.sm3")) { ++ putSM3(); ++ } ++ if (KAEConfig.useKaeProvider("kae.aes")) { ++ putAES(); ++ } ++ if (KAEConfig.useKaeProvider("kae.sm4")) { ++ putSM4(); ++ } ++ if (KAEConfig.useKaeProvider("kae.hmac")) { ++ putHMAC(); ++ } ++ if (KAEConfig.useKaeProvider("kae.rsa")) { ++ putRSA(); ++ putSignatureRSA(); ++ } ++ if (KAEConfig.useKaeProvider("kae.dh")) { ++ putDH(); ++ } ++ if (KAEConfig.useKaeProvider("kae.ec")) { ++ putEC(); ++ } ++ } ++ ++ private void putAES() { ++ final String blockModes = "ECB|CBC|CTR|GCM"; ++ final String blockPads = "NOPADDING|PKCS5PADDING"; ++ ++ put("Cipher.AES SupportedModes", blockModes); ++ put("Cipher.AES SupportedPaddings", blockPads); ++ put("Cipher.AES", "org.openeuler.security.openssl.KAEAESCipher$Aes$Ecb$PKCS5Padding"); ++ ++ put("Cipher.AES/CBC/PKCS5Padding", "org.openeuler.security.openssl.KAEAESCipher$Aes$Cbc$PKCS5Padding"); ++ put("Cipher.AES/CBC/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes$Cbc$NoPadding"); ++ put("Alg.Alias.Cipher.AES/CBC/PKCS7Padding", "AES/CBC/PKCS5Padding"); ++ put("Cipher.AES/ECB/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes$Ecb$NoPadding"); ++ put("Cipher.AES/ECB/PKCS5Padding", "org.openeuler.security.openssl.KAEAESCipher$Aes$Ecb$PKCS5Padding"); ++ put("Alg.Alias.Cipher.AES/ECB/PKCS7Padding", "AES/ECB/PKCS5Padding"); ++ put("Cipher.AES/CTR/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes$Ctr$NoPadding"); ++ put("Cipher.AES/GCM/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes$Gcm$NoPadding"); ++ put("Alg.Alias.Cipher.AES/GCM/PKCS5Padding", "AES/GCM/NoPadding"); // PKCS5Padding -> noPadding ++ ++ put("Cipher.AES_128/CBC/PKCS5Padding", "org.openeuler.security.openssl.KAEAESCipher$Aes_128$Cbc$PKCS5Padding"); ++ put("Cipher.AES_128/CBC/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_128$Cbc$NoPadding"); ++ put("Alg.Alias.Cipher.AES_128/CBC/PKCS7Padding", "AES_128/CBC/PKCS5Padding"); ++ put("Cipher.AES_128/ECB/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_128$Ecb$NoPadding"); ++ put("Cipher.AES_128/ECB/PKCS5Padding", "org.openeuler.security.openssl.KAEAESCipher$Aes_128$Ecb$PKCS5Padding"); ++ put("Alg.Alias.Cipher.AES_128/ECB/PKCS7Padding", "AES_128/ECB/PKCS5Padding"); ++ put("Cipher.AES_128/CTR/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_128$Ctr$NoPadding"); ++ put("Cipher.AES_128/GCM/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_128$Gcm$NoPadding"); ++ put("Alg.Alias.Cipher.AES_128/GCM/PKCS5Padding", "AES/GCM/NoPadding"); ++ ++ put("Cipher.AES_192/CBC/PKCS5Padding", "org.openeuler.security.openssl.KAEAESCipher$Aes_192$Cbc$PKCS5Padding"); ++ put("Cipher.AES_192/CBC/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_192$Cbc$NoPadding"); ++ put("Alg.Alias.Cipher.AES_192/CBC/PKCS7Padding", "AES_192/CBC/PKCS5Padding"); ++ put("Cipher.AES_192/ECB/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_192$Ecb$NoPadding"); ++ put("Cipher.AES_192/ECB/PKCS5Padding", "org.openeuler.security.openssl.KAEAESCipher$Aes_192$Ecb$PKCS5Padding"); ++ put("Alg.Alias.Cipher.AES_192/ECB/PKCS7Padding", "AES_192/ECB/PKCS5Padding"); ++ put("Cipher.AES_192/CTR/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_192$Ctr$NoPadding"); ++ put("Cipher.AES_192/GCM/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_192$Gcm$NoPadding"); ++ put("Alg.Alias.Cipher.AES_192/GCM/PKCS5Padding", "AES/GCM/NoPadding"); ++ ++ put("Cipher.AES_256/CBC/PKCS5Padding", "org.openeuler.security.openssl.KAEAESCipher$Aes_256$Cbc$PKCS5Padding"); ++ put("Cipher.AES_256/CBC/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_256$Cbc$NoPadding"); ++ put("Alg.Alias.Cipher.AES_256/CBC/PKCS7Padding", "AES_256/CBC/PKCS5Padding"); ++ put("Cipher.AES_256/ECB/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_256$Ecb$NoPadding"); ++ put("Cipher.AES_256/ECB/PKCS5Padding", "org.openeuler.security.openssl.KAEAESCipher$Aes_256$Ecb$PKCS5Padding"); ++ put("Alg.Alias.Cipher.AES_256/ECB/PKCS7Padding", "AES_256/ECB/PKCS5Padding"); ++ put("Cipher.AES_256/CTR/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_256$Ctr$NoPadding"); ++ put("Cipher.AES_256/GCM/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_256$Gcm$NoPadding"); ++ put("Alg.Alias.Cipher.AES_256/GCM/PKCS5Padding", "AES/GCM/NoPadding"); ++ } ++ ++ private void putMD5() { ++ put("MessageDigest.MD5", "org.openeuler.security.openssl.KAEDigest$MD5"); ++ } ++ ++ private void putSHA256() { ++ put("MessageDigest.SHA-256", "org.openeuler.security.openssl.KAEDigest$SHA256"); ++ } ++ ++ private void putSHA384() { ++ put("MessageDigest.SHA-384", "org.openeuler.security.openssl.KAEDigest$SHA384"); ++ } ++ ++ private void putSM3() { ++ put("MessageDigest.SM3", "org.openeuler.security.openssl.KAEDigest$SM3"); ++ } ++ ++ private void putSM4() { ++ final String blockModes = "ECB|CBC|CTR|OFB"; ++ final String blockPads = "NOPADDING|PKCS5PADDING"; ++ ++ put("Cipher.SM4 SupportedModes", blockModes); ++ put("Cipher.SM4 SupportedPaddings", blockPads); ++ put("Cipher.SM4", "org.openeuler.security.openssl.KAESM4Cipher$Sm4$Ecb$PKCS5Padding"); ++ ++ put("Cipher.SM4/CBC/PKCS5Padding", "org.openeuler.security.openssl.KAESM4Cipher$Sm4$Cbc$PKCS5Padding"); ++ put("Cipher.SM4/CBC/NoPadding", "org.openeuler.security.openssl.KAESM4Cipher$Sm4$Cbc$NoPadding"); ++ put("Alg.Alias.Cipher.SM4/CBC/PKCS7Padding", "SM4/CBC/PKCS5Padding"); ++ put("Cipher.SM4/ECB/NoPadding", "org.openeuler.security.openssl.KAESM4Cipher$Sm4$Ecb$NoPadding"); ++ put("Cipher.SM4/ECB/PKCS5Padding", "org.openeuler.security.openssl.KAESM4Cipher$Sm4$Ecb$PKCS5Padding"); ++ put("Alg.Alias.Cipher.SM4/ECB/PKCS7Padding", "SM4/ECB/PKCS5Padding"); ++ put("Cipher.SM4/CTR/NoPadding", "org.openeuler.security.openssl.KAESM4Cipher$Sm4$Ctr$NoPadding"); ++ put("Cipher.SM4/OFB/NoPadding", "org.openeuler.security.openssl.KAESM4Cipher$Sm4$Ofb$NoPadding"); ++ put("Cipher.SM4/OFB/PKCS5Padding", "org.openeuler.security.openssl.KAESM4Cipher$Sm4$Ofb$PKCS5Padding"); ++ put("Alg.Alias.Cipher.SM4/OFB/PKCS7Padding", "SM4/OFB/PKCS5Padding"); ++ ++ put("KeyGenerator.SM4", "org.openeuler.security.openssl.KAESM4KeyGenerator"); ++ put("AlgorithmParameters.SM4", "com.sun.crypto.provider.AESParameters"); ++ } ++ ++ private void putRSA() { ++ // rsa ++ put("KeyPairGenerator.RSA", "org.openeuler.security.openssl.KAERSAKeyPairGenerator$Legacy"); ++ put("Alg.Alias.KeyPairGenerator.1.2.840.113549.1.1", "RSA"); ++ put("Alg.Alias.KeyPairGenerator.OID.1.2.840.113549.1.1", "RSA"); ++ ++ put("KeyPairGenerator.RSASSA-PSS", "org.openeuler.security.openssl.KAERSAKeyPairGenerator$PSS"); ++ put("Alg.Alias.KeyPairGenerator.1.2.840.113549.1.1.10", "RSASSA-PSS"); ++ put("Alg.Alias.KeyPairGenerator.OID.1.2.840.113549.1.1.10", "RSASSA-PSS"); ++ ++ put("Cipher.RSA", "org.openeuler.security.openssl.KAERSACipher"); ++ put("Cipher.RSA SupportedModes", "ECB"); ++ put("Cipher.RSA SupportedPaddings", ++ "NOPADDING|PKCS1PADDING|OAEPPADDING" ++ + "|OAEPWITHMD5ANDMGF1PADDING" ++ + "|OAEPWITHSHA1ANDMGF1PADDING" ++ + "|OAEPWITHSHA-1ANDMGF1PADDING" ++ + "|OAEPWITHSHA-224ANDMGF1PADDING" ++ + "|OAEPWITHSHA-256ANDMGF1PADDING" ++ + "|OAEPWITHSHA-384ANDMGF1PADDING" ++ + "|OAEPWITHSHA-512ANDMGF1PADDING" ++ + "|OAEPWITHSHA-512/224ANDMGF1PADDING" ++ + "|OAEPWITHSHA-512/256ANDMGF1PADDING"); ++ put("Cipher.RSA SupportedKeyClasses", ++ "java.security.interfaces.RSAPublicKey" + ++ "|java.security.interfaces.RSAPrivateKey"); ++ } ++ ++ private void putHMAC() { ++ put("MAC.HmacMD5", "org.openeuler.security.openssl.KAEHMac$HmacMD5"); ++ put("MAC.HmacSHA1", "org.openeuler.security.openssl.KAEHMac$HmacSHA1"); ++ put("MAC.HmacSHA224", "org.openeuler.security.openssl.KAEHMac$HmacSHA224"); ++ put("MAC.HmacSHA256", "org.openeuler.security.openssl.KAEHMac$HmacSHA256"); ++ put("MAC.HmacSHA384", "org.openeuler.security.openssl.KAEHMac$HmacSHA384"); ++ put("MAC.HmacSHA512", "org.openeuler.security.openssl.KAEHMac$HmacSHA512"); ++ } ++ ++ private void putDH() { ++ put("KeyPairGenerator.DiffieHellman", "org.openeuler.security.openssl.KAEDHKeyPairGenerator"); ++ put("Alg.Alias.KeyPairGenerator.DH", "DiffieHellman"); ++ put("KeyAgreement.DiffieHellman", "org.openeuler.security.openssl.KAEDHKeyAgreement"); ++ put("Alg.Alias.KeyAgreement.DH", "DiffieHellman"); ++ } ++ ++ private void putSignatureRSA() { ++ put("Signature.MD5withRSA", ++ "org.openeuler.security.openssl.KAERSASignature$MD5withRSA"); ++ put("Signature.SHA1withRSA", ++ "org.openeuler.security.openssl.KAERSASignature$SHA1withRSA"); ++ put("Signature.SHA224withRSA", ++ "org.openeuler.security.openssl.KAERSASignature$SHA224withRSA"); ++ put("Signature.SHA256withRSA", ++ "org.openeuler.security.openssl.KAERSASignature$SHA256withRSA"); ++ put("Signature.SHA384withRSA", ++ "org.openeuler.security.openssl.KAERSASignature$SHA384withRSA"); ++ put("Signature.SHA512withRSA", ++ "org.openeuler.security.openssl.KAERSASignature$SHA512withRSA"); ++ ++ // alias ++ 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.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.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.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.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.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.OID.1.2.840.113549.1.1.10", "RSASSA-PSS"); ++ ++ // attributes for supported key classes ++ String rsaKeyClasses = "java.security.interfaces.RSAPublicKey" + ++ "|java.security.interfaces.RSAPrivateKey"; ++ put("Signature.MD5withRSA SupportedKeyClasses", rsaKeyClasses); ++ put("Signature.SHA1withRSA SupportedKeyClasses", rsaKeyClasses); ++ put("Signature.SHA224withRSA SupportedKeyClasses", rsaKeyClasses); ++ put("Signature.SHA256withRSA SupportedKeyClasses", rsaKeyClasses); ++ put("Signature.SHA384withRSA SupportedKeyClasses", rsaKeyClasses); ++ put("Signature.SHA512withRSA SupportedKeyClasses", rsaKeyClasses); ++ put("Signature.RSASSA-PSS SupportedKeyClasses", rsaKeyClasses); ++ } ++ ++ private void putEC() { ++ put("KeyPairGenerator.EC", "org.openeuler.security.openssl.KAEECKeyPairGenerator"); ++ put("Alg.Alias.KeyPairGenerator.EllipticCurve", "EC"); ++ put("KeyAgreement.ECDH", "org.openeuler.security.openssl.KAEECDHKeyAgreement"); ++ } ++ ++ // init openssl ++ static native void initOpenssl(boolean useGlobalMode, String engineId, boolean[] algorithmKaeFlags) ++ throws RuntimeException; ++ ++ static native boolean[] getEngineFlags(); ++} +\ No newline at end of file +diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSACipher.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSACipher.java +new file mode 100644 +index 000000000..a45fb0dac +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSACipher.java +@@ -0,0 +1,796 @@ ++/* ++ * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2023, 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. 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.openeuler.security.openssl; ++ ++import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec; ++import sun.security.jca.Providers; ++import sun.security.rsa.RSACore; ++import sun.security.rsa.RSAKeyFactory; ++import sun.security.rsa.RSAPadding; ++import sun.security.util.KeyUtil; ++ ++import javax.crypto.*; ++import javax.crypto.spec.OAEPParameterSpec; ++import javax.crypto.spec.PSource; ++import java.lang.ref.PhantomReference; ++import java.lang.ref.ReferenceQueue; ++import java.security.*; ++import java.security.interfaces.RSAKey; ++import java.security.interfaces.RSAPrivateCrtKey; ++import java.security.interfaces.RSAPrivateKey; ++import java.security.interfaces.RSAPublicKey; ++import java.security.spec.AlgorithmParameterSpec; ++import java.security.spec.InvalidParameterSpecException; ++import java.security.spec.MGF1ParameterSpec; ++import java.util.Arrays; ++import java.util.Locale; ++import java.util.Set; ++import java.util.concurrent.ConcurrentSkipListSet; ++ ++ ++/** ++ * RSA cipher implementation. Supports RSA en/decryption and signing/verifying ++ * using both PKCS#1 v1.5 and OAEP (v2.2) paddings and without padding (raw RSA). ++ * Note that raw RSA is supported mostly for completeness and should only be ++ * used in rare cases. ++ *

++ * Objects should be instantiated by calling Cipher.getInstance() using the ++ * following algorithm names: ++ * . "RSA/ECB/PKCS1Padding" (or "RSA") for PKCS#1 v1.5 padding. ++ * . "RSA/ECB/OAEPwithandMGF1Padding" (or "RSA/ECB/OAEPPadding") for ++ * PKCS#1 v2.2 padding. ++ * . "RSA/ECB/NoPadding" for rsa RSA. ++ *

++ * We only do one RSA operation per doFinal() call. If the application passes ++ * more data via calls to update() or doFinal(), we throw an ++ * IllegalBlockSizeException when doFinal() is called (see JCE API spec). ++ * Bulk encryption using RSA does not make sense and is not standardized. ++ *

++ * Note: RSA keys should be at least 512 bits long ++ */ ++public final class KAERSACipher extends CipherSpi { ++ // constant for an empty byte array ++ private final static byte[] B0 = new byte[0]; ++ ++ // mode constant for public key encryption ++ private final static int MODE_ENCRYPT = 1; ++ ++ // mode constant for private key decryption ++ private final static int MODE_DECRYPT = 2; ++ ++ // mode constant for private key encryption (signing) ++ private final static int MODE_SIGN = 3; ++ ++ // mode constant for public key decryption (verifying) ++ private final static int MODE_VERIFY = 4; ++ ++ // current mode, one of MODE_* above. Set when init() is called ++ private int mode; ++ ++ // active padding type, one of PAD_* above. Set by setPadding() ++ private KAERSAPaddingType paddingType; ++ ++ // padding object ++ private RSAPadding padding; ++ ++ // cipher parameter for OAEP padding and TLS RSA premaster secret ++ private AlgorithmParameterSpec spec = null; ++ ++ // buffer for the data ++ private byte[] buffer; ++ ++ // offset into the buffer (number of bytes buffered) ++ private int bufOfs; ++ ++ // size of the output ++ private int outputSize; ++ ++ // hash algorithm for OAEP ++ private String oaepHashAlgorithm = "SHA-1"; ++ ++ // the source of randomness ++ private SecureRandom random; ++ ++ private RSAKey rsaKey; ++ ++ // rsa key holder ++ private KAERSAKeyHolder rsaKeyHolder; ++ ++ ++ public KAERSACipher() { ++ paddingType = KAERSAPaddingType.PKCS1Padding; ++ } ++ ++ // modes do not make sense for RSA, but allow ECB ++ // see JCE spec ++ @Override ++ protected void engineSetMode(String mode) throws NoSuchAlgorithmException { ++ if (!mode.equalsIgnoreCase("ECB")) { ++ throw new NoSuchAlgorithmException("Unsupported mode " + mode); ++ } ++ } ++ ++ // set the padding type ++ // see JCE spec ++ @Override ++ protected void engineSetPadding(String paddingName) ++ throws NoSuchPaddingException { ++ if (KAERSAPaddingType.NoPadding.getName().equalsIgnoreCase(paddingName)) { ++ paddingType = KAERSAPaddingType.NoPadding; ++ } else if (KAERSAPaddingType.PKCS1Padding.getName().equalsIgnoreCase(paddingName)) { ++ paddingType = KAERSAPaddingType.PKCS1Padding; ++ } else { ++ String lowerPadding = paddingName.toLowerCase(Locale.ENGLISH); ++ if ("oaeppadding".equals(lowerPadding)) { ++ paddingType = KAERSAPaddingType.OAEP; ++ } else if (lowerPadding.startsWith("oaepwith") && ++ lowerPadding.endsWith("andmgf1padding")) { ++ paddingType = KAERSAPaddingType.OAEP; ++ // "oaepwith" length is 8 ++ // "andmgf1padding" length is 14 ++ oaepHashAlgorithm = ++ paddingName.substring(8, paddingName.length() - 14); ++ // check if MessageDigest appears to be available ++ // avoid getInstance() call here ++ if (Providers.getProviderList().getService ++ ("MessageDigest", oaepHashAlgorithm) == null) { ++ throw new NoSuchPaddingException ++ ("MessageDigest not available for " + paddingName); ++ } ++ } else { ++ throw new NoSuchPaddingException ++ ("Padding " + paddingName + " not supported"); ++ } ++ } ++ } ++ ++ // return 0 as block size, we are not a block cipher ++ // see JCE spec ++ @Override ++ protected int engineGetBlockSize() { ++ return 0; ++ } ++ ++ // return the output size ++ // see JCE spec ++ @Override ++ protected int engineGetOutputSize(int inputLen) { ++ return outputSize; ++ } ++ ++ // no iv, return null ++ // see JCE spec ++ @Override ++ protected byte[] engineGetIV() { ++ return null; ++ } ++ ++ // see JCE spec ++ @Override ++ protected AlgorithmParameters engineGetParameters() { ++ if (spec != null && spec instanceof OAEPParameterSpec) { ++ try { ++ AlgorithmParameters params = ++ AlgorithmParameters.getInstance("OAEP"); ++ params.init(spec); ++ return params; ++ } catch (NoSuchAlgorithmException nsae) { ++ // should never happen ++ throw new RuntimeException("Cannot find OAEP " + ++ " AlgorithmParameters implementation in SunJCE provider"); ++ } catch (InvalidParameterSpecException ipse) { ++ // should never happen ++ throw new RuntimeException("OAEPParameterSpec not supported"); ++ } ++ } else { ++ return null; ++ } ++ } ++ ++ // see JCE spec ++ @Override ++ protected void engineInit(int opmode, Key key, SecureRandom random) ++ throws InvalidKeyException { ++ try { ++ init(opmode, key, random, null); ++ } catch (InvalidAlgorithmParameterException iape) { ++ // never thrown when null parameters are used; ++ // but re-throw it just in case ++ throw new InvalidKeyException("Wrong parameters", iape); ++ } ++ } ++ ++ // see JCE spec ++ @Override ++ protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random) ++ throws InvalidKeyException, InvalidAlgorithmParameterException { ++ init(opmode, key, random, params); ++ } ++ ++ // see JCE spec ++ @Override ++ protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random) ++ throws InvalidKeyException, InvalidAlgorithmParameterException { ++ if (params == null) { ++ init(opmode, key, random, null); ++ } else { ++ try { ++ OAEPParameterSpec oaepParameterSpec = ++ params.getParameterSpec(OAEPParameterSpec.class); ++ init(opmode, key, random, oaepParameterSpec); ++ } catch (InvalidParameterSpecException ipse) { ++ InvalidAlgorithmParameterException iape = ++ new InvalidAlgorithmParameterException("Wrong parameter"); ++ iape.initCause(ipse); ++ throw iape; ++ } ++ } ++ } ++ ++ // check TlsRsaPremasterSecretParameterSpec ++ @SuppressWarnings("deprecation") ++ private void checkTlsRsaPremasterSecretParameterSpec(AlgorithmParameterSpec params) ++ throws InvalidAlgorithmParameterException { ++ if (!(params instanceof TlsRsaPremasterSecretParameterSpec)) { ++ throw new InvalidAlgorithmParameterException( ++ "Parameters not supported"); ++ } ++ } ++ ++ // check OAEPParameterSpec ++ private void checkOAEPParameterSpec(AlgorithmParameterSpec params) ++ throws InvalidAlgorithmParameterException { ++ if (!(params instanceof OAEPParameterSpec)) { ++ throw new InvalidAlgorithmParameterException ++ ("Wrong Parameters for OAEP Padding"); ++ } ++ ++ // check MGF algorithm ++ OAEPParameterSpec oaepParameterSpec = (OAEPParameterSpec) params; ++ String mgfName = oaepParameterSpec.getMGFAlgorithm(); ++ if (!mgfName.equalsIgnoreCase("MGF1")) { ++ throw new InvalidAlgorithmParameterException ++ ("Unsupported MGF algo: " + mgfName); ++ } ++ ++ // check PSource algorithm ++ PSource pSource = oaepParameterSpec.getPSource(); ++ String pSourceAlgorithm = pSource.getAlgorithm(); ++ if (!pSourceAlgorithm.equalsIgnoreCase("PSpecified")) { ++ throw new InvalidAlgorithmParameterException ++ ("Unsupported pSource algo: " + pSourceAlgorithm); ++ } ++ } ++ ++ // compute OAEP data buffer length ++ private int getOAEPBufferLen(int outputSize, OAEPParameterSpec oaepParameterSpec, boolean encrypt) ++ throws InvalidKeyException { ++ if (!encrypt) { ++ return outputSize; ++ } ++ String mdName = oaepParameterSpec.getDigestAlgorithm(); ++ String mgfMdName = ((MGF1ParameterSpec) oaepParameterSpec.getMGFParameters()) ++ .getDigestAlgorithm(); ++ int digestLen = KAEUtils.getDigestLength(mdName); ++ int bufferLen = outputSize - 2 - 2 * digestLen; ++ if (bufferLen < 0) { ++ throw new InvalidKeyException ++ ("Key is too short for encryption using OAEPPadding" + ++ " with " + mdName + " and MGF1" + mgfMdName); ++ } ++ return bufferLen; ++ } ++ ++ // non-CRT private key, use the jdk soft calculation. ++ private boolean useJdkSoftCalculation() { ++ return (rsaKey instanceof RSAPrivateKey) && !(rsaKey instanceof RSAPrivateCrtKey); ++ } ++ ++ // get the rsa padding ++ private RSAPadding getRSAPadding(KAERSAPaddingType paddingType, int paddedSize, ++ SecureRandom random, AlgorithmParameterSpec spec) ++ throws InvalidKeyException, InvalidAlgorithmParameterException { ++ RSAPadding rsaPadding; ++ if (KAERSAPaddingType.NoPadding.equals(paddingType)) { ++ rsaPadding = RSAPadding.getInstance(RSAPadding.PAD_NONE, paddedSize, random); ++ } else if (KAERSAPaddingType.PKCS1Padding.equals(paddingType)) { ++ int blockType = (mode <= MODE_DECRYPT) ? RSAPadding.PAD_BLOCKTYPE_2 ++ : RSAPadding.PAD_BLOCKTYPE_1; ++ rsaPadding = RSAPadding.getInstance(blockType, paddedSize, random); ++ } else { ++ rsaPadding = RSAPadding.getInstance(RSAPadding.PAD_OAEP_MGF1, paddedSize, ++ random, (OAEPParameterSpec) spec); ++ } ++ return rsaPadding; ++ } ++ ++ private boolean isEncrypt(int opmode) throws InvalidKeyException { ++ boolean encrypt; ++ switch (opmode) { ++ case Cipher.ENCRYPT_MODE: ++ case Cipher.WRAP_MODE: ++ encrypt = true; ++ break; ++ case Cipher.DECRYPT_MODE: ++ case Cipher.UNWRAP_MODE: ++ encrypt = false; ++ break; ++ default: ++ throw new InvalidKeyException("Unknown mode: " + opmode); ++ } ++ return encrypt; ++ } ++ ++ // initialize this cipher ++ private void init(int opmode, Key key, SecureRandom random, AlgorithmParameterSpec params) ++ throws InvalidKeyException, InvalidAlgorithmParameterException { ++ // check the key, and convert to RSAKey ++ rsaKey = RSAKeyFactory.toRSAKey(key); ++ ++ // init mode ++ boolean encrypt = isEncrypt(opmode); ++ if (key instanceof RSAPublicKey) { ++ mode = encrypt ? MODE_ENCRYPT : MODE_VERIFY; ++ } else { ++ mode = encrypt ? MODE_SIGN : MODE_DECRYPT; ++ } ++ ++ int bufferLen = RSACore.getByteLength(rsaKey.getModulus()); ++ outputSize = bufferLen; ++ bufOfs = 0; ++ if (KAERSAPaddingType.PKCS1Padding.equals(paddingType)) { ++ if (params != null) { ++ checkTlsRsaPremasterSecretParameterSpec(params); ++ spec = params; ++ this.random = random; // for TLS RSA premaster secret ++ } ++ if (encrypt) { ++ bufferLen -= 11; ++ } ++ } else if (KAERSAPaddingType.OAEP.equals(paddingType)) { ++ if ((mode == MODE_SIGN) || (mode == MODE_VERIFY)) { ++ throw new InvalidKeyException ++ ("OAEP cannot be used to sign or verify signatures"); ++ } ++ if (params != null) { ++ checkOAEPParameterSpec(params); ++ spec = params; ++ } else { ++ spec = new OAEPParameterSpec(oaepHashAlgorithm, "MGF1", ++ MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT); ++ } ++ bufferLen = getOAEPBufferLen(bufferLen, (OAEPParameterSpec) spec, encrypt); ++ } ++ buffer = new byte[bufferLen]; ++ ++ if (useJdkSoftCalculation()) { ++ this.padding = getRSAPadding(paddingType, outputSize, random, spec); ++ } ++ } ++ ++ // internal update method ++ private void update(byte[] in, int inOfs, int inLen) { ++ if ((inLen == 0) || (in == null)) { ++ return; ++ } ++ if (inLen > (buffer.length - bufOfs)) { ++ bufOfs = buffer.length + 1; ++ return; ++ } ++ System.arraycopy(in, inOfs, buffer, bufOfs, inLen); ++ bufOfs += inLen; ++ } ++ ++ // encrypt or decrypt for NoPadding or PKCS1Padding ++ private int doCryptNotOAEPPadding(long keyAddress, byte[] input, byte[] output) throws BadPaddingException { ++ int resultSize; ++ switch (mode) { ++ case MODE_SIGN: ++ resultSize = nativeRSAPrivateEncrypt(keyAddress, input.length, input, output, paddingType.getId()); ++ break; ++ case MODE_VERIFY: ++ resultSize = nativeRSAPublicDecrypt(keyAddress, input.length, input, output, paddingType.getId()); ++ break; ++ case MODE_ENCRYPT: ++ resultSize = nativeRSAPublicEncrypt(keyAddress, input.length, input, output, paddingType.getId()); ++ break; ++ case MODE_DECRYPT: ++ resultSize = nativeRSAPrivateDecrypt(keyAddress, input.length, input, output, paddingType.getId()); ++ break; ++ default: ++ throw new AssertionError("Internal error"); ++ } ++ return resultSize; ++ } ++ ++ ++ // encrypt or decrypt for OAEPPadding ++ private int doCryptOAEPPadding(long keyAddress, byte[] input, byte[] output, OAEPParameterSpec oaepParameterSpec) ++ throws BadPaddingException { ++ // oaep digest algorithm ++ String oaepMdAlgorithm = KAEUtils.getKAEDigestName(oaepParameterSpec.getDigestAlgorithm()); ++ // mgf1 digest algorithm ++ MGF1ParameterSpec mgf1ParameterSpec = (MGF1ParameterSpec) oaepParameterSpec.getMGFParameters(); ++ String mgf1MdAlgorithm = KAEUtils.getKAEDigestName(mgf1ParameterSpec.getDigestAlgorithm()); ++ // label ++ PSource pSource = oaepParameterSpec.getPSource(); ++ byte[] label = ((PSource.PSpecified) pSource).getValue(); ++ int resultSize; ++ switch (mode) { ++ case MODE_ENCRYPT: ++ resultSize = nativeRSAEncryptOAEPPadding(keyAddress, input.length, input, output, paddingType.getId(), ++ oaepMdAlgorithm, mgf1MdAlgorithm, label); ++ break; ++ case MODE_DECRYPT: ++ resultSize = nativeRSADecryptOAEPPadding(keyAddress, input.length, input, output, paddingType.getId(), ++ oaepMdAlgorithm, mgf1MdAlgorithm, label); ++ break; ++ default: ++ throw new AssertionError("Internal error"); ++ } ++ return resultSize; ++ } ++ ++ // get input bytes ++ private byte[] getInputBytes(byte[] buffer, int bufOfs, KAERSAPaddingType paddingType) { ++ if (bufOfs == buffer.length) { ++ return buffer; ++ } ++ ++ // if padding type is NoPadding , data should move to end ++ final byte[] input; ++ if (KAERSAPaddingType.NoPadding.equals(paddingType)) { ++ input = new byte[buffer.length]; ++ System.arraycopy(buffer, 0, input, buffer.length - bufOfs, bufOfs); ++ } else { ++ input = Arrays.copyOf(buffer, bufOfs); ++ } ++ return input; ++ } ++ ++ // internal doFinal() method. Here we perform the actual RSA operation ++ private byte[] doFinal() throws BadPaddingException, IllegalBlockSizeException { ++ if (bufOfs > buffer.length) { ++ throw new IllegalBlockSizeException("Data must not be longer " ++ + "than " + buffer.length + " bytes"); ++ } ++ ++ if (useJdkSoftCalculation()) { ++ return doFinalForJdkSoftCalculation(padding); ++ } ++ ++ // get input bytes ++ final byte[] input = getInputBytes(buffer, bufOfs, paddingType); ++ ++ try { ++ rsaKeyHolder = new KAERSAKeyHolder(this, rsaKey); ++ } catch (InvalidKeyException e) { ++ throw new RuntimeException(e.getMessage()); ++ } ++ ++ long keyAddress = rsaKeyHolder.keyAddress; ++ byte[] output = new byte[outputSize]; ++ int cipherTextLength; ++ try { ++ if (KAERSAPaddingType.OAEP.equals(paddingType)) { ++ // do crypt for OAEPPadding ++ cipherTextLength = doCryptOAEPPadding(keyAddress, input, output, (OAEPParameterSpec) spec); ++ } else { ++ // do crypt for NoPadding or PKCS1Padding ++ cipherTextLength = doCryptNotOAEPPadding(keyAddress, input, output); ++ } ++ ++ // If mode is signing or verifying , and the length of the ciphertext is less than output length, ++ // just keep output length ciphertext. ++ if ((mode == MODE_VERIFY || mode == MODE_DECRYPT) && cipherTextLength != output.length) { ++ output = Arrays.copyOf(output, cipherTextLength); ++ } ++ } finally { ++ bufOfs = 0; ++ resetKeyHolder(); ++ } ++ return output; ++ } ++ ++ private byte[] doFinalForJdkSoftCalculation(RSAPadding padding) throws BadPaddingException { ++ try { ++ byte[] data; ++ switch (mode) { ++ case MODE_SIGN: ++ data = padding.pad(buffer, 0, bufOfs); ++ return RSACore.rsa(data, (RSAPrivateKey) rsaKey, true); ++ case MODE_DECRYPT: ++ byte[] decryptBuffer = RSACore.convert(buffer, 0, bufOfs); ++ data = RSACore.rsa(decryptBuffer, (RSAPrivateKey) rsaKey, false); ++ return padding.unpad(data); ++ default: ++ throw new AssertionError("Internal error"); ++ } ++ } finally { ++ bufOfs = 0; ++ } ++ } ++ ++ // see JCE spec ++ @Override ++ protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) { ++ update(in, inOfs, inLen); ++ return B0; ++ } ++ ++ // see JCE spec ++ @Override ++ protected int engineUpdate(byte[] in, int inOfs, int inLen, byte[] out, ++ int outOfs) { ++ update(in, inOfs, inLen); ++ return 0; ++ } ++ ++ // see JCE spec ++ @Override ++ protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen) ++ throws BadPaddingException, IllegalBlockSizeException { ++ update(in, inOfs, inLen); ++ return doFinal(); ++ } ++ ++ // see JCE spec ++ @Override ++ protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out, int outOfs) ++ throws ShortBufferException, BadPaddingException, IllegalBlockSizeException { ++ if (outputSize > out.length - outOfs) { ++ throw new ShortBufferException ++ ("Need " + outputSize + " bytes for output"); ++ } ++ update(in, inOfs, inLen); ++ byte[] result = doFinal(); ++ int length = result.length; ++ System.arraycopy(result, 0, out, outOfs, length); ++ return length; ++ } ++ ++ // see JCE spec ++ @Override ++ protected byte[] engineWrap(Key key) throws InvalidKeyException, ++ IllegalBlockSizeException { ++ byte[] encoded = key.getEncoded(); ++ if ((encoded == null) || (encoded.length == 0)) { ++ throw new InvalidKeyException("Could not obtain encoded key"); ++ } ++ if (encoded.length > buffer.length) { ++ throw new InvalidKeyException("Key is too long for wrapping"); ++ } ++ update(encoded, 0, encoded.length); ++ try { ++ return doFinal(); ++ } catch (BadPaddingException e) { ++ // should not occur ++ throw new InvalidKeyException("Wrapping failed", e); ++ } ++ } ++ ++ // see JCE spec ++ @Override ++ @SuppressWarnings("deprecation") ++ protected Key engineUnwrap(byte[] wrappedKey, String algorithm, int type) ++ throws InvalidKeyException, NoSuchAlgorithmException { ++ if (wrappedKey.length > buffer.length) { ++ throw new InvalidKeyException("Key is too long for unwrapping"); ++ } ++ ++ boolean isTlsRsaPremasterSecret = "TlsRsaPremasterSecret".equals(algorithm); ++ Exception failover = null; ++ byte[] encoded = null; ++ ++ update(wrappedKey, 0, wrappedKey.length); ++ try { ++ encoded = doFinal(); ++ } catch (BadPaddingException e) { ++ if (isTlsRsaPremasterSecret) { ++ failover = e; ++ } else { ++ throw new InvalidKeyException("Unwrapping failed", e); ++ } ++ } catch (IllegalBlockSizeException e) { ++ // should not occur, handled with length check above ++ throw new InvalidKeyException("Unwrapping failed", e); ++ } ++ ++ if (isTlsRsaPremasterSecret) { ++ if (!(spec instanceof TlsRsaPremasterSecretParameterSpec)) { ++ throw new IllegalStateException( ++ "No TlsRsaPremasterSecretParameterSpec specified"); ++ } ++ ++ // polish the TLS premaster secret ++ encoded = KeyUtil.checkTlsPreMasterSecretKey( ++ ((TlsRsaPremasterSecretParameterSpec) spec).getClientVersion(), ++ ((TlsRsaPremasterSecretParameterSpec) spec).getServerVersion(), ++ random, encoded, (failover != null)); ++ } ++ return KAEUtils.ConstructKeys.constructKey(encoded, algorithm, type); ++ } ++ ++ // see JCE spec ++ @Override ++ protected int engineGetKeySize(Key key) throws InvalidKeyException { ++ RSAKey newRSAKey = RSAKeyFactory.toRSAKey(key); ++ return newRSAKey.getModulus().bitLength(); ++ } ++ ++ // reset the key holder ++ private void resetKeyHolder() { ++ if (rsaKeyHolder != null) { ++ rsaKeyHolder.dispose(true); ++ rsaKeyHolder = null; ++ } ++ } ++ ++ // create KAE rsa key ++ protected static native long nativeCreateRSAPrivateCrtKey(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q, ++ byte[] dmp1, byte[] dmq1, byte[] iqmp); ++ ++ // create KAE rsa public key ++ protected static native long nativeCreateRSAPublicKey(byte[] n, byte[] e); ++ ++ // encrypt by private key for padding type (NOPADDING|PKCS1PADDING) ++ protected static native int nativeRSAPrivateEncrypt(long keyAddress, int inLen, byte[] in, byte[] out, ++ int paddingType) throws BadPaddingException; ++ ++ // decrypt by private key for padding type (NOPADDING|PKCS1PADDING) ++ protected static native int nativeRSAPrivateDecrypt(long keyAddress, int inLen, byte[] in, byte[] out, ++ int paddingType) throws BadPaddingException; ++ ++ // encrypt by public key for padding type (NOPADDING|PKCS1PADDING) ++ protected static native int nativeRSAPublicEncrypt(long keyAddress, int inLen, byte[] in, byte[] out, ++ int paddingType) throws BadPaddingException; ++ ++ // decrypt by public key for padding type (NOPADDING|PKCS1PADDING) ++ protected static native int nativeRSAPublicDecrypt(long keyAddress, int inLen, byte[] in, byte[] out, ++ int paddingType) throws BadPaddingException; ++ ++ // encrypt by public for padding type (OAEPPADDING) ++ protected static native int nativeRSAEncryptOAEPPadding(long keyAddress, int inLen, byte[] in, byte[] out, ++ int paddingType, String oaepMdAlgo, String mgf1MdAlgo, ++ byte[] label) throws BadPaddingException; ++ ++ // decrypt by public for padding type (OAEPPADDING) ++ protected static native int nativeRSADecryptOAEPPadding(long keyAddress, int inLen, byte[] in, byte[] out, ++ int paddingType, String oaepMdAlgo, String mgf1MdAlgo, ++ byte[] label) throws BadPaddingException; ++ ++ // free the key ++ protected static native void nativeFreeKey(long keyAddress); ++ ++ /** ++ * The rsa openssl key holder , use PhantomReference in case of native memory leaks ++ */ ++ private static class KAERSAKeyHolder extends PhantomReference ++ implements Comparable { ++ private static ReferenceQueue referenceQueue = new ReferenceQueue<>(); ++ private static Set referenceList = new ConcurrentSkipListSet<>(); ++ private final long keyAddress; ++ ++ KAERSAKeyHolder(KAERSACipher rsaCipher, RSAKey rsaKey) throws InvalidKeyException { ++ super(rsaCipher, referenceQueue); ++ this.keyAddress = getKeyAddress(rsaKey); ++ referenceList.add(this); ++ drainRefQueueBounded(); ++ } ++ ++ private static void drainRefQueueBounded() { ++ while (true) { ++ KAERSAKeyHolder next = (KAERSAKeyHolder) referenceQueue.poll(); ++ if (next == null) { ++ break; ++ } ++ next.dispose(true); ++ } ++ } ++ ++ void dispose(boolean needFree) { ++ referenceList.remove(this); ++ try { ++ if (needFree) { ++ nativeFreeKey(keyAddress); ++ } ++ } finally { ++ this.clear(); ++ } ++ } ++ ++ @Override ++ public int compareTo(KAERSAKeyHolder other) { ++ if (this.keyAddress == other.keyAddress) { ++ return 0; ++ } else { ++ return (this.keyAddress < other.keyAddress) ? -1 : 1; ++ } ++ } ++ ++ private long getKeyAddress(RSAKey rsaKey) throws InvalidKeyException { ++ long address; ++ if (rsaKey instanceof RSAPrivateCrtKey) { // RSAPrivateCrtKeyImpl ++ address = getKeyAddress((RSAPrivateCrtKey) rsaKey); ++ } else if (rsaKey instanceof RSAPublicKey) { // RSAPublicKeyImpl ++ address = getKeyAddress((RSAPublicKey) rsaKey); ++ } else { ++ throw new InvalidKeyException("Invalid RSAKey implement " + rsaKey.getClass()); ++ } ++ return address; ++ } ++ ++ private long getKeyAddress(RSAPrivateCrtKey key) throws InvalidKeyException { ++ checkKey(key); ++ try { ++ return nativeCreateRSAPrivateCrtKey( ++ key.getModulus().toByteArray(), ++ key.getPublicExponent().toByteArray(), ++ key.getPrivateExponent().toByteArray(), ++ key.getPrimeP().toByteArray(), ++ key.getPrimeQ().toByteArray(), ++ key.getPrimeExponentP().toByteArray(), ++ key.getPrimeExponentQ().toByteArray(), ++ key.getCrtCoefficient().toByteArray()); ++ } catch (Exception e) { ++ throw new InvalidKeyException(e); ++ } ++ } ++ ++ private long getKeyAddress(RSAPublicKey key) throws InvalidKeyException { ++ checkKey(key); ++ try { ++ return nativeCreateRSAPublicKey( ++ key.getModulus().toByteArray(), ++ key.getPublicExponent().toByteArray() ++ ); ++ } catch (Exception e) { ++ throw new InvalidKeyException(e); ++ } ++ } ++ ++ private void checkKey(RSAPrivateCrtKey key) throws InvalidKeyException { ++ boolean isInValidKey = key.getModulus() == null ++ || key.getPublicExponent() == null ++ || key.getPrivateExponent() == null ++ || key.getPrimeP() == null ++ || key.getPrimeQ() == null ++ || key.getPrimeExponentP() == null ++ || key.getPrimeExponentQ() == null ++ || key.getCrtCoefficient() == null; ++ if (isInValidKey) { ++ throw new InvalidKeyException("Invalid RSA private key"); ++ } ++ } ++ ++ private void checkKey(RSAPublicKey key) throws InvalidKeyException { ++ if (key.getModulus() == null || key.getPublicExponent() == null) { ++ throw new InvalidKeyException("Invalid RSA public key"); ++ } ++ } ++ } ++} +diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAKeyPairGenerator.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAKeyPairGenerator.java +new file mode 100644 +index 000000000..88bd30574 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAKeyPairGenerator.java +@@ -0,0 +1,164 @@ ++/* ++ * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2023, 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. 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.openeuler.security.openssl; ++ ++import sun.security.rsa.*; ++import sun.security.rsa.RSAUtil.KeyType; ++import sun.security.util.SecurityProviderConstants; ++ ++import java.math.BigInteger; ++import java.security.*; ++import java.security.spec.AlgorithmParameterSpec; ++import java.security.spec.RSAKeyGenParameterSpec; ++ ++public abstract class KAERSAKeyPairGenerator extends KeyPairGeneratorSpi { ++ // public exponent to use ++ private BigInteger publicExponent; ++ ++ // size of the key to generate, >= KAERSAKeyFactory.MIN_MODLEN ++ private int keySize; ++ ++ private final KeyType type; ++ ++ private AlgorithmParameterSpec keyParams; ++ ++ ++ KAERSAKeyPairGenerator(KeyType keyType, int keySize) { ++ this.type = keyType; ++ initialize(keySize, null); ++ } ++ ++ // initialize the generator. See JCA doc ++ @Override ++ public void initialize(int keySize, SecureRandom random) { ++ try { ++ initialize(new RSAKeyGenParameterSpec(keySize, ++ RSAKeyGenParameterSpec.F4), null); ++ } catch (InvalidAlgorithmParameterException iape) { ++ throw new InvalidParameterException(iape.getMessage()); ++ } ++ } ++ ++ // second initialize method. See JCA doc ++ @Override ++ public void initialize(AlgorithmParameterSpec params, SecureRandom random) ++ throws InvalidAlgorithmParameterException { ++ if (!(params instanceof RSAKeyGenParameterSpec)) { ++ throw new InvalidAlgorithmParameterException ++ ("Params must be instance of RSAKeyGenParameterSpec"); ++ } ++ ++ RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) params; ++ int tmpKeySize = rsaSpec.getKeysize(); ++ BigInteger tmpPublicExponent = rsaSpec.getPublicExponent(); ++ keyParams = rsaSpec.getKeyParams(); ++ ++ if (tmpPublicExponent == null) { ++ tmpPublicExponent = RSAKeyGenParameterSpec.F4; ++ } else { ++ if (tmpPublicExponent.compareTo(RSAKeyGenParameterSpec.F0) < 0) { ++ throw new InvalidAlgorithmParameterException ++ ("Public exponent must be 3 or larger"); ++ } ++ if (tmpPublicExponent.bitLength() > tmpKeySize) { ++ throw new InvalidAlgorithmParameterException ++ ("Public exponent must be smaller than key size"); ++ } ++ } ++ ++ // do not allow unreasonably large key sizes, probably user error ++ try { ++ RSAKeyFactory.checkKeyLengths(tmpKeySize, tmpPublicExponent, ++ 512, 64 * 1024); ++ } catch (InvalidKeyException e) { ++ throw new InvalidAlgorithmParameterException( ++ "Invalid key sizes", e); ++ } ++ ++ this.keySize = tmpKeySize; ++ this.publicExponent = tmpPublicExponent; ++ } ++ ++ // generate the keypair. See JCA doc ++ @Override ++ public KeyPair generateKeyPair() { ++ // get the KAE RSA key Parameters ++ byte[][] params = nativeGenerateKeyPair(keySize, publicExponent.toByteArray()); ++ ++ try { ++ // check KAE RSA key Parameters ++ checkKAERSAParams(params); ++ ++ BigInteger n = new BigInteger(params[0]); ++ BigInteger e = new BigInteger(params[1]); ++ BigInteger d = new BigInteger(params[2]); ++ BigInteger p = new BigInteger(params[3]); ++ BigInteger q = new BigInteger(params[4]); ++ BigInteger pe = new BigInteger(params[5]); ++ BigInteger qe = new BigInteger(params[6]); ++ BigInteger coeff = new BigInteger(params[7]); ++ ++ // public key ++ PublicKey publicKey = RSAPublicKeyImpl.newKey(type, keyParams, n, e); ++ ++ // private key ++ PrivateKey privateKey = RSAPrivateCrtKeyImpl.newKey(type, keyParams, n, e, d, p, q, pe, qe, coeff); ++ ++ return new KeyPair(publicKey, privateKey); ++ } catch (InvalidKeyException ex) { ++ throw new RuntimeException(ex); ++ } ++ } ++ ++ // check KAE RSA key Parameters ++ private void checkKAERSAParams(byte[][] params) throws InvalidKeyException { ++ if (params == null || params.length < 8) { ++ throw new InvalidKeyException("Invalid KAE RSA key Parameter"); ++ } ++ ++ for (int i = 0; i < params.length; i++) { ++ if (params[i] == null) { ++ throw new InvalidKeyException("Invalid KAE RSA key Parameter , params[" + i + "] = null"); ++ } ++ } ++ } ++ ++ public static final class Legacy extends KAERSAKeyPairGenerator { ++ public Legacy() { ++ super(KeyType.RSA, SecurityProviderConstants.DEF_RSA_KEY_SIZE); ++ } ++ } ++ ++ public static final class PSS extends KAERSAKeyPairGenerator { ++ public PSS() { ++ super(KeyType.PSS, SecurityProviderConstants.DEF_RSASSA_PSS_KEY_SIZE); ++ } ++ } ++ ++ // generate key pair ++ static native byte[][] nativeGenerateKeyPair(int keySize, byte[] publicExponent) throws RuntimeException; ++} +diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAPSSSignature.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAPSSSignature.java +new file mode 100644 +index 000000000..065317148 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAPSSSignature.java +@@ -0,0 +1,683 @@ ++/* ++ * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2023, 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. 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.openeuler.security.openssl; ++ ++import java.io.IOException; ++import java.nio.ByteBuffer; ++ ++import java.security.*; ++import java.security.spec.AlgorithmParameterSpec; ++import java.security.spec.PSSParameterSpec; ++import java.security.spec.MGF1ParameterSpec; ++import java.security.interfaces.*; ++ ++import java.util.Arrays; ++import java.util.HashSet; ++import java.util.Locale; ++import java.util.Set; ++ ++import sun.security.rsa.PSSParameters; ++import sun.security.rsa.RSACore; ++import sun.security.jca.JCAUtil; ++ ++import javax.crypto.BadPaddingException; ++ ++/** ++ * PKCS#1 v2.2 RSASSA-PSS signatures with various message digest algorithms. ++ * RSASSA-PSS implementation takes the message digest algorithm, MGF algorithm, ++ * and salt length values through the required signature PSS parameters. ++ * We support SHA-1, SHA-224, SHA-256, SHA-384, SHA-512. ++ * The Openssl does not support rsa signatures with SHA-512/224 and SHA-512/256 as the digest algorithm, ++ * so we have not implemented them. ++ * The Openssl does not support non-CRT private key , when signing with a non-CRT private key, we use the sun sign. ++ */ ++@SuppressWarnings({"deprecation", "removal"}) ++public class KAERSAPSSSignature extends SignatureSpi { ++ // openssl unsupport rsa sign with digest algorithm ++ private static final Set UNSUPPORTED_DIGEST_ALGORITHM = new HashSet<>( ++ Arrays.asList("SHA-512/224", "SHA-512/256")); ++ ++ private static final byte[] EIGHT_BYTES_OF_ZEROS = new byte[8]; ++ ++ // message digest implementation we use for hashing the data ++ private MessageDigest md; ++ ++ // flag indicating whether the digest is reset ++ private boolean digestReset = true; ++ ++ // private key, if initialized for signing ++ private RSAPrivateKey privKey = null; ++ ++ // public key, if initialized for verifying ++ private RSAPublicKey pubKey = null; ++ ++ // PSS parameters from signatures and keys respectively ++ private PSSParameterSpec sigParams = null; ++ ++ // PRNG used to generate salt bytes if none given ++ private SecureRandom random; ++ ++ /** ++ * Construct a new RSAPSSSignatur with arbitrary digest algorithm ++ */ ++ public KAERSAPSSSignature() { ++ this.md = null; ++ } ++ ++ // initialize for verification. See JCA doc ++ @Override ++ protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { ++ if (!(publicKey instanceof RSAPublicKey)) { ++ throw new InvalidKeyException("key must be RSAPublicKey"); ++ } ++ this.pubKey = (RSAPublicKey) isValid((RSAKey) publicKey); ++ this.privKey = null; ++ resetDigest(); ++ } ++ ++ // initialize for signing. See JCA doc ++ @Override ++ protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { ++ engineInitSign(privateKey, null); ++ } ++ ++ // initialize for signing. See JCA doc ++ @Override ++ protected void engineInitSign(PrivateKey privateKey, SecureRandom random) throws InvalidKeyException { ++ if (!(privateKey instanceof RSAPrivateKey)) { ++ throw new InvalidKeyException("key must be RSAPrivateKey"); ++ } ++ this.privKey = (RSAPrivateKey) isValid((RSAKey) privateKey); ++ this.pubKey = null; ++ this.random = ++ (random == null ? JCAUtil.getSecureRandom() : random); ++ resetDigest(); ++ } ++ ++ /** ++ * Utility method for checking the key PSS parameters against signature ++ * PSS parameters. ++ * Returns false if any of the digest/MGF algorithms and trailerField ++ * values does not match or if the salt length in key parameters is ++ * larger than the value in signature parameters. ++ */ ++ private static boolean isCompatible(AlgorithmParameterSpec keyParams, PSSParameterSpec sigParams) { ++ if (keyParams == null) { ++ // key with null PSS parameters means no restriction ++ return true; ++ } ++ if (!(keyParams instanceof PSSParameterSpec)) { ++ return false; ++ } ++ // nothing to compare yet, defer the check to when sigParams is set ++ if (sigParams == null) { ++ return true; ++ } ++ PSSParameterSpec pssKeyParams = (PSSParameterSpec) keyParams; ++ // first check the salt length requirement ++ if (pssKeyParams.getSaltLength() > sigParams.getSaltLength()) { ++ return false; ++ } ++ ++ // compare equality of the rest of fields based on DER encoding ++ PSSParameterSpec keyParams2 = ++ new PSSParameterSpec(pssKeyParams.getDigestAlgorithm(), ++ pssKeyParams.getMGFAlgorithm(), ++ pssKeyParams.getMGFParameters(), ++ sigParams.getSaltLength(), ++ pssKeyParams.getTrailerField()); ++ ++ // skip the JCA overhead ++ try { ++ byte[] encoded = PSSParameters.getEncoded(keyParams2); ++ byte[] encoded2 = PSSParameters.getEncoded(sigParams); ++ return Arrays.equals(encoded, encoded2); ++ } catch (IOException e) { ++ return false; ++ } ++ } ++ ++ /** ++ * Validate the specified RSAKey and its associated parameters against ++ * internal signature parameters. ++ */ ++ private RSAKey isValid(RSAKey rsaKey) throws InvalidKeyException { ++ try { ++ AlgorithmParameterSpec keyParams = rsaKey.getParams(); ++ // validate key parameters ++ if (!isCompatible(rsaKey.getParams(), this.sigParams)) { ++ throw new InvalidKeyException ++ ("Key contains incompatible PSS parameter values"); ++ } ++ // validate key length ++ if (this.sigParams != null) { ++ Integer hLen = ++ KAEUtils.getDigestLength(this.sigParams.getDigestAlgorithm()); ++ if (hLen == null) { ++ throw new ProviderException("Unsupported digest algo: " + ++ this.sigParams.getDigestAlgorithm()); ++ } ++ checkKeyLength(rsaKey, hLen, this.sigParams.getSaltLength()); ++ } ++ return rsaKey; ++ } catch (SignatureException e) { ++ throw new InvalidKeyException(e); ++ } ++ } ++ ++ /** ++ * Validate the specified Signature PSS parameters. ++ */ ++ private PSSParameterSpec validateSigParams(AlgorithmParameterSpec p) throws InvalidAlgorithmParameterException { ++ if (p == null) { ++ throw new InvalidAlgorithmParameterException ++ ("Parameters cannot be null"); ++ } ++ if (!(p instanceof PSSParameterSpec)) { ++ throw new InvalidAlgorithmParameterException ++ ("parameters must be type PSSParameterSpec"); ++ } ++ // no need to validate again if same as current signature parameters ++ PSSParameterSpec params = (PSSParameterSpec) p; ++ if (params == this.sigParams) { ++ return params; ++ } ++ ++ RSAKey key = (this.privKey == null ? this.pubKey : this.privKey); ++ // check against keyParams if set ++ if (key != null) { ++ if (!isCompatible(key.getParams(), params)) { ++ throw new InvalidAlgorithmParameterException ++ ("Signature parameters does not match key parameters"); ++ } ++ } ++ // now sanity check the parameter values ++ if (!(params.getMGFAlgorithm().equalsIgnoreCase("MGF1"))) { ++ throw new InvalidAlgorithmParameterException("Only supports MGF1"); ++ } ++ if (params.getTrailerField() != PSSParameterSpec.TRAILER_FIELD_BC) { ++ throw new InvalidAlgorithmParameterException("Only supports TrailerFieldBC(1)"); ++ } ++ String digestAlgo = params.getDigestAlgorithm(); ++ // check key length again ++ if (key != null) { ++ try { ++ int hLen = KAEUtils.getDigestLength(digestAlgo); ++ checkKeyLength(key, hLen, params.getSaltLength()); ++ } catch (SignatureException e) { ++ throw new InvalidAlgorithmParameterException(e); ++ } ++ } ++ return params; ++ } ++ ++ /** ++ * Ensure the object is initialized with key and parameters and ++ * reset digest ++ */ ++ private void ensureInit() throws SignatureException { ++ RSAKey key = (this.privKey == null ? this.pubKey : this.privKey); ++ if (key == null) { ++ throw new SignatureException("Missing key"); ++ } ++ if (this.sigParams == null) { ++ // Parameters are required for signature verification ++ throw new SignatureException ++ ("Parameters required for RSASSA-PSS signatures"); ++ } ++ } ++ ++ /** ++ * Utility method for checking key length against digest length and ++ * salt length ++ */ ++ private static void checkKeyLength(RSAKey key, int digestLen, int saltLen) throws SignatureException { ++ if (key != null) { ++ int keyLength = getKeyLengthInBits(key) >> 3; ++ int minLength = Math.addExact(Math.addExact(digestLen, saltLen), 2); ++ if (keyLength < minLength) { ++ throw new SignatureException ++ ("Key is too short, need min " + minLength); ++ } ++ } ++ } ++ ++ /** ++ * Reset the message digest if it is not already reset. ++ */ ++ private void resetDigest() { ++ if (digestReset == false) { ++ this.md.reset(); ++ digestReset = true; ++ } ++ } ++ ++ /** ++ * Return the message digest value. ++ */ ++ private byte[] getDigestValue() { ++ digestReset = true; ++ return this.md.digest(); ++ } ++ ++ // update the signature with the plaintext data. See JCA doc ++ @Override ++ protected void engineUpdate(byte b) throws SignatureException { ++ ensureInit(); ++ this.md.update(b); ++ digestReset = false; ++ } ++ ++ // update the signature with the plaintext data. See JCA doc ++ @Override ++ protected void engineUpdate(byte[] b, int off, int len) throws SignatureException { ++ ensureInit(); ++ this.md.update(b, off, len); ++ digestReset = false; ++ } ++ ++ // update the signature with the plaintext data. See JCA doc ++ @Override ++ protected void engineUpdate(ByteBuffer b) { ++ try { ++ ensureInit(); ++ } catch (SignatureException se) { ++ // hack for working around API bug ++ throw new RuntimeException(se.getMessage()); ++ } ++ this.md.update(b); ++ digestReset = false; ++ } ++ ++ // determine whether the digest is valid ++ private boolean isValidDigest() { ++ String digestName = this.md.getAlgorithm(); ++ if (UNSUPPORTED_DIGEST_ALGORITHM.contains(digestName.toUpperCase(Locale.ROOT))) { ++ return false; ++ } ++ AlgorithmParameterSpec mgfParams = this.sigParams.getMGFParameters(); ++ String mgfDigestName = ""; ++ if (mgfParams != null) { ++ mgfDigestName = ++ ((MGF1ParameterSpec) mgfParams).getDigestAlgorithm(); ++ } ++ return !UNSUPPORTED_DIGEST_ALGORITHM.contains(mgfDigestName.toUpperCase(Locale.ROOT)); ++ } ++ ++ // determine whether use kae sign ++ private boolean useKaeSign() { ++ return (privKey instanceof RSAPrivateCrtKey) && isValidDigest(); ++ } ++ ++ // sun sign ++ private byte[] sunSign(byte[] mHash) throws SignatureException { ++ try { ++ byte[] encoded = encodeSignature(mHash); ++ return RSACore.rsa(encoded, privKey, true); ++ } catch (GeneralSecurityException e) { ++ throw new SignatureException("Could not sign data", e); ++ } catch (IOException e) { ++ throw new SignatureException("Could not encode data", e); ++ } ++ } ++ ++ // kae sign ++ private byte[] kaeSign(byte[] mHash) throws SignatureException { ++ String kaeDigestName = KAEUtils.getKAEDigestName(this.sigParams.getDigestAlgorithm()); ++ String mgfDigestName = kaeDigestName; ++ AlgorithmParameterSpec mgfParams = this.sigParams.getMGFParameters(); ++ if (mgfParams != null) { ++ mgfDigestName = ++ ((MGF1ParameterSpec) mgfParams).getDigestAlgorithm(); ++ } ++ String mgf1DigestName = KAEUtils.getKAEDigestName(mgfDigestName); ++ ++ RSAPrivateCrtKey privateCrtKey = (RSAPrivateCrtKey) privKey; ++ long keyAddress = KAERSACipher.nativeCreateRSAPrivateCrtKey( ++ privateCrtKey.getModulus().toByteArray(), ++ privateCrtKey.getPublicExponent().toByteArray(), ++ privateCrtKey.getPrivateExponent().toByteArray(), ++ privateCrtKey.getPrimeP().toByteArray(), ++ privateCrtKey.getPrimeQ().toByteArray(), ++ privateCrtKey.getPrimeExponentP().toByteArray(), ++ privateCrtKey.getPrimeExponentQ().toByteArray(), ++ privateCrtKey.getCrtCoefficient().toByteArray()); ++ byte[] bytes; ++ try { ++ bytes = KAERSASignatureNative.pssSign(keyAddress, kaeDigestName, mHash, ++ KAERSAPaddingType.PKCS1PssPadding.getId(), ++ mgf1DigestName, this.sigParams.getSaltLength()); ++ } catch (SignatureException e) { ++ throw e; ++ } finally { ++ KAERSACipher.nativeFreeKey(keyAddress); ++ } ++ return bytes; ++ } ++ ++ // sign the data and return the signature. See JCA doc ++ @Override ++ protected byte[] engineSign() throws SignatureException { ++ ensureInit(); ++ byte[] mHash = getDigestValue(); ++ if (useKaeSign()) { ++ return kaeSign(mHash); ++ } ++ return sunSign(mHash); ++ } ++ ++ // determine whether use kae verify ++ private boolean useKaeVerify() { ++ return isValidDigest(); ++ } ++ ++ // sun verify ++ private boolean sunVerify(byte[] mHash, byte[] sigBytes) throws SignatureException { ++ try { ++ byte[] decrypted = RSACore.rsa(sigBytes, this.pubKey); ++ return decodeSignature(mHash, decrypted); ++ } catch (javax.crypto.BadPaddingException e) { ++ // occurs if the app has used the wrong RSA public key ++ // or if sigBytes is invalid ++ // return false rather than propagating the exception for ++ // compatibility/ease of use ++ return false; ++ } catch (IOException e) { ++ throw new SignatureException("Signature encoding error", e); ++ } finally { ++ resetDigest(); ++ } ++ } ++ ++ // kae verify ++ private boolean kaeVerify(byte[] mHash, byte[] sigBytes) throws SignatureException { ++ String kaeDigestName = KAEUtils.getKAEDigestName(this.sigParams.getDigestAlgorithm()); ++ String mgfDigestName = kaeDigestName; ++ AlgorithmParameterSpec mgfParams = this.sigParams.getMGFParameters(); ++ if (mgfParams != null) { ++ mgfDigestName = ++ ((MGF1ParameterSpec) mgfParams).getDigestAlgorithm(); ++ } ++ String mgf1KaeDigestName = KAEUtils.getKAEDigestName(mgfDigestName); ++ ++ long keyAddress = KAERSACipher.nativeCreateRSAPublicKey(this.pubKey.getModulus().toByteArray(), ++ this.pubKey.getPublicExponent().toByteArray()); ++ boolean verify; ++ try { ++ verify = KAERSASignatureNative.pssVerify(keyAddress, kaeDigestName, mHash, ++ KAERSAPaddingType.PKCS1PssPadding.getId(), mgf1KaeDigestName, ++ this.sigParams.getSaltLength(), sigBytes); ++ } catch (SignatureException e) { ++ throw e; ++ } catch (BadPaddingException e) { ++ // occurs if the app has used the wrong RSA public key ++ // or if sigBytes is invalid or sourceBytes is invalid ++ // return false rather than propagating the exception for ++ // compatibility/ease of use ++ return false; ++ } finally { ++ resetDigest(); ++ KAERSACipher.nativeFreeKey(keyAddress); ++ } ++ return verify; ++ } ++ ++ // verify the data and return the result. See JCA doc ++ // should be reset to the state after engineInitVerify call. ++ @Override ++ protected boolean engineVerify(byte[] sigBytes) throws SignatureException { ++ ensureInit(); ++ if (sigBytes.length != RSACore.getByteLength(this.pubKey)) { ++ throw new SignatureException("Signature length not correct: got " + ++ sigBytes.length + " but was expecting " + ++ RSACore.getByteLength(this.pubKey)); ++ } ++ byte[] mHash = getDigestValue(); ++ if (useKaeVerify()) { ++ return kaeVerify(mHash, sigBytes); ++ } ++ return sunVerify(mHash, sigBytes); ++ } ++ ++ // return the modulus length in bits ++ private static int getKeyLengthInBits(RSAKey k) { ++ if (k != null) { ++ return k.getModulus().bitLength(); ++ } ++ return -1; ++ } ++ ++ /** ++ * Encode the digest 'mHash', return the to-be-signed data. ++ * Also used by the PKCS#11 provider. ++ */ ++ private byte[] encodeSignature(byte[] mHash) throws IOException, DigestException { ++ AlgorithmParameterSpec mgfParams = this.sigParams.getMGFParameters(); ++ String mgfDigestAlgo; ++ if (mgfParams != null) { ++ mgfDigestAlgo = ++ ((MGF1ParameterSpec) mgfParams).getDigestAlgorithm(); ++ } else { ++ mgfDigestAlgo = this.md.getAlgorithm(); ++ } ++ try { ++ int emBits = getKeyLengthInBits(this.privKey) - 1; ++ int emLen = (emBits + 7) >> 3; ++ int hLen = this.md.getDigestLength(); ++ int dbLen = emLen - hLen - 1; ++ int sLen = this.sigParams.getSaltLength(); ++ ++ // maps DB into the corresponding region of EM and ++ // stores its bytes directly into EM ++ byte[] em = new byte[emLen]; ++ ++ // step7 and some of step8 ++ em[dbLen - sLen - 1] = (byte) 1; // set DB's padding2 into EM ++ em[em.length - 1] = (byte) 0xBC; // set trailer field of EM ++ ++ if (!digestReset) { ++ throw new ProviderException("Digest should be reset"); ++ } ++ // step5: generates M' using padding1, mHash, and salt ++ this.md.update(EIGHT_BYTES_OF_ZEROS); ++ digestReset = false; // mark digest as it now has data ++ this.md.update(mHash); ++ if (sLen != 0) { ++ // step4: generate random salt ++ byte[] salt = new byte[sLen]; ++ this.random.nextBytes(salt); ++ this.md.update(salt); ++ ++ // step8: set DB's salt into EM ++ System.arraycopy(salt, 0, em, dbLen - sLen, sLen); ++ } ++ // step6: generate H using M' ++ this.md.digest(em, dbLen, hLen); // set H field of EM ++ digestReset = true; ++ ++ // step7 and 8 are already covered by the code which setting up ++ // EM as above ++ ++ // step9 and 10: feed H into MGF and xor with DB in EM ++ KAEMGF1 mgf1 = new KAEMGF1(mgfDigestAlgo); ++ mgf1.generateAndXor(em, dbLen, hLen, dbLen, em, 0); ++ // step11: set the leftmost (8emLen - emBits) bits of the leftmost ++ // octet to 0 ++ int numZeroBits = (emLen << 3) - emBits; ++ if (numZeroBits != 0) { ++ byte mask = (byte) (0xff >>> numZeroBits); ++ em[0] = (byte) (em[0] & mask); ++ } ++ ++ // step12: em should now holds maskedDB || hash h || 0xBC ++ return em; ++ } catch (NoSuchAlgorithmException | RuntimeException e) { ++ e.printStackTrace(); ++ throw new IOException(e.toString()); ++ } ++ } ++ ++ private String getMgfDigestAlgo() { ++ String mgfDigestAlgo; ++ AlgorithmParameterSpec mgfParams = this.sigParams.getMGFParameters(); ++ if (mgfParams != null) { ++ mgfDigestAlgo = ++ ((MGF1ParameterSpec) mgfParams).getDigestAlgorithm(); ++ } else { ++ mgfDigestAlgo = this.md.getAlgorithm(); ++ } ++ return mgfDigestAlgo; ++ } ++ ++ private void generateAndXor(String mgfDigestAlgo, byte[] em, int dbLen, int hLen) throws IOException { ++ try { ++ KAEMGF1 mgf1 = new KAEMGF1(mgfDigestAlgo); ++ mgf1.generateAndXor(em, dbLen, hLen, dbLen, em, 0); ++ } catch (NoSuchAlgorithmException | RuntimeException e) { ++ e.printStackTrace(); ++ throw new IOException(e.toString()); ++ } ++ } ++ ++ /** ++ * Decode the signature data. Verify that the object identifier matches ++ * and return the message digest. ++ */ ++ private boolean decodeSignature(byte[] mHash, byte[] em) throws IOException { ++ int hLen = mHash.length; ++ int sLen = this.sigParams.getSaltLength(); ++ int emLen = em.length; ++ int emBits = getKeyLengthInBits(this.pubKey) - 1; ++ ++ // step3 ++ if (emLen < (hLen + sLen + 2)) { ++ return false; ++ } ++ ++ // step4 ++ if (em[emLen - 1] != (byte) 0xBC) { ++ return false; ++ } ++ ++ // step6: check if the leftmost (8emLen - emBits) bits of the leftmost ++ // octet are 0 ++ int numZeroBits = (emLen << 3) - emBits; ++ if (numZeroBits != 0) { ++ byte mask = (byte) (0xff << (8 - numZeroBits)); ++ if ((em[0] & mask) != 0) { ++ return false; ++ } ++ } ++ ++ // step 7 and 8 ++ int dbLen = emLen - hLen - 1; ++ ++ // generateAndXor ++ String mgfDigestAlgo = getMgfDigestAlgo(); ++ generateAndXor(mgfDigestAlgo, em, dbLen, hLen); ++ ++ // step9: set the leftmost (8emLen - emBits) bits of the leftmost ++ // octet to 0 ++ if (numZeroBits != 0) { ++ byte mask = (byte) (0xff >>> numZeroBits); ++ em[0] = (byte) (em[0] & mask); ++ } ++ ++ // step10 ++ int index = 0; ++ for (; index < dbLen - sLen - 1; index++) { ++ if (em[index] != 0) { ++ return false; ++ } ++ } ++ if (em[index] != 0x01) { ++ return false; ++ } ++ // step12 and 13 ++ this.md.update(EIGHT_BYTES_OF_ZEROS); ++ digestReset = false; ++ this.md.update(mHash); ++ if (sLen > 0) { ++ this.md.update(em, (dbLen - sLen), sLen); ++ } ++ byte[] digest2 = this.md.digest(); ++ digestReset = true; ++ ++ // step14 ++ byte[] digestInEM = Arrays.copyOfRange(em, dbLen, emLen - 1); ++ return MessageDigest.isEqual(digest2, digestInEM); ++ } ++ ++ // set parameter, not supported. See JCA doc ++ @Deprecated ++ @Override ++ protected void engineSetParameter(String param, Object value) throws InvalidParameterException { ++ throw new UnsupportedOperationException("setParameter() not supported"); ++ } ++ ++ @Override ++ protected void engineSetParameter(AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException { ++ this.sigParams = validateSigParams(params); ++ // disallow changing parameters when digest has been used ++ if (!digestReset) { ++ throw new ProviderException ++ ("Cannot set parameters during operations"); ++ } ++ String newHashAlg = this.sigParams.getDigestAlgorithm(); ++ // re-allocate md if not yet assigned or algorithm changed ++ if ((this.md == null) || ++ !(this.md.getAlgorithm().equalsIgnoreCase(newHashAlg))) { ++ try { ++ this.md = MessageDigest.getInstance(newHashAlg); ++ } catch (NoSuchAlgorithmException nsae) { ++ // should not happen as we pick default digest algorithm ++ throw new InvalidAlgorithmParameterException("Unsupported digest algorithm " + newHashAlg, nsae); ++ } ++ } ++ } ++ ++ // get parameter, not supported. See JCA doc ++ @Deprecated ++ @Override ++ protected Object engineGetParameter(String param) throws InvalidParameterException { ++ throw new UnsupportedOperationException("getParameter() not supported"); ++ } ++ ++ @Override ++ protected AlgorithmParameters engineGetParameters() { ++ AlgorithmParameters ap = null; ++ if (this.sigParams != null) { ++ try { ++ ap = AlgorithmParameters.getInstance("RSASSA-PSS"); ++ ap.init(this.sigParams); ++ } catch (GeneralSecurityException gse) { ++ throw new ProviderException(gse.getMessage()); ++ } ++ } ++ return ap; ++ } ++} ++ +diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAPaddingType.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAPaddingType.java +new file mode 100644 +index 000000000..35e7f2f67 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAPaddingType.java +@@ -0,0 +1,83 @@ ++/* ++ * Copyright (c) 2023, 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 java.util.Arrays; ++import java.util.Collections; ++import java.util.HashSet; ++import java.util.Set; ++ ++enum KAERSAPaddingType { ++ // raw RSA ++ PKCS1Padding(1, "PKCS1Padding"), ++ ++ // PKCS#1 v1.5 RSA ++ NoPadding(3, "NoPadding"), ++ ++ // PKCS#2 v2.2 OAEP with MGF1 ++ OAEP(4, "OAEP", new HashSet<>( ++ Arrays.asList( ++ "OAEPPADDING", ++ "OAEPWITHMD5ANDMGF1PADDING", ++ "OAEPWITHSHA1ANDMGF1PADDING", ++ "OAEPWITHMD5ANDMGF1PADDING", ++ "OAEPWITHSHA1ANDMGF1PADDING", ++ "OAEPWITHSHA-1ANDMGF1PADDING", ++ "OAEPWITHSHA-224ANDMGF1PADDING", ++ "OAEPWITHSHA-256ANDMGF1PADDING", ++ "OAEPWITHSHA-384ANDMGF1PADDING", ++ "OAEPWITHSHA-512ANDMGF1PADDING", ++ "OAEPWITHSHA-512/224ANDMGF1PADDING", ++ "OAEPWITHSHA-512/256ANDMGF1PADDING")) ++ ), ++ ++ // PSS ++ PKCS1PssPadding(6, "RSA_PKCS1_PSS_PADDING"); ++ ++ private final int id; ++ private final String name; ++ private final Set supportPaddings; ++ ++ public int getId() { ++ return id; ++ } ++ ++ public String getName() { ++ return name; ++ } ++ ++ KAERSAPaddingType(int id, String name) { ++ this(id, name, Collections.singleton(name)); ++ } ++ ++ KAERSAPaddingType(int id, String name, Set supportPaddings) { ++ this.id = id; ++ this.name = name; ++ this.supportPaddings = supportPaddings; ++ } ++ ++ public Set getSupportPaddings() { ++ return supportPaddings; ++ } ++} +diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSASignature.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSASignature.java +new file mode 100644 +index 000000000..b91a8cdaa +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSASignature.java +@@ -0,0 +1,365 @@ ++/* ++ * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2023, 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. 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.openeuler.security.openssl; ++ ++import java.io.IOException; ++import java.nio.ByteBuffer; ++ ++import java.security.*; ++import java.security.interfaces.*; ++import java.security.spec.AlgorithmParameterSpec; ++ ++import sun.security.rsa.RSACore; ++import sun.security.rsa.RSAKeyFactory; ++import sun.security.rsa.RSAPadding; ++import sun.security.rsa.RSAUtil; ++import sun.security.rsa.RSAUtil.KeyType; ++import sun.security.util.*; ++import sun.security.x509.AlgorithmId; ++ ++import javax.crypto.BadPaddingException; ++ ++/** ++ * We support support rsa signatures with MD2, MD5, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512 as the digest algorithm. ++ * The Openssl does not support rsa signatures with SHA-512/224 and SHA-512/256 as the digest algorithm, ++ * so we have not implemented them. ++ * The Openssl does not support non-CRT private key , when signing with a non-CRT private key, we use the sun sign. ++ */ ++public abstract class KAERSASignature extends SignatureSpi { ++ // we sign an ASN.1 SEQUENCE of AlgorithmId and digest ++ // it has the form 30:xx:30:xx:[digestOID]:05:00:04:xx:[digest] ++ // this means the encoded length is (8 + digestOID.length + digest.length) ++ private static final int BASE_LENGTH = 8; ++ ++ private String digestAlgorithm; ++ ++ // object identifier for the message digest algorithm used ++ private final ObjectIdentifier digestOID; ++ ++ // length of the encoded signature blob ++ private final int encodedLength; ++ ++ // message digest implementation we use ++ private final MessageDigest md; ++ ++ // flag indicating whether the digest is reset ++ private boolean digestReset; ++ ++ // private key, if initialized for signing ++ private RSAPrivateKey privateKey; ++ ++ // public key, if initialized for verifying ++ private RSAPublicKey publicKey; ++ ++ // padding to use, set when the initSign/initVerify is called ++ private RSAPadding padding; ++ ++ /** ++ * Construct a new RSASignature. Used by subclasses. ++ */ ++ KAERSASignature(String algorithm, ObjectIdentifier digestOID, int oidLength) { ++ this.digestAlgorithm = algorithm; ++ this.digestOID = digestOID; ++ try { ++ md = MessageDigest.getInstance(algorithm); ++ } catch (NoSuchAlgorithmException e) { ++ throw new ProviderException(e); ++ } ++ digestReset = true; ++ encodedLength = BASE_LENGTH + oidLength + md.getDigestLength(); ++ } ++ ++ // initialize for verification. See JCA doc ++ @Override ++ protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { ++ RSAPublicKey rsaKey = (RSAPublicKey) RSAKeyFactory.toRSAKey(publicKey); ++ this.privateKey = null; ++ this.publicKey = rsaKey; ++ initCommon(rsaKey, null); ++ } ++ ++ // initialize for signing. See JCA doc ++ @Override ++ protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { ++ engineInitSign(privateKey, null); ++ } ++ ++ // initialize for signing. See JCA doc ++ @Override ++ protected void engineInitSign(PrivateKey privateKey, SecureRandom random) throws InvalidKeyException { ++ RSAPrivateKey rsaKey = ++ (RSAPrivateKey) RSAKeyFactory.toRSAKey(privateKey); ++ this.privateKey = rsaKey; ++ this.publicKey = null; ++ initCommon(rsaKey, random); ++ } ++ ++ /** ++ * Init code common to sign and verify. ++ */ ++ private void initCommon(RSAKey rsaKey, SecureRandom random) throws InvalidKeyException { ++ try { ++ RSAUtil.checkParamsAgainstType(KeyType.RSA, rsaKey.getParams()); ++ } catch (ProviderException e) { ++ throw new InvalidKeyException("Invalid key for RSA signatures", e); ++ } ++ resetDigest(); ++ int keySize = RSACore.getByteLength(rsaKey); ++ try { ++ padding = RSAPadding.getInstance ++ (RSAPadding.PAD_BLOCKTYPE_1, keySize, random); ++ } catch (InvalidAlgorithmParameterException iape) { ++ throw new InvalidKeyException(iape.getMessage()); ++ } ++ int maxDataSize = padding.getMaxDataSize(); ++ if (encodedLength > maxDataSize) { ++ throw new InvalidKeyException ++ ("Key is too short for this signature algorithm"); ++ } ++ } ++ ++ /** ++ * Reset the message digest if it is not already reset. ++ */ ++ private void resetDigest() { ++ if (digestReset == false) { ++ md.reset(); ++ digestReset = true; ++ } ++ } ++ ++ /** ++ * Return the message digest value. ++ */ ++ private byte[] getDigestValue() { ++ digestReset = true; ++ return md.digest(); ++ } ++ ++ // update the signature with the plaintext data. See JCA doc ++ @Override ++ protected void engineUpdate(byte b) throws SignatureException { ++ md.update(b); ++ digestReset = false; ++ } ++ ++ // update the signature with the plaintext data. See JCA doc ++ @Override ++ protected void engineUpdate(byte[] b, int off, int len) throws SignatureException { ++ md.update(b, off, len); ++ digestReset = false; ++ } ++ ++ // update the signature with the plaintext data. See JCA doc ++ @Override ++ protected void engineUpdate(ByteBuffer b) { ++ md.update(b); ++ digestReset = false; ++ } ++ ++ // sign the data and return the signature. See JCA doc ++ @Override ++ protected byte[] engineSign() throws SignatureException { ++ if (privateKey == null) { ++ throw new SignatureException("Missing private key"); ++ } ++ ++ byte[] digest = getDigestValue(); ++ if (useKaeSign()) { ++ return kaeSign(digest); ++ } ++ return sunSign(digest); ++ } ++ ++ // determine if use kae sign , openssl do not support non-CRT private key ++ private boolean useKaeSign() { ++ return privateKey instanceof RSAPrivateCrtKey; ++ } ++ ++ // sun sign ++ private byte[] sunSign(byte[] digest) throws SignatureException { ++ try { ++ byte[] encoded = encodeSignature(digestOID, digest); ++ byte[] padded = padding.pad(encoded); ++ return RSACore.rsa(padded, privateKey, true); ++ } catch (GeneralSecurityException e) { ++ throw new SignatureException("Could not sign data", e); ++ } catch (IOException e) { ++ throw new SignatureException("Could not encode data", e); ++ } ++ } ++ ++ // kae sign ++ private byte[] kaeSign(byte[] digest) throws SignatureException { ++ String kaeDigestName = KAEUtils.getKAEDigestName(this.digestAlgorithm); ++ RSAPrivateCrtKey privateCrtKey = (RSAPrivateCrtKey) privateKey; ++ long keyAddress = KAERSACipher.nativeCreateRSAPrivateCrtKey( ++ privateCrtKey.getModulus().toByteArray(), ++ privateCrtKey.getPublicExponent().toByteArray(), ++ privateCrtKey.getPrivateExponent().toByteArray(), ++ privateCrtKey.getPrimeP().toByteArray(), ++ privateCrtKey.getPrimeQ().toByteArray(), ++ privateCrtKey.getPrimeExponentP().toByteArray(), ++ privateCrtKey.getPrimeExponentQ().toByteArray(), ++ privateCrtKey.getCrtCoefficient().toByteArray()); ++ byte[] sigBytes; ++ try { ++ sigBytes = KAERSASignatureNative.rsaSign(keyAddress, ++ kaeDigestName, digest, KAERSAPaddingType.PKCS1Padding.getId()); ++ } catch (SignatureException e) { ++ throw e; ++ } finally { ++ // free keyAddress ++ KAERSACipher.nativeFreeKey(keyAddress); ++ } ++ return sigBytes; ++ } ++ ++ // verify the data and return the result. See JCA doc ++ @Override ++ protected boolean engineVerify(byte[] sigBytes) throws SignatureException { ++ if (publicKey == null) { ++ throw new SignatureException("Missing public key"); ++ } ++ ++ boolean verify; ++ long keyAddress = 0L; ++ try { ++ if (sigBytes.length != RSACore.getByteLength(publicKey)) { ++ throw new SignatureException("Signature length not correct: got " + ++ sigBytes.length + " but was expecting " + ++ RSACore.getByteLength(publicKey)); ++ } ++ ++ String kaeDigestName = KAEUtils.getKAEDigestName(this.digestAlgorithm); ++ byte[] digest = getDigestValue(); ++ keyAddress = KAERSACipher.nativeCreateRSAPublicKey(publicKey.getModulus().toByteArray(), ++ publicKey.getPublicExponent().toByteArray()); ++ verify = KAERSASignatureNative.rsaVerify(keyAddress, ++ kaeDigestName, digest, KAERSAPaddingType.PKCS1Padding.getId(), sigBytes); ++ } catch (SignatureException e) { ++ throw e; ++ } catch (BadPaddingException e) { ++ // occurs if the app has used the wrong RSA public key ++ // or if sigBytes is invalid or sourceBytes is invalid ++ // return false rather than propagating the exception for ++ // compatibility/ease of use ++ return false; ++ } finally { ++ // reset digest ++ resetDigest(); ++ // free keyAddress ++ if (keyAddress != 0L) { ++ KAERSACipher.nativeFreeKey(keyAddress); ++ } ++ } ++ return verify; ++ } ++ ++ /** ++ * Encode the digest, return the to-be-signed data. ++ * Also used by the PKCS#11 provider. ++ */ ++ public static byte[] encodeSignature(ObjectIdentifier oid, byte[] digest) throws IOException { ++ DerOutputStream out = new DerOutputStream(); ++ new AlgorithmId(oid).encode(out); ++ out.putOctetString(digest); ++ DerValue result = ++ new DerValue(DerValue.tag_Sequence, out.toByteArray()); ++ return result.toByteArray(); ++ } ++ ++ // set parameter, not supported. See JCA doc ++ @Deprecated ++ @Override ++ protected void engineSetParameter(String param, Object value) throws InvalidParameterException { ++ throw new UnsupportedOperationException("setParameter() not supported"); ++ } ++ ++ // See JCA doc ++ @Override ++ protected void engineSetParameter(AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException { ++ if (params != null) { ++ throw new InvalidAlgorithmParameterException("No parameters accepted"); ++ } ++ } ++ ++ // get parameter, not supported. See JCA doc ++ @Deprecated ++ @Override ++ protected Object engineGetParameter(String param) throws InvalidParameterException { ++ throw new UnsupportedOperationException("getParameter() not supported"); ++ } ++ ++ // See JCA doc ++ @Override ++ protected AlgorithmParameters engineGetParameters() { ++ return null; ++ } ++ ++ // Nested class for MD5withRSA signatures ++ public static final class MD5withRSA extends KAERSASignature { ++ public MD5withRSA() { ++ super("MD5", AlgorithmId.MD5_oid, 10); ++ } ++ } ++ ++ // Nested class for SHA1withRSA signatures ++ public static final class SHA1withRSA extends KAERSASignature { ++ public SHA1withRSA() { ++ super("SHA-1", AlgorithmId.SHA_oid, 7); ++ } ++ } ++ ++ // Nested class for SHA224withRSA signatures ++ public static final class SHA224withRSA extends KAERSASignature { ++ public SHA224withRSA() { ++ super("SHA-224", AlgorithmId.SHA224_oid, 11); ++ } ++ } ++ ++ // Nested class for SHA256withRSA signatures ++ public static final class SHA256withRSA extends KAERSASignature { ++ public SHA256withRSA() { ++ super("SHA-256", AlgorithmId.SHA256_oid, 11); ++ } ++ } ++ ++ // Nested class for SHA384withRSA signatures ++ public static final class SHA384withRSA extends KAERSASignature { ++ public SHA384withRSA() { ++ super("SHA-384", AlgorithmId.SHA384_oid, 11); ++ } ++ } ++ ++ // Nested class for SHA512withRSA signatures ++ public static final class SHA512withRSA extends KAERSASignature { ++ public SHA512withRSA() { ++ super("SHA-512", AlgorithmId.SHA512_oid, 11); ++ } ++ } ++} +diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSASignatureNative.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSASignatureNative.java +new file mode 100644 +index 000000000..dda2f068d +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSASignatureNative.java +@@ -0,0 +1,46 @@ ++/* ++ * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2023, 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 javax.crypto.BadPaddingException; ++import java.security.SignatureException; ++ ++public class KAERSASignatureNative { ++ // rsa sign ++ protected static native byte[] rsaSign(long keyAddress, String digestName, byte[] digestBytes, int paddingType) ++ throws SignatureException; ++ ++ // rsa verify ++ protected static native boolean rsaVerify(long keyAddress, String digestName, byte[] digestBytes, int paddingType, ++ byte[] sigBytes) throws SignatureException, BadPaddingException; ++ ++ // rsa pss sign ++ protected static native byte[] pssSign(long keyAddress, String digestName, byte[] digestBytes, int paddingType, ++ String mgf1DigestName, int saltLen) throws SignatureException; ++ ++ // rsa pss verify ++ protected static native boolean pssVerify(long keyAddress, String digestName, byte[] digestBytes, int paddingType, ++ String mgf1DigestName, int saltLen, byte[] sigBytes) throws SignatureException, BadPaddingException; ++} +diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESM4Cipher.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESM4Cipher.java +new file mode 100644 +index 000000000..a3e47606d +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESM4Cipher.java +@@ -0,0 +1,370 @@ ++/* ++ * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2023, 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. 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.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: ++ * - SM4/ECB/NOPADDING ++ * - SM4/ECB/PKCS5PADDING ++ * - SM4/CBC/NOPADDING ++ * - SM4/CBC/PKCS5PADDING ++ * - SM4/CTR/NOPADDING ++ * - SM4/OFB/NOPADDING ++ * - SM4/OFB/PKCS5PADDING ++ */ ++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); ++ } ++ ++ public static class Cbc extends Sm4 { ++ public Cbc(Padding padding) { ++ super(Mode.CBC, padding); ++ } ++ public static class NoPadding extends Cbc { ++ public NoPadding() { ++ super(Padding.NOPADDING); ++ } ++ } ++ public static class PKCS5Padding extends Cbc { ++ public PKCS5Padding() { ++ super(Padding.PKCS5PADDING); ++ } ++ } ++ } ++ ++ public static class Ecb extends Sm4 { ++ public Ecb(Padding padding) { ++ super(Mode.ECB, padding); ++ } ++ public static class NoPadding extends Ecb { ++ public NoPadding() { ++ super(Padding.NOPADDING); ++ } ++ } ++ public static class PKCS5Padding extends Ecb { ++ public PKCS5Padding() { ++ super(Padding.PKCS5PADDING); ++ } ++ } ++ } ++ ++ public static class Ctr extends Sm4 { ++ public Ctr(Padding padding) { ++ super(Mode.CTR, padding); ++ } ++ public static class NoPadding extends Ctr { ++ public NoPadding() { ++ super(Padding.NOPADDING); ++ } ++ } ++ } ++ ++ public static class Ofb extends Sm4 { ++ public Ofb(Padding padding) { ++ super(Mode.OFB, padding); ++ } ++ public static class NoPadding extends Ofb { ++ public NoPadding() { ++ super(Padding.NOPADDING); ++ } ++ } ++ public static class PKCS5Padding extends Ofb { ++ public PKCS5Padding() { ++ super(Padding.PKCS5PADDING); ++ } ++ } ++ } ++ } ++ ++ KAESM4Cipher(Mode mode, Padding padding, int fixedKeySize) { ++ super(mode, padding, fixedKeySize, "SM4"); ++ } ++ ++ protected void checkKey(Key key) throws InvalidKeyException { ++ if (key == null || key.getEncoded() == null) { ++ throw new InvalidKeyException("Key cannot be null"); ++ } else { ++ int keyLen = key.getEncoded().length; ++ if (keyLen != fixedKeySize) { ++ throw new InvalidKeyException("Only " + fixedKeySize + "-byte keys are accepted. Got: " + keyLen); ++ } ++ } ++ } ++ ++ protected String getCipherName(int keyLength, Mode mode) { ++ return "sm4" + "-" + mode.toString().toLowerCase(Locale.US); ++ } ++ ++ @Override ++ protected void engineSetMode(String modeStr) throws NoSuchAlgorithmException { ++ if (modeStr == null) { ++ throw new NoSuchAlgorithmException("null mode"); ++ } ++ ++ if (modeStr.equalsIgnoreCase("ECB")) { ++ mode = Mode.ECB; ++ } else if (modeStr.equalsIgnoreCase("CBC")) { ++ mode = Mode.CBC; ++ } else if (modeStr.equalsIgnoreCase("CTR")) { ++ mode = Mode.CTR; ++ } else if (modeStr.equalsIgnoreCase("OFB")) { ++ mode = Mode.OFB; ++ } else { ++ throw new NoSuchAlgorithmException("Unsupported mode " + mode); ++ } ++ } ++ ++ @Override ++ protected void engineSetPadding(String paddingStr) throws NoSuchPaddingException { ++ if (paddingStr == null) { ++ throw new NoSuchPaddingException("null padding"); ++ } ++ if (paddingStr.equalsIgnoreCase("PKCS7PADDING")) { ++ paddingStr = "PKCS5Padding"; ++ } ++ ++ if (paddingStr.equalsIgnoreCase("NOPADDING")) { ++ this.padding = Padding.NOPADDING; ++ } else if(paddingStr.equalsIgnoreCase("PKCS5PADDING")) { ++ if (mode == Mode.CTR) { ++ throw new NoSuchPaddingException("PKCS#5 padding not supported with CTR mode"); ++ } ++ this.padding = Padding.PKCS5PADDING; ++ } else { ++ throw new NoSuchPaddingException("Unsupported padding " + paddingStr); ++ } ++ } ++ ++ @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 "); ++ } ++ if (mode == Mode.CTR) { ++ // For compatibility, SM4 CTR allows 8 < IV < blockSize, the remaining bytes will be filled with 0 in engineInit ++ if (ivBytes.length < 8 || ivBytes.length > blockSize) { ++ throw new InvalidAlgorithmParameterException("Wrong IV length: CTR mode requires IV of at least" + ++ "8 bytes, and no greater than " + blockSize + "bytes"); ++ } ++ return; ++ } ++ if (ivBytes.length != blockSize) { ++ throw new InvalidAlgorithmParameterException("Wrong IV length: must be " + blockSize + " bytes long."); ++ } ++ } ++} +\ No newline at end of file +diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESM4KeyGenerator.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESM4KeyGenerator.java +new file mode 100644 +index 000000000..3f60e804f +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESM4KeyGenerator.java +@@ -0,0 +1,74 @@ ++/* ++ * Copyright (c) 2023, 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. Huawei designates this ++ * particular file as subject to the "Classpath" exception as provided ++ * by Huawei 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 visit https://gitee.com/openeuler/bgmprovider if you need additional ++ * information or have any questions. ++ */ ++ ++package org.openeuler.security.openssl; ++ ++import javax.crypto.KeyGeneratorSpi; ++import javax.crypto.SecretKey; ++import javax.crypto.spec.SecretKeySpec; ++import java.security.InvalidAlgorithmParameterException; ++import java.security.InvalidParameterException; ++import java.security.SecureRandom; ++import java.security.spec.AlgorithmParameterSpec; ++ ++public class KAESM4KeyGenerator extends KeyGeneratorSpi { ++ private byte[] key; ++ private int keySize = 16; // default keysize (in number of bytes) ++ private SecureRandom random; ++ ++ @Override ++ protected void engineInit(SecureRandom random) { ++ this.random = random; ++ } ++ ++ @Override ++ protected void engineInit(AlgorithmParameterSpec params, SecureRandom random) ++ throws InvalidAlgorithmParameterException { ++ throw new InvalidAlgorithmParameterException ++ ("SM4 key generation does not take any parameters"); ++ } ++ ++ @Override ++ protected void engineInit(int keysize, SecureRandom random) { ++ if (keysize != 128) { ++ throw new InvalidParameterException("SM4 requires a 128 bit key"); ++ } ++ this.keySize = keysize / 8; ++ engineInit(random); ++ } ++ ++ private static class SecureRandomHolder { ++ static final SecureRandom RANDOM = new SecureRandom(); ++ } ++ ++ @Override ++ protected SecretKey engineGenerateKey() { ++ key = new byte[keySize]; ++ if (random == null) { ++ random = SecureRandomHolder.RANDOM; ++ } ++ random.nextBytes(key); ++ return new SecretKeySpec(key, "SM4"); ++ } ++} +diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESymmetricCipherBase.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESymmetricCipherBase.java +new file mode 100644 +index 000000000..7b5433b38 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESymmetricCipherBase.java +@@ -0,0 +1,791 @@ ++/* ++ * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2023, 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. 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.openeuler.security.openssl; ++ ++import sun.security.jca.JCAUtil; ++ ++import java.lang.ref.PhantomReference; ++import java.lang.ref.ReferenceQueue; ++import java.nio.ByteBuffer; ++import java.security.*; ++import java.security.spec.*; ++import java.util.Arrays; ++import java.util.Set; ++import java.util.concurrent.ConcurrentSkipListSet; ++ ++import javax.crypto.*; ++import javax.crypto.spec.GCMParameterSpec; ++import javax.crypto.spec.IvParameterSpec; ++import javax.crypto.spec.SecretKeySpec; ++ ++/* ++ * Cipher wrapper class utilizing openssl APIs. ++ */ ++abstract class KAESymmetricCipherBase extends CipherSpi { ++ enum Padding { ++ NOPADDING, ++ PKCS5PADDING ++ } ++ ++ enum Mode { ++ ECB, ++ CBC, ++ CTR, ++ OFB, ++ GCM ++ } ++ ++ protected final String keyAlgo; ++ protected final int blockSize = 16; ++ protected Mode mode; ++ protected Padding padding; ++ protected int fixedKeySize; ++ ++ private CipherContextRef pCtx = null; ++ private byte[] keyValue; ++ protected byte[] iv; ++ private boolean initialized = false; ++ private boolean encrypt = false; ++ private int bytesBuffered = 0; ++ ++ private boolean calledUpdate; ++ private String cipherName; ++ ++ // for gcm ++ private final int defaultGcmTagLen = blockSize; ++ private final int defaultGcmIvLen = 12; ++ private int tagLengthInBytes; ++ private byte[] lastEncKey = null; ++ private byte[] lastEncIv = null; ++ private byte[] aad; ++ ++ private static PublicKey constructPublicKey(byte[] encodedKey, String encodedKeyAlgorithm) ++ throws NoSuchAlgorithmException, InvalidKeyException { ++ PublicKey key; ++ try { ++ KeyFactory keyFactory = KeyFactory.getInstance(encodedKeyAlgorithm); ++ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedKey); ++ key = keyFactory.generatePublic(keySpec); ++ } catch (NoSuchAlgorithmException e) { ++ throw new NoSuchAlgorithmException("No provider found for " + encodedKeyAlgorithm + " KeyFactory"); ++ } catch (InvalidKeySpecException e) { ++ throw new InvalidKeyException("Cannot construct public key", e); ++ } ++ return key; ++ } ++ ++ private static PrivateKey constructPrivateKey(byte[] encodedKey, ++ String encodedKeyAlgorithm) throws InvalidKeyException, NoSuchAlgorithmException { ++ PrivateKey key = null; ++ try { ++ KeyFactory keyFactory = KeyFactory.getInstance(encodedKeyAlgorithm); ++ PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey); ++ key = keyFactory.generatePrivate(keySpec); ++ } catch (NoSuchAlgorithmException e) { ++ throw new NoSuchAlgorithmException("No provider found for " + encodedKeyAlgorithm + " KeyFactory"); ++ } catch (InvalidKeySpecException e) { ++ throw new InvalidKeyException("Cannot construct private key", e); ++ } ++ return key; ++ } ++ ++ private static SecretKey constructSecretKey(byte[] encodedKey, String encodedKeyAlgorithm) { ++ return new SecretKeySpec(encodedKey, encodedKeyAlgorithm); ++ } ++ ++ static final Key constructKey(int keyType, byte[] encodedKey, ++ String encodedKeyAlgorithm) throws NoSuchAlgorithmException, InvalidKeyException { ++ Key res = null; ++ switch (keyType) { ++ case Cipher.SECRET_KEY: ++ res = constructSecretKey(encodedKey, encodedKeyAlgorithm); ++ break; ++ case Cipher.PRIVATE_KEY: ++ res = constructPrivateKey(encodedKey, encodedKeyAlgorithm); ++ break; ++ case Cipher.PUBLIC_KEY: ++ res = constructPublicKey(encodedKey, encodedKeyAlgorithm); ++ break; ++ default: ++ throw new InvalidKeyException("Unknown keytype " + keyType); ++ } ++ return res; ++ } ++ ++ KAESymmetricCipherBase(Mode mode, Padding padding, int fixedKeySize, String keyAlgo) { ++ this.mode = mode; ++ this.padding = padding; ++ this.fixedKeySize = fixedKeySize; ++ this.keyAlgo = keyAlgo; ++ } ++ ++ private static class CipherContextRef extends PhantomReference ++ implements Comparable { ++ private static ReferenceQueue refQueue = new ReferenceQueue<>(); ++ private static Set refList = new ConcurrentSkipListSet<>(); ++ private static boolean disableKaeDispose = Boolean.getBoolean("kae.disableKaeDispose"); ++ ++ final long ctxAddress; ++ ++ private static void drainRefQueueBounded() { ++ while (true) { ++ CipherContextRef next = (CipherContextRef) refQueue.poll(); ++ if (next == null) { ++ break; ++ } ++ next.dispose(true); ++ } ++ } ++ ++ CipherContextRef(KAESymmetricCipherBase kaeCipher, long ctxAddress) { ++ super(kaeCipher, refQueue); ++ this.ctxAddress = ctxAddress; ++ if (!disableKaeDispose) { ++ refList.add(this); ++ drainRefQueueBounded(); ++ } ++ } ++ ++ @Override ++ public int compareTo(CipherContextRef o) { ++ if (this.ctxAddress == o.ctxAddress) { ++ return 0; ++ } else { ++ return (this.ctxAddress < o.ctxAddress) ? -1 : 1; ++ } ++ } ++ ++ void dispose(boolean needFree) { ++ if (!disableKaeDispose) { ++ refList.remove(this); ++ try { ++ if (needFree) { ++ nativeFree(ctxAddress); ++ } ++ } finally { ++ this.clear(); ++ } ++ } else { ++ nativeFree(ctxAddress); ++ } ++ } ++ } ++ ++ @Override ++ protected int engineGetBlockSize() { ++ return blockSize; ++ } ++ ++ @Override ++ protected int engineGetOutputSize(int inputLen) { ++ return getOutputSizeByOperation(inputLen, true); ++ } ++ ++ @Override ++ protected byte[] engineGetIV() { ++ return iv == null ? null : iv.clone(); ++ } ++ ++ @Override ++ protected AlgorithmParameters engineGetParameters() { ++ if (iv == null) { ++ return null; ++ } ++ AlgorithmParameterSpec spec; ++ AlgorithmParameters params; ++ String algName = keyAlgo; ++ if (mode == Mode.GCM) { ++ algName = "GCM"; ++ spec = new GCMParameterSpec(tagLengthInBytes * 8, iv.clone()); ++ } else { ++ spec = new IvParameterSpec(iv.clone()); ++ } ++ try { ++ params = AlgorithmParameters.getInstance(algName); ++ params.init(spec); ++ return params; ++ } catch (GeneralSecurityException e) { ++ throw new RuntimeException("Could not encode parameters", e); ++ } ++ } ++ ++ @Override ++ protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException { ++ try { ++ engineInit(opmode, key, (AlgorithmParameterSpec) null, random); ++ } catch (InvalidAlgorithmParameterException e) { ++ throw new InvalidKeyException("init() failed", e); ++ } ++ } ++ ++ @Override ++ protected void engineInit(int opmode, Key key, AlgorithmParameters params, ++ SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { ++ AlgorithmParameterSpec spec = null; ++ String paramType = null; ++ if (params != null) { ++ try { ++ if (mode == Mode.GCM) { ++ spec = params.getParameterSpec(GCMParameterSpec.class); ++ paramType = "GCM"; ++ } else { ++ spec = params.getParameterSpec(IvParameterSpec.class); ++ paramType = "IV"; ++ } ++ } catch (InvalidParameterSpecException e) { ++ throw new InvalidAlgorithmParameterException("Could not decode " + paramType, e); ++ } ++ } ++ engineInit(opmode, key, spec, random); ++ } ++ ++ @Override ++ protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, ++ SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { ++ checkKey(key); ++ boolean doEncrypt = (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE); ++ ++ byte[] ivBytes = null; ++ int tagLen = -1; ++ if (params != null) { ++ if (mode == Mode.GCM) { ++ if (params instanceof GCMParameterSpec) { ++ tagLen = ((GCMParameterSpec)params).getTLen(); ++ checkTagLen(tagLen); ++ tagLen = tagLen >> 3; ++ ivBytes = ((GCMParameterSpec)params).getIV(); ++ } else { ++ throw new InvalidAlgorithmParameterException("Unsupported parameter: " + params); ++ } ++ } else { ++ if (params instanceof IvParameterSpec) { ++ ivBytes = ((IvParameterSpec) params).getIV(); ++ checkIvBytes(ivBytes); ++ } else { ++ throw new InvalidKeyException("IvParameterSpec required. Received: " + params.getClass().getName()); ++ } ++ } ++ } ++ if (mode == Mode.ECB) { ++ if (params != null) { ++ throw new InvalidAlgorithmParameterException("No Parameters for ECB mode"); ++ } ++ } else if (ivBytes == null) { ++ if (doEncrypt) { ++ if (mode == Mode.GCM) { ++ ivBytes = new byte[defaultGcmIvLen]; ++ } else { ++ ivBytes = new byte[blockSize]; ++ } ++ if (random == null) { ++ random = JCAUtil.getSecureRandom(); ++ } ++ random.nextBytes(ivBytes); ++ } else { ++ throw new InvalidAlgorithmParameterException("Parameters required for decryption"); ++ } ++ } else if (keyAlgo.equalsIgnoreCase("SM4") && ivBytes.length < blockSize) { ++ byte[] temp = new byte[blockSize]; ++ System.arraycopy(ivBytes, 0, temp, 0, ivBytes.length); ++ ivBytes = temp; ++ } ++ implInit(doEncrypt, key.getEncoded(), ivBytes, tagLen); ++ } ++ ++ private void checkTagLen(int tagLen) throws InvalidAlgorithmParameterException { ++ if ((tagLen < 96) || (tagLen > 128) || ((tagLen & 0x07) != 0)) { ++ throw new InvalidAlgorithmParameterException ++ ("Unsupported TLen value; must be one of {128, 120, 112, 104, 96}"); ++ } ++ } ++ ++ protected abstract void checkIvBytes(byte[] ivBytes) throws InvalidAlgorithmParameterException; ++ ++ protected abstract String getCipherName(int keyLength, Mode mode); ++ ++ private void implInit(boolean encrypt, byte[] keyVal, byte[] ivVal, int tagLen) ++ throws InvalidAlgorithmParameterException { ++ reset(true); ++ this.encrypt = encrypt; ++ this.keyValue = keyVal; ++ this.iv = ivVal; ++ this.cipherName = getCipherName(keyValue.length * 8, mode); ++ ++ if (mode == Mode.GCM) { ++ if (tagLen == -1) { ++ tagLen = defaultGcmTagLen; ++ } ++ this.tagLengthInBytes = tagLen; ++ if (encrypt) { ++ // Check key+iv for encryption in GCM mode. ++ boolean requireReinit = Arrays.equals(ivVal, lastEncIv) && MessageDigest.isEqual(keyVal, lastEncKey); ++ if (requireReinit) { ++ throw new InvalidAlgorithmParameterException("Cannot reuse iv for GCM encryption"); ++ } ++ lastEncIv = ivVal; ++ lastEncKey = keyVal; ++ } ++ } ++ ++ // OpenSSL only supports PKCS5 Padding. ++ long pCtxVal; ++ try { ++ pCtxVal = nativeInit(cipherName, encrypt, keyValue, iv, padding == Padding.PKCS5PADDING); ++ } catch (RuntimeException e) { ++ throw new ProviderException("Invoke nativeInit failed for " + cipherName, e); ++ } ++ ++ initialized = (pCtxVal != 0L); ++ if (initialized) { ++ pCtx = new CipherContextRef(this, pCtxVal); ++ } else { ++ throw new NullPointerException("pCtxVal == 0"); ++ } ++ calledUpdate = false; ++ } ++ ++ protected abstract void checkKey(Key key) throws InvalidKeyException; ++ ++ @Override ++ protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { ++ byte[] out = new byte[getOutputSizeByOperation(inputLen, false)]; ++ int outLen = implUpdate(input, inputOffset, inputLen, out, 0); ++ if (outLen == 0) { ++ return new byte[0]; ++ } else if (out.length != outLen) { ++ out = Arrays.copyOf(out, outLen); ++ } ++ return out; ++ } ++ ++ @Override ++ protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, ++ int outputOffset) throws ShortBufferException { ++ int min = getOutputSizeByOperation(inputLen, false); ++ if (output == null || output.length - outputOffset < min) { ++ throw new ShortBufferException("min " + min + "-byte buffer needed"); ++ } ++ return implUpdate(input, inputOffset, inputLen, output, outputOffset); ++ } ++ ++ private int implUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) { ++ ensureInitialized(); ++ if (inputLen <= 0) { ++ return 0; ++ } ++ int outLen; ++ try { ++ outLen = nativeUpdate(pCtx.ctxAddress, input, inputOffset, inputLen, output, outputOffset, ++ mode == Mode.GCM, aad); ++ aad = null; ++ } catch (ArrayIndexOutOfBoundsException e) { ++ reset(true); ++ throw new ProviderException("Invoke nativeUpdate failed for " + cipherName, e); ++ } ++ bytesBuffered += (inputLen - outLen); ++ ++ calledUpdate = true; ++ return outLen; ++ } ++ ++ protected int getOutputSizeByOperation(int inLen, boolean isDoFinal) { ++ int ret; ++ ++ if (inLen <= 0) { ++ inLen = 0; ++ } ++ if (padding == Padding.NOPADDING) { ++ ret = inLen + bytesBuffered; ++ } else { ++ int len = inLen + bytesBuffered; ++ ++ // The amount of data written may be anything from zero bytes to (inl + cipher_block_size - 1) for encrypt. ++ // Refer to {@link https://www.openssl.org/docs/man1.1.0/man3/EVP_CipherUpdate.html} for details. ++ len += (len % blockSize != 0 || encrypt) ? blockSize : 0; ++ ret = len - (len % blockSize); ++ } ++ if (mode == Mode.GCM && isDoFinal) { ++ if (encrypt) { ++ ret = ret + tagLengthInBytes; ++ } else { ++ ret = Math.max(0, ret - tagLengthInBytes); ++ } ++ } ++ return ret; ++ } ++ ++ @Override ++ protected byte[] engineDoFinal(byte[] input, int inputOffset, ++ int inputLen) throws IllegalBlockSizeException, BadPaddingException { ++ byte[] out = new byte[getOutputSizeByOperation(inputLen, true)]; ++ try { ++ int outLen = engineDoFinal(input, inputOffset, inputLen, out, 0); ++ if (out.length != outLen) { ++ out = Arrays.copyOf(out, outLen); ++ } ++ return out; ++ } catch (ShortBufferException e) { ++ throw new ProviderException(e); ++ } ++ } ++ ++ @Override ++ protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, ++ int outputOffset) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { ++ int outLen = 0; ++ int min = getOutputSizeByOperation(inputLen, true); ++ if (output == null || output.length - outputOffset < min) { ++ throw new ShortBufferException("min " + min + "-byte buffer needed"); ++ } ++ ++ int updateLen = inputLen; ++ if (mode == Mode.GCM && !encrypt) { ++ // Remove tagLengthInBytes suffix in GCM decrypt. ++ updateLen = inputLen - tagLengthInBytes; ++ } ++ outLen = implUpdate(input, inputOffset, updateLen, output, outputOffset); ++ outputOffset += outLen; ++ ++ byte[] gcmTag = null; ++ if (mode == Mode.GCM && !encrypt) { ++ if (inputLen - outLen != tagLengthInBytes) { ++ throw new AEADBadTagException("Tag mismatch!"); ++ } ++ // The last tagLengthInBytees in the input arg gcmTag. ++ gcmTag = Arrays.copyOfRange(input, inputOffset + inputLen - tagLengthInBytes, inputOffset + inputLen); ++ } ++ ++ outLen += implDoFinal(output, outputOffset, gcmTag); ++ return outLen; ++ } ++ ++ @Override ++ protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, InvalidKeyException { ++ byte[] res = null; ++ try { ++ byte[] encodedKey = key.getEncoded(); ++ if (encodedKey == null || encodedKey.length == 0) { ++ throw new InvalidKeyException("Cannot get an encoding of the key to be wrapped"); ++ } ++ res = engineDoFinal(encodedKey, 0, encodedKey.length); ++ } catch (BadPaddingException e) { ++ // Should never happen ++ } ++ return res; ++ } ++ ++ @Override ++ protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, ++ int wrappedKeyType) throws InvalidKeyException, NoSuchAlgorithmException { ++ byte[] encodedKey; ++ try { ++ encodedKey = engineDoFinal(wrappedKey, 0, wrappedKey.length); ++ } catch (IllegalBlockSizeException | BadPaddingException e) { ++ throw (InvalidKeyException) (new InvalidKeyException()).initCause(e); ++ } ++ return constructKey(wrappedKeyType, encodedKey, wrappedKeyAlgorithm); ++ } ++ ++ @Override ++ protected void engineUpdateAAD(ByteBuffer byteBuffer) { ++ if (aad == null) { ++ aad = new byte[byteBuffer.remaining()]; ++ byteBuffer.get(aad); ++ } else { ++ int newSize = aad.length + byteBuffer.remaining(); ++ byte[] newaad = new byte[newSize]; ++ System.arraycopy(aad, 0, newaad, 0, aad.length); ++ byteBuffer.get(newaad, aad.length, byteBuffer.remaining()); ++ aad = newaad; ++ } ++ } ++ ++ @Override ++ protected void engineUpdateAAD(byte[] input, int inputOffset, int inputLen) { ++ if (aad == null) { ++ aad = Arrays.copyOfRange(input, inputOffset, inputOffset + inputLen); ++ } else { ++ int newSize = aad.length + inputLen; ++ byte[] newaad = new byte[newSize]; ++ System.arraycopy(aad, 0, newaad, 0, aad.length); ++ System.arraycopy(input, inputOffset, newaad, aad.length, inputLen); ++ aad = newaad; ++ } ++ } ++ ++ private int implDoFinal(byte[] out, int outputOffset, byte[] gcmTag) ++ throws BadPaddingException, IllegalBlockSizeException { ++ if (!encrypt && !calledUpdate) { ++ return 0; ++ } ++ ensureInitialized(); ++ ++ int outLen; ++ try { ++ if (mode == Mode.GCM) { ++ outLen = nativeFinalGcm(pCtx.ctxAddress, out, outputOffset, mode == Mode.GCM, tagLengthInBytes, ++ gcmTag, encrypt); ++ } else { ++ outLen = nativeFinal(pCtx.ctxAddress, out, outputOffset); ++ } ++ } catch (ArrayIndexOutOfBoundsException | BadPaddingException e) { ++ if (e instanceof AEADBadTagException) { ++ throw e; // AEADBadTagException is expected for some tests ++ } else if (e instanceof BadPaddingException) { ++ if (padding == Padding.NOPADDING || e.getMessage().contains("wrong final block length")) { ++ throw new IllegalBlockSizeException("Input length not multiple of " + blockSize + " bytes"); ++ } else { ++ throw e; ++ } ++ } else { ++ throw new ProviderException("Invoke nativeFinal failed for " + cipherName, e); ++ } ++ } finally { ++ reset(true); ++ } ++ ++ return outLen; ++ } ++ ++ protected void reset(boolean doCancel) { ++ initialized = false; ++ bytesBuffered = 0; ++ calledUpdate = false; ++ ++ // for gcm ++ aad = null; ++ ++ if (pCtx != null) { ++ pCtx.dispose(doCancel); ++ pCtx = null; ++ } ++ } ++ ++ protected static native long nativeInit(String cipherType, boolean encrypt, byte[] key, byte[] iv, boolean padding) ++ throws RuntimeException; ++ ++ protected static native int nativeUpdate(long pContext, byte[] in, int inOfs, int inLen, byte[] out, ++ int outOfs, boolean gcm, byte[] aad) throws ArrayIndexOutOfBoundsException; ++ ++ protected static native int nativeFinal(long pContext, byte[] out, ++ int outOfs) throws ArrayIndexOutOfBoundsException, BadPaddingException; ++ ++ protected static native void nativeFree(long pContext); ++ ++ protected static native int nativeFinalGcm(long pContext, byte[] out, int outOfs, boolean gcm, ++ int tagLength, byte[] gcmTag, boolean encrypt) throws ArrayIndexOutOfBoundsException, BadPaddingException; ++ ++ protected void ensureInitialized() { ++ if (!initialized) { ++ reset(true); ++ long pCtxVal = nativeInit(cipherName, encrypt, keyValue, iv, padding == Padding.PKCS5PADDING); ++ initialized = (pCtxVal != 0L); ++ if (initialized) { ++ pCtx = new CipherContextRef(this, pCtxVal); ++ } else { ++ throw new RuntimeException("Cannot initialize Cipher"); ++ } ++ } ++ } ++ ++ // copied from sun.security.jca.JCAUtil ++ // will be changed to reference that method once that code has been ++ // integrated and promoted ++ static int getTempArraySize(int totalSize) { ++ return Math.min(4096, totalSize); ++ } ++ ++ /** ++ * Continues a multiple-part encryption or decryption operation ++ * (depending on how this cipher was initialized), processing another data ++ * part. ++ * ++ *

All input.remaining() bytes starting at ++ * input.position() are processed. The result is stored ++ * in the output buffer. ++ * Upon return, the input buffer's position will be equal ++ * to its limit; its limit will not have changed. The output buffer's ++ * position will have advanced by n, where n is the value returned ++ * by this method; the output buffer's limit will not have changed. ++ * ++ *

If output.remaining() bytes are insufficient to ++ * hold the result, a ShortBufferException is thrown. ++ * ++ *

Subclasses should consider overriding this method if they can ++ * process ByteBuffers more efficiently than byte arrays. ++ * ++ * @param input the input ByteBuffer ++ * @param output the output ByteByffer ++ * ++ * @return the number of bytes stored in output ++ * ++ * @exception ShortBufferException if there is insufficient space in the ++ * output buffer ++ * ++ * @throws NullPointerException if either parameter is null ++ * @since 1.5 ++ */ ++ 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()"); ++ } ++ } ++ ++ /** ++ * Finalize crypto operation with ByteBuffers ++ * ++ * @param input the input ByteBuffer ++ * @param output the output ByteBuffer ++ * ++ * @return output length ++ * @throws ShortBufferException ++ * @throws IllegalBlockSizeException ++ * @throws BadPaddingException ++ */ ++ @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 = getOutputSizeByOperation(inLen, !isUpdate); ++ ++ 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; ++ ++ if (a2) { // output has an accessible byte[] ++ 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); ++ } ++ // adjust input position manually ++ input.position(inLimit); ++ } else { // output does not have an accessible byte[] ++ byte[] outArray = null; ++ 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[getTempArraySize(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; ++ } ++} ++ +diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEUtils.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEUtils.java +new file mode 100644 +index 000000000..a4a005285 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEUtils.java +@@ -0,0 +1,220 @@ ++/* ++ * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2023, 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 javax.crypto.Cipher; ++import javax.crypto.SecretKey; ++import javax.crypto.spec.SecretKeySpec; ++import java.security.*; ++import java.security.spec.InvalidKeySpecException; ++import java.security.spec.PKCS8EncodedKeySpec; ++import java.security.spec.X509EncodedKeySpec; ++import java.util.*; ++ ++class KAEUtils { ++ enum MessageDigestType { ++ MD2("MD2", "md2", 16), ++ MD5("MD5", "md5", 16), ++ SHA1("SHA-1", "sha1", 20, ++ new HashSet<>(Arrays.asList("SHA1", "1.3.14.3.2.26", "OID.1.3.14.3.2.26"))), ++ SHA224("SHA-224", "sha224", 28, ++ new HashSet<>(Arrays.asList("2.16.840.1.101.3.4.2.4", "OID.2.16.840.1.101.3.4.2.4"))), ++ SHA256("SHA-256", "sha256", 32, ++ new HashSet<>(Arrays.asList("2.16.840.1.101.3.4.2.1", "OID.2.16.840.1.101.3.4.2.1"))), ++ SHA384("SHA-384", "sha384", 48, ++ new HashSet<>(Arrays.asList("2.16.840.1.101.3.4.2.2", "OID.2.16.840.1.101.3.4.2.2"))), ++ SHA512("SHA-512", "sha512", 64, ++ new HashSet<>(Arrays.asList("2.16.840.1.101.3.4.2.3", "OID.2.16.840.1.101.3.4.2.3"))), ++ SHA512_224("SHA-512/224", "sha512-224", 28, ++ new HashSet<>(Arrays.asList("2.16.840.1.101.3.4.2.5", "OID.2.16.840.1.101.3.4.2.5"))), ++ SHA_512_256("SHA-512/256", "sha512-256", 32, ++ new HashSet<>(Arrays.asList("2.16.840.1.101.3.4.2.6", "OID.2.16.840.1.101.3.4.2.6"))); ++ ++ final String digestName; ++ final String kaeDigestName; ++ final int digestLen; ++ final Set aliasNames; ++ ++ public String getDigestName() { ++ return digestName; ++ } ++ ++ public String getKaeDigestName() { ++ return kaeDigestName; ++ } ++ ++ public int getDigestLen() { ++ return digestLen; ++ } ++ ++ public Set getAliasNames() { ++ return aliasNames; ++ } ++ ++ MessageDigestType(String digestName, String kaeDigestName, int digestLen, Set aliasNames) { ++ this.digestName = digestName; ++ this.kaeDigestName = kaeDigestName; ++ this.digestLen = digestLen; ++ this.aliasNames = aliasNames; ++ } ++ ++ MessageDigestType(String digestName, String kaeDigestName, int digestLen) { ++ this(digestName, kaeDigestName, digestLen, Collections.emptySet()); ++ } ++ } ++ ++ /** ++ * kae digest algorithm info map ++ */ ++ private static final Map DIGEST_ALGORITHM_NAME_MAP = new HashMap<>(); ++ private static final Map DIGEST_ALGORITHM_LENGTH_MAP = new HashMap<>(); ++ ++ private static final Map SIZE_TO_CURVE = new HashMap<>(); ++ private static final Map CURVE_ALIAS = new HashMap<>(); ++ ++ static { ++ initDigest(); ++ initECDH(); ++ } ++ ++ private static void initDigest() { ++ MessageDigestType[] messageDigestTypes = MessageDigestType.values(); ++ for (MessageDigestType messageDigestType : messageDigestTypes) { ++ DIGEST_ALGORITHM_NAME_MAP.put(messageDigestType.getDigestName(), messageDigestType.getKaeDigestName()); ++ DIGEST_ALGORITHM_LENGTH_MAP.put(messageDigestType.getDigestName(), messageDigestType.getDigestLen()); ++ for (String aliasName : messageDigestType.getAliasNames()) { ++ DIGEST_ALGORITHM_NAME_MAP.put(aliasName, messageDigestType.getKaeDigestName()); ++ DIGEST_ALGORITHM_LENGTH_MAP.put(aliasName, messageDigestType.getDigestLen()); ++ } ++ } ++ } ++ ++ // get the kae digest algorithm name ++ static String getKAEDigestName(String digestName) { ++ return digestName == null ? null : DIGEST_ALGORITHM_NAME_MAP.get(digestName.toUpperCase(Locale.ROOT)); ++ } ++ ++ static Integer getDigestLength(String digestName) { ++ return digestName == null ? null : DIGEST_ALGORITHM_LENGTH_MAP.get(digestName.toUpperCase(Locale.ROOT)); ++ } ++ ++ static class ConstructKeys { ++ /** ++ * Construct a public key from its encoding. ++ * ++ * @param encodedKey the encoding of a public key. ++ * @param encodedKeyAlgorithm the algorithm the encodedKey is for. ++ * @return a public key constructed from the encodedKey. ++ */ ++ private static PublicKey constructPublicKey(byte[] encodedKey, ++ String encodedKeyAlgorithm) ++ throws InvalidKeyException, NoSuchAlgorithmException { ++ try { ++ KeyFactory keyFactory = ++ KeyFactory.getInstance(encodedKeyAlgorithm); ++ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedKey); ++ return keyFactory.generatePublic(keySpec); ++ } catch (NoSuchAlgorithmException nsae) { ++ throw new NoSuchAlgorithmException("No installed providers " + ++ "can create keys for the " + ++ encodedKeyAlgorithm + ++ "algorithm", nsae); ++ } catch (InvalidKeySpecException ike) { ++ throw new InvalidKeyException("Cannot construct public key", ike); ++ } ++ } ++ ++ /** ++ * Construct a private key from its encoding. ++ * ++ * @param encodedKey the encoding of a private key. ++ * @param encodedKeyAlgorithm the algorithm the wrapped key is for. ++ * @return a private key constructed from the encodedKey. ++ */ ++ private static PrivateKey constructPrivateKey(byte[] encodedKey, ++ String encodedKeyAlgorithm) throws InvalidKeyException, ++ NoSuchAlgorithmException { ++ try { ++ KeyFactory keyFactory = ++ KeyFactory.getInstance(encodedKeyAlgorithm); ++ PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey); ++ return keyFactory.generatePrivate(keySpec); ++ } catch (NoSuchAlgorithmException nsae) { ++ throw new NoSuchAlgorithmException("No installed providers " + ++ "can create keys for the " + ++ encodedKeyAlgorithm + ++ "algorithm", nsae); ++ } catch (InvalidKeySpecException ike) { ++ throw new InvalidKeyException("Cannot construct private key", ike); ++ } ++ } ++ ++ /** ++ * Construct a secret key from its encoding. ++ * ++ * @param encodedKey the encoding of a secret key. ++ * @param encodedKeyAlgorithm the algorithm the secret key is for. ++ * @return a secret key constructed from the encodedKey. ++ */ ++ private static SecretKey constructSecretKey(byte[] encodedKey, ++ String encodedKeyAlgorithm) { ++ return new SecretKeySpec(encodedKey, encodedKeyAlgorithm); ++ } ++ ++ static Key constructKey(byte[] encoding, String keyAlgorithm, ++ int keyType) throws InvalidKeyException, NoSuchAlgorithmException { ++ switch (keyType) { ++ case Cipher.SECRET_KEY: ++ return constructSecretKey(encoding, keyAlgorithm); ++ case Cipher.PRIVATE_KEY: ++ return constructPrivateKey(encoding, keyAlgorithm); ++ case Cipher.PUBLIC_KEY: ++ return constructPublicKey(encoding, keyAlgorithm); ++ default: ++ throw new InvalidKeyException("Unknown keytype " + keyType); ++ } ++ } ++ } ++ ++ private static void initECDH() { ++ SIZE_TO_CURVE.put(224, "secp224r1"); ++ SIZE_TO_CURVE.put(256, "prime256v1"); ++ SIZE_TO_CURVE.put(384, "secp384r1"); ++ SIZE_TO_CURVE.put(521, "secp521r1"); ++ CURVE_ALIAS.put("secp256r1", "prime256v1"); ++ CURVE_ALIAS.put("1.3.132.0.33", "secp224r1"); ++ CURVE_ALIAS.put("1.3.132.0.34", "secp384r1"); ++ CURVE_ALIAS.put("1.3.132.0.35", "secp521r1"); ++ CURVE_ALIAS.put("1.2.840.10045.3.1.7", "prime256v1"); ++ } ++ ++ static String getCurveBySize(int size) { ++ return SIZE_TO_CURVE.get(size); ++ } ++ ++ static String getCurveByAlias(String alias) { ++ return CURVE_ALIAS.get(alias); ++ } ++} +diff --git a/src/jdk.crypto.kaeprovider/linux/conf/security/kaeprovider.conf b/src/jdk.crypto.kaeprovider/linux/conf/security/kaeprovider.conf +new file mode 100644 +index 000000000..49ff98fd8 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/conf/security/kaeprovider.conf +@@ -0,0 +1,75 @@ ++# ++# 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. ++# kae.md5=false ++# kae.sha256=false ++# kae.sha384=false ++# kae.sm3=false ++# kae.aes=false ++# kae.sm4=false ++# kae.hmac=false ++# kae.rsa=false ++# kae.dh=false ++# kae.ec=false ++ ++# 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 +\ No newline at end of file +diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_cipher_rsa.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_cipher_rsa.c +new file mode 100644 +index 000000000..557d3965b +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_cipher_rsa.c +@@ -0,0 +1,471 @@ ++/* ++ * Copyright (c) 2023, 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 ++#include ++#include ++#include "kae_log.h" ++#include "kae_util.h" ++#include "kae_exception.h" ++#include "org_openeuler_security_openssl_KAERSACipher.h" ++ ++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); ++ ++typedef int EvpPkeyCryptInitOperation(EVP_PKEY_CTX*); ++ ++/* ++ * RSA encrypt or decrypt for NoPadding or PKCS1Padding , follow the steps below ++ * ++ */ ++static int RSACryptNotOAEPPadding(JNIEnv* env, jlong keyAddress, jint inLen, jbyteArray in, jbyteArray out, ++ jint paddingType, RSACryptOperation rsaCryptOperation, char* cryptName) { ++ jbyte* inBytes = NULL; ++ jbyte* outBytes = NULL; ++ int resultSize = 0; ++ ++ // get RSA ++ EVP_PKEY* pkey = (EVP_PKEY*) keyAddress; ++ ++ // rsa = pkey->rsa ++ RSA* rsa = EVP_PKEY_get1_RSA(pkey); ++ if (rsa == NULL) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_get1_RSA", KAE_ThrowRuntimeException); ++ return 0; ++ } ++ ++ // do encrypt or decrypt ++ inBytes = (*env)->GetByteArrayElements(env, in, NULL); ++ if (inBytes == NULL) { ++ KAE_ThrowNullPointerException(env, "GetByteArrayElements failed"); ++ goto cleanup; ++ } ++ outBytes = (*env)->GetByteArrayElements(env, out, NULL); ++ if (outBytes == NULL) { ++ KAE_ThrowNullPointerException(env, "GetByteArrayElements failed"); ++ goto cleanup; ++ } ++ resultSize = rsaCryptOperation(inLen, (unsigned char*)inBytes, (unsigned char*)outBytes, rsa, paddingType); ++ if (resultSize <= 0) { ++ KAE_ThrowFromOpenssl(env, cryptName, KAE_ThrowBadPaddingException); ++ goto cleanup; ++ } ++ jsize outLen = (*env)->GetArrayLength(env, out); ++ (*env)->SetByteArrayRegion(env, out, 0, outLen, outBytes); ++ ++cleanup: ++ if (outBytes != NULL) { ++ (*env)->ReleaseByteArrayElements(env, out, outBytes, 0); ++ } ++ if (inBytes != NULL) { ++ (*env)->ReleaseByteArrayElements(env, in, inBytes, 0); ++ } ++ if (rsa != NULL) { ++ RSA_free(rsa); ++ } ++ return resultSize; ++} ++ ++/* ++ * set rsa padding ++ */ ++static bool SetRSAPadding(JNIEnv* env, EVP_PKEY_CTX* pkeyCtx, int paddingType) { ++ if (EVP_PKEY_CTX_set_rsa_padding(pkeyCtx, paddingType) <= 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_CTX_set_rsa_padding", KAE_ThrowInvalidAlgorithmParameterException); ++ return false; ++ } ++ return true; ++} ++ ++/* ++ * set rsa mgf1 md ++ */ ++static bool SetRSAMgf1Md(JNIEnv* env, EVP_PKEY_CTX* pkeyCtx, const char* mgf1MdAlgoUTF) { ++ EVP_MD* mgf1MD = (EVP_MD*)EVP_get_digestbyname(mgf1MdAlgoUTF); ++ if (mgf1MD == NULL) { ++ KAE_ThrowFromOpenssl(env, "EVP_get_digestbyname", KAE_ThrowInvalidAlgorithmParameterException); ++ return false; ++ } ++ if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkeyCtx, mgf1MD) <= 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_CTX_set_rsa_mgf1_md", KAE_ThrowInvalidAlgorithmParameterException); ++ return false; ++ } ++ return true; ++} ++ ++/* ++ * set rsa oaep md ++ */ ++static bool SetRSAOaepMd(JNIEnv* env, EVP_PKEY_CTX* pkeyCtx, const char* oaepMdAlgoUTF) { ++ EVP_MD* oaepMD = (EVP_MD*)EVP_get_digestbyname(oaepMdAlgoUTF); ++ if (oaepMD == NULL) { ++ KAE_ThrowFromOpenssl(env, "EVP_get_digestbyname", KAE_ThrowInvalidAlgorithmParameterException); ++ return false; ++ } ++ if (EVP_PKEY_CTX_set_rsa_oaep_md(pkeyCtx, oaepMD) <= 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_CTX_set_rsa_oaep_md", KAE_ThrowInvalidAlgorithmParameterException); ++ return false; ++ } ++ return true; ++} ++ ++/* ++ * set rsa oaep label ++ */ ++static bool SetRSAOaepLabel(JNIEnv* env, EVP_PKEY_CTX* pkeyCtx, jbyte* labelBytes, jsize labelSize) { ++ if (EVP_PKEY_CTX_set0_rsa_oaep_label(pkeyCtx, labelBytes, labelSize) <= 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_CTX_set0_rsa_oaep_label", KAE_ThrowInvalidAlgorithmParameterException); ++ return false; ++ } ++ return true; ++} ++ ++/* ++ * release rsa oaep temp resource ++ */ ++static void ReleaseRSACryptOAEPResource(JNIEnv* env, EVP_PKEY_CTX* pkeyCtx, ++ jstring mgf1MdAlgo, const char* mgf1MdAlgoUTF, jstring oaepMdAlgo, const char* oaepMdAlgoUTF, ++ jbyteArray in, jbyte* inBytes, jbyteArray out, jbyte* outBytes) { ++ if (mgf1MdAlgoUTF != NULL) { ++ (*env)->ReleaseStringUTFChars(env, mgf1MdAlgo, mgf1MdAlgoUTF); ++ } ++ if (oaepMdAlgoUTF != NULL) { ++ (*env)->ReleaseStringUTFChars(env, oaepMdAlgo, oaepMdAlgoUTF); ++ } ++ if (outBytes != NULL) { ++ (*env)->ReleaseByteArrayElements(env, out, outBytes, 0); ++ } ++ if (inBytes != NULL) { ++ (*env)->ReleaseByteArrayElements(env, in, inBytes, 0); ++ } ++ EVP_PKEY_CTX_free(pkeyCtx); ++} ++ ++static int RSACryptOAEPPadding(JNIEnv* env, jlong keyAddress, jint inLen, jbyteArray in, jbyteArray out, ++ jint paddingType, jstring oaepMdAlgo, jstring mgf1MdAlgo, jbyteArray label, ++ EvpPkeyCryptInitOperation cryptInitOperation, char* cryptInitName, ++ EvpPkeyCryptOperation cryptOperation, char* cryptName) { ++ EVP_PKEY_CTX* pkeyCtx = NULL; ++ const char* mgf1MdAlgoUTF = NULL; ++ const char* oaepMdAlgoUTF = NULL; ++ jbyte* labelBytes = NULL; ++ jbyte* outBytes = NULL; ++ 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); ++ ++ ++ EVP_PKEY* pkey = (EVP_PKEY*) keyAddress; ++ ++ // new ctx ++ // rsa encrypt/decrypt init ++ if ((pkeyCtx = EVP_PKEY_CTX_new(pkey, kaeEngine)) == NULL || cryptInitOperation(pkeyCtx) <= 0) { ++ KAE_ThrowFromOpenssl(env, pkeyCtx == NULL ? "EVP_PKEY_CTX_new" : cryptInitName, KAE_ThrowInvalidKeyException); ++ goto cleanup; ++ } ++ ++ if ((mgf1MdAlgoUTF = (*env)->GetStringUTFChars(env, mgf1MdAlgo, 0)) == NULL || ++ (oaepMdAlgoUTF = (*env)->GetStringUTFChars(env, oaepMdAlgo, 0)) == NULL) { ++ KAE_ThrowOOMException(env, "GetStringUTFChars failed"); ++ goto cleanup; ++ } ++ ++ /* ++ * set padding type ++ * set rsa mgf1 md ++ * set rsa oaep md ++ */ ++ if (!SetRSAPadding(env, pkeyCtx, paddingType) || !SetRSAMgf1Md(env, pkeyCtx, mgf1MdAlgoUTF) || ++ !SetRSAOaepMd(env, pkeyCtx, oaepMdAlgoUTF)) { ++ goto cleanup; ++ } ++ ++ // set rsa oaep label ++ jsize labelSize = (*env)->GetArrayLength(env, label); ++ if (labelSize > 0) { ++ // EVP_PKEY_CTX_free will free the labelBytes, so we can not free labelBytes when cleanup. ++ // Only SetRSAOaepLabel failed , free labelBytes. ++ if ((labelBytes = malloc(labelSize)) == NULL) { ++ KAE_ThrowNullPointerException(env, "malloc failed"); ++ goto cleanup; ++ } ++ (*env)->GetByteArrayRegion(env, label, 0, labelSize, labelBytes); ++ if (!SetRSAOaepLabel(env, pkeyCtx, labelBytes, labelSize)) { ++ free(labelBytes); ++ goto cleanup; ++ } ++ } ++ ++ // do encrypt/decrypt ++ outLen = (size_t)(*env)->GetArrayLength(env, out); ++ if ((outBytes = (*env)->GetByteArrayElements(env, out, NULL)) == NULL || ++ (inBytes = (*env)->GetByteArrayElements(env, in, NULL)) == NULL) { ++ KAE_ThrowNullPointerException(env, "GetByteArrayElements failed"); ++ goto cleanup; ++ } ++ if (cryptOperation(pkeyCtx, (unsigned char*)outBytes, &outLen, (unsigned char*)inBytes, inLen) <= 0) { ++ KAE_ThrowFromOpenssl(env, cryptName, KAE_ThrowBadPaddingException); ++ goto cleanup; ++ } ++ (*env)->SetByteArrayRegion(env, out, 0, outLen, outBytes); ++ ++cleanup: ++ ReleaseRSACryptOAEPResource(env, pkeyCtx, mgf1MdAlgo, mgf1MdAlgoUTF, oaepMdAlgo, oaepMdAlgoUTF, ++ in, inBytes, out, outBytes); ++ return outLen; ++} ++ ++/* ++ * Release rsa param n,e,d,p,q,dmp1,dmq1,iqmp ++ */ ++void ReleaseRSAParams(BIGNUM* bnN, BIGNUM* bnE, BIGNUM* bnD, BIGNUM* bnP, BIGNUM* bnQ, ++ BIGNUM* bnDMP1, BIGNUM* bnDMQ1, BIGNUM* bnIQMP) { ++ KAE_ReleaseBigNumFromByteArray(bnN); ++ KAE_ReleaseBigNumFromByteArray(bnE); ++ KAE_ReleaseBigNumFromByteArray(bnD); ++ KAE_ReleaseBigNumFromByteArray(bnP); ++ KAE_ReleaseBigNumFromByteArray(bnQ); ++ KAE_ReleaseBigNumFromByteArray(bnDMP1); ++ KAE_ReleaseBigNumFromByteArray(bnDMQ1); ++ KAE_ReleaseBigNumFromByteArray(bnIQMP); ++} ++ ++/* ++ * Create rsa private crt key ++ * Class: org_openeuler_security_openssl_KAERSACipher ++ * Method: nativeCreateRSAPrivateCrtKey ++ * Signature: ([B[B[B[B[B[B[B[B)J ++ */ ++JNIEXPORT jlong JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeCreateRSAPrivateCrtKey(JNIEnv* env, ++ jclass cls, jbyteArray n, jbyteArray e, jbyteArray d, jbyteArray p, jbyteArray q, ++ jbyteArray dmp1, jbyteArray dmq1, jbyteArray iqmp) { ++ BIGNUM* bnN = NULL; ++ BIGNUM* bnE = NULL; ++ BIGNUM* bnD = NULL; ++ BIGNUM* bnP = NULL; ++ BIGNUM* bnQ = NULL; ++ BIGNUM* bnDMP1 = NULL; ++ BIGNUM* bnDMQ1 = NULL; ++ BIGNUM* bnIQMP = NULL; ++ RSA* rsa = NULL; ++ EVP_PKEY* pkey = NULL; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); ++ KAE_TRACE("KAERSACipher_nativeCreateRSAPrivateCrtKey: kaeEngine => %p", kaeEngine); ++ ++ // convert to big num ++ if ((bnN = KAE_GetBigNumFromByteArray(env, n)) == NULL || ++ (bnE = KAE_GetBigNumFromByteArray(env, e)) == NULL || ++ (bnD = KAE_GetBigNumFromByteArray(env, d)) == NULL || ++ (bnP = KAE_GetBigNumFromByteArray(env, p)) == NULL || ++ (bnQ = KAE_GetBigNumFromByteArray(env, q)) == NULL || ++ (bnDMP1 = KAE_GetBigNumFromByteArray(env, dmp1)) == NULL || ++ (bnDMQ1 = KAE_GetBigNumFromByteArray(env, dmq1)) == NULL || ++ (bnIQMP = KAE_GetBigNumFromByteArray(env, iqmp)) == NULL) { ++ goto cleanup; ++ } ++ ++ // new pkey ++ pkey = EVP_PKEY_new(); ++ if (pkey == NULL) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_new", KAE_ThrowRuntimeException); ++ goto cleanup; ++ } ++ ++ // new rsa ++ rsa = RSA_new_method(kaeEngine); ++ if (rsa == NULL) { ++ KAE_ThrowFromOpenssl(env, "RSA_new_method", KAE_ThrowRuntimeException); ++ goto cleanup; ++ } ++ ++ // set rsa private crt key params n,e,d,p,q,dmp1,dmp1,iqmp ++ if (RSA_set0_key(rsa, bnN, bnE, bnD) <= 0 || ++ RSA_set0_factors(rsa, bnP, bnQ) <= 0 || ++ RSA_set0_crt_params(rsa, bnDMP1, bnDMQ1, bnIQMP) <= 0) { ++ KAE_ThrowFromOpenssl(env, "RSA set param", KAE_ThrowRuntimeException); ++ goto cleanup; ++ } ++ ++ // assign rsa to pkey ++ int result = EVP_PKEY_assign_RSA(pkey, rsa); ++ if (result <= 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_assign_RSA", KAE_ThrowRuntimeException); ++ goto cleanup; ++ } ++ return (jlong)pkey; ++cleanup: ++ ReleaseRSAParams(bnN, bnE, bnD, bnP, bnQ, bnDMP1, bnDMQ1, bnIQMP); ++ RSA_free(rsa); ++ EVP_PKEY_free(pkey); ++ return 0; ++} ++ ++/* ++ * Create rsa public key ++ * Class: org_openeuler_security_openssl_KAERSACipher ++ * Method: nativeCreateRSAPublicKey ++ * Signature: ([B[B)J ++ */ ++JNIEXPORT jlong JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeCreateRSAPublicKey( ++ JNIEnv* env, jclass cls, jbyteArray n, jbyteArray e) { ++ BIGNUM* bnN = NULL; ++ BIGNUM* bnE = NULL; ++ RSA* rsa = NULL; ++ EVP_PKEY* pkey = NULL; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); ++ KAE_TRACE("KAERSACipher_nativeCreateRSAPublicKey: kaeEngine => %p", kaeEngine); ++ ++ // get public key param n ++ bnN = KAE_GetBigNumFromByteArray(env, n); ++ if (bnN == NULL) { ++ goto cleanup; ++ } ++ ++ // get public key param e ++ bnE = KAE_GetBigNumFromByteArray(env, e); ++ if (bnE == NULL) { ++ goto cleanup; ++ } ++ ++ // new rsa ++ rsa = RSA_new_method(kaeEngine); ++ if (rsa == NULL) { ++ KAE_ThrowFromOpenssl(env, "RSA_new_method", KAE_ThrowRuntimeException); ++ goto cleanup; ++ } ++ ++ // new EVP_PKEY ++ pkey = EVP_PKEY_new(); ++ if (pkey == NULL) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_new", KAE_ThrowRuntimeException); ++ goto cleanup; ++ } ++ ++ // set rsa public key params n and e ++ if (RSA_set0_key(rsa, bnN, bnE, NULL) <= 0) { ++ KAE_ThrowFromOpenssl(env, "RSA_set0_key", KAE_ThrowRuntimeException); ++ goto cleanup; ++ } ++ ++ // assign rsa to pkey ++ int result = EVP_PKEY_assign_RSA(pkey, rsa); ++ if (result <= 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_assign_RSA", KAE_ThrowRuntimeException); ++ goto cleanup; ++ } ++ return (jlong)pkey; ++cleanup: ++ KAE_ReleaseBigNumFromByteArray(bnN); ++ KAE_ReleaseBigNumFromByteArray(bnE); ++ RSA_free(rsa); ++ EVP_PKEY_free(pkey); ++ return 0; ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAERSACipher ++ * Method: nativeRSAPrivateEncrypt ++ * Signature: (JI[B[BI)I ++ */ ++JNIEXPORT jint JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeRSAPrivateEncrypt(JNIEnv* env, ++ jclass cls, jlong keyAddress, jint inLen, jbyteArray in, jbyteArray out, jint paddingType) { ++ return RSACryptNotOAEPPadding(env, keyAddress, inLen, in, out, paddingType, RSA_private_encrypt, ++ "RSA_private_encrypt"); ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAERSACipher ++ * Method: nativeRSAPrivateDecrypt ++ * Signature: (JI[B[BI)I ++ */ ++JNIEXPORT jint JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeRSAPrivateDecrypt(JNIEnv* env, ++ jclass cls, jlong keyAddress, jint inLen, jbyteArray in, jbyteArray out, jint paddingType) { ++ return RSACryptNotOAEPPadding(env, keyAddress, inLen, in, out, paddingType, RSA_private_decrypt, ++ "RSA_private_decrypt"); ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAERSACipher ++ * Method: nativeRSAPublicEncrypt ++ * Signature: (JI[B[BI)I ++ */ ++JNIEXPORT jint JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeRSAPublicEncrypt(JNIEnv* env, ++ jclass cls, jlong keyAddress, jint inLen, jbyteArray in, jbyteArray out, jint paddingType) { ++ return RSACryptNotOAEPPadding(env, keyAddress, inLen, in, out, paddingType, RSA_public_encrypt, ++ "RSA_public_encrypt"); ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAERSACipher ++ * Method: nativeRSAPublicDecrypt ++ * Signature: (JI[B[BI)I ++ */ ++JNIEXPORT jint JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeRSAPublicDecrypt(JNIEnv* env, ++ jclass cls, jlong keyAddress, jint inLen, jbyteArray in, jbyteArray out, jint paddingType) { ++ return RSACryptNotOAEPPadding(env, keyAddress, inLen, in, out, paddingType, RSA_public_decrypt, ++ "RSA_public_decrypt"); ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAERSACipher ++ * Method: nativeRSAEncryptOAEPPading ++ * Signature: (JI[B[BI[B[B[B)I ++ */ ++JNIEXPORT jint JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeRSAEncryptOAEPPadding(JNIEnv* env, ++ jclass cls, jlong keyAddress, jint inLen, jbyteArray in, jbyteArray out, ++ jint paddingType, jstring oaepMdAlgo, jstring mgf1MdAlgo, jbyteArray label) { ++ return RSACryptOAEPPadding(env, keyAddress, inLen, in, out, paddingType, oaepMdAlgo, mgf1MdAlgo, label, ++ EVP_PKEY_encrypt_init, "EVP_PKEY_encrypt_init", ++ EVP_PKEY_encrypt, "EVP_PKEY_encrypt"); ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAERSACipher ++ * Method: nativeRSADecryptOAEPPadding ++ * Signature: (JI[B[BILjava/lang/String;Ljava/lang/String;[B)I ++ */ ++JNIEXPORT jint JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeRSADecryptOAEPPadding(JNIEnv* env, ++ jclass cls, jlong keyAddress, jint inLen, jbyteArray in, jbyteArray out, jint paddingType, ++ jstring oaepMdAlgo, jstring mgf1MdAlgo, jbyteArray label) { ++ return RSACryptOAEPPadding(env, keyAddress, inLen, in, out, paddingType, oaepMdAlgo, mgf1MdAlgo, label, ++ EVP_PKEY_decrypt_init, "EVP_PKEY_decrypt_init", ++ EVP_PKEY_decrypt, "EVP_PKEY_decrypt"); ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAERSACipher ++ * Method: nativeFreeKey ++ * Signature: (J)V ++ */ ++JNIEXPORT void JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeFreeKey(JNIEnv* env, ++ jclass cls, jlong keyAddress) { ++ EVP_PKEY* pkey = (EVP_PKEY*) keyAddress; ++ if (pkey != NULL) { ++ EVP_PKEY_free(pkey); ++ } ++} +\ No newline at end of file +diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_digest.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_digest.c +new file mode 100644 +index 000000000..82bf477da +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_digest.c +@@ -0,0 +1,232 @@ ++/* ++ * Copyright (c) 2023, 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 ++#include ++#include ++#include "kae_exception.h" ++#include "kae_log.h" ++#include "kae_util.h" ++#include "org_openeuler_security_openssl_KAEDigest.h" ++ ++#define DIGEST_STACK_SIZE 1024 ++#define DIGEST_CHUNK_SIZE 64*1024 ++#define DIGEST_LENGTH_THRESHOLD 48 ++ ++/* ++ * Class: org_openeuler_security_openssl_KAEDigest ++ * Method: nativeInit ++ * Signature: (Ljava/lang/String;)J ++ */ ++JNIEXPORT jlong JNICALL ++Java_org_openeuler_security_openssl_KAEDigest_nativeInit(JNIEnv *env, jclass cls, jstring algorithmName) ++{ ++ EVP_MD_CTX* ctx = NULL; ++ ENGINE* kaeEngine = NULL; ++ ++ if (algorithmName == NULL) { ++ KAE_ThrowNullPointerException(env, "algorithm is null"); ++ return 0; ++ } ++ ++ // EVP_get_digestbyname ++ const char* algo_utf = (*env)->GetStringUTFChars(env, algorithmName, 0); ++ 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) { ++ KAE_TRACE("%s not supported", algo_utf); ++ return 0; ++ } ++ KAE_TRACE("KAEDigest_nativeInit: create md => %p", md); ++ ++ ctx = EVP_MD_CTX_create(); ++ if (ctx == NULL) { ++ KAE_ThrowOOMException(env, "create EVP_MD_CTX fail"); ++ return 0; ++ } ++ KAE_TRACE("KAEDigest_nativeInit: create ctx => %p", ctx); ++ ++ // EVP_DigestInit_ex ++ int result_code = EVP_DigestInit_ex(ctx, md, kaeEngine); ++ if (result_code == 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_DigestInit_ex failed", KAE_ThrowRuntimeException); ++ goto cleanup; ++ } ++ KAE_TRACE("KAEDigest_nativeInit EVP_DigestInit_ex(ctx = %p, md = %p) success", ctx, md); ++ ++ KAE_TRACE("KAEDigest_nativeInit: finished"); ++ return (jlong) ctx; ++ ++cleanup: ++ EVP_MD_CTX_destroy(ctx); ++ return 0; ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAEDigest ++ * Method: nativeUpdate ++ * Signature: (Ljava/lang/String;J[BII)I ++ */ ++JNIEXPORT void JNICALL ++Java_org_openeuler_security_openssl_KAEDigest_nativeUpdate(JNIEnv *env, jclass cls, jlong ctxAddress, ++ jbyteArray input, jint offset, jint inLen) ++{ ++ EVP_MD_CTX* ctx = (EVP_MD_CTX*) ctxAddress; ++ KAE_TRACE("KAEDigest_nativeUpdate(ctx = %p, input = %p, offset = %d, inLen = %d", ctx, input, offset, inLen); ++ if (ctx == NULL) { ++ return; ++ } ++ ++ jint in_offset = offset; ++ jint in_size = inLen; ++ int result_code = 0; ++ if (in_size <= DIGEST_STACK_SIZE) { // allocation on the stack ++ jbyte buffer[DIGEST_STACK_SIZE]; ++ (*env)->GetByteArrayRegion(env, input, offset, inLen, buffer); ++ result_code = EVP_DigestUpdate(ctx, buffer, inLen); ++ } else { // data chunk ++ jint remaining = in_size; ++ jint buf_size = (remaining >= DIGEST_CHUNK_SIZE) ? DIGEST_CHUNK_SIZE : remaining; ++ jbyte* buffer = malloc(buf_size); ++ if (buffer == NULL) { ++ KAE_ThrowOOMException(env, "malloc error"); ++ return; ++ } ++ while (remaining > 0) { ++ jint chunk_size = (remaining >= buf_size) ? buf_size : remaining; ++ (*env)->GetByteArrayRegion(env, input, in_offset, chunk_size, buffer); ++ result_code = EVP_DigestUpdate(ctx, buffer, chunk_size); ++ if (!result_code) { ++ break; ++ } ++ in_offset += chunk_size; ++ remaining -= chunk_size; ++ } ++ free(buffer); ++ } ++ if (!result_code) { ++ KAE_ThrowFromOpenssl(env, "EVP_DigestUpdate failed", KAE_ThrowRuntimeException); ++ return; ++ } ++ KAE_TRACE("KAEDigest_nativeUpdate EVP_DigestUpdate success"); ++ KAE_TRACE("KAEDigest_nativeUpdate: finished"); ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAEDigest ++ * Method: nativeDigest ++ * Signature: (Ljava/lang/String;J[BII)I ++ */ ++JNIEXPORT jint JNICALL ++Java_org_openeuler_security_openssl_KAEDigest_nativeDigest(JNIEnv *env, jclass cls, ++ jlong ctxAddress, jbyteArray output, jint offset, jint len) ++{ ++ EVP_MD_CTX* ctx = (EVP_MD_CTX*) ctxAddress; ++ KAE_TRACE("KAEDigest_nativeDigest(ctx = %p, output = %p, offset = %d, len = %d", ctx, output, offset, len); ++ unsigned char* md = NULL; ++ unsigned int bytesWritten = 0; ++ ++ if (ctx == NULL) { ++ return 0; ++ } ++ ++ if (len <= 0 || len > DIGEST_LENGTH_THRESHOLD) { ++ KAE_ThrowRuntimeException(env, "len out of length"); ++ return 0; ++ } ++ md = malloc(len); ++ if (md == NULL) { ++ KAE_ThrowOOMException(env, "malloc error"); ++ return 0; ++ } ++ ++ // EVP_DigestFinal_ex ++ int result_code = EVP_DigestFinal_ex(ctx, md, &bytesWritten); ++ if (result_code == 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_DigestFinal_ex failed", KAE_ThrowRuntimeException); ++ goto cleanup; ++ } ++ KAE_TRACE("KAEDigest_nativeFinal EVP_DigestFinal_ex success, bytesWritten = %d", bytesWritten); ++ ++ (*env)->SetByteArrayRegion(env, output, offset, bytesWritten, (jbyte*) md); ++ ++ KAE_TRACE("KAEDigest_nativeFinal: finished"); ++ ++cleanup: ++ free(md); ++ return bytesWritten; ++} ++ ++/* ++* Class: org_openeuler_security_openssl_KAEDigest ++* Method: nativeClone ++* Signature: (J)J ++*/ ++JNIEXPORT jlong JNICALL ++Java_org_openeuler_security_openssl_KAEDigest_nativeClone(JNIEnv *env, jclass cls, jlong ctxAddress) ++{ ++ EVP_MD_CTX* ctx = (EVP_MD_CTX*) ctxAddress; ++ KAE_TRACE("KAEDigest_nativeClone: ctx = %p", ctx); ++ if (ctx == NULL) { ++ return 0; ++ } ++ ++ EVP_MD_CTX* ctxCopy = EVP_MD_CTX_create(); ++ if (ctxCopy == NULL) { ++ KAE_ThrowOOMException(env, "create EVP_MD_CTX fail"); ++ return 0; ++ } ++ KAE_TRACE("KAEDigest_nativeClone: create ctxCopy => %p", ctxCopy); ++ ++ int result_code = EVP_MD_CTX_copy_ex(ctxCopy, ctx); ++ if (result_code == 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_MD_CTX_copy_ex failed", KAE_ThrowRuntimeException); ++ goto cleanup; ++ } ++ KAE_TRACE("KAEDigest_nativeClone EVP_MD_CTX_copy_ex(ctxCopy = %p, ctx = %p) success", ctxCopy, ctx); ++ KAE_TRACE("KAEDigest_nativeClone: finished"); ++ return (jlong) ctxCopy; ++ ++cleanup: ++ EVP_MD_CTX_destroy(ctxCopy); ++ return 0; ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAEDigest ++ * Method: nativeFree ++ * Signature: (J)V ++ */ ++JNIEXPORT void JNICALL ++Java_org_openeuler_security_openssl_KAEDigest_nativeFree(JNIEnv *env, jclass cls, jlong ctxAddress) ++{ ++ EVP_MD_CTX* ctx = (EVP_MD_CTX*) ctxAddress; ++ KAE_TRACE("KAEDigest_nativeFree(ctx = %p)", ctx); ++ if (ctx != NULL) { ++ EVP_MD_CTX_destroy(ctx); ++ } ++ ++ KAE_TRACE("KAEDigest_nativeFree: finished"); ++} +\ No newline at end of file +diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_exception.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_exception.c +new file mode 100644 +index 000000000..c0579ebf4 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_exception.c +@@ -0,0 +1,135 @@ ++/* ++ * Copyright (c) 2023, 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 ++#include ++#include ++#include "kae_log.h" ++#include "kae_exception.h" ++#include "openssl_ad.h" ++ ++void KAE_ThrowByName(JNIEnv* env, const char* name, const char* msg) { ++ jclass cls = (*env)->FindClass(env, name); ++ if (cls != 0) { ++ (*env)->ThrowNew(env, cls, msg); ++ (*env)->DeleteLocalRef(env, cls); ++ } ++} ++ ++void KAE_ThrowOOMException(JNIEnv* env, const char* msg) { ++ KAE_ThrowByName(env, "java/lang/OutOfMemoryError", msg); ++} ++ ++void KAE_ThrowNullPointerException(JNIEnv* env, const char* msg) { ++ KAE_ThrowByName(env, "java/lang/NullPointerException", msg); ++} ++ ++void KAE_ThrowArrayIndexOutOfBoundsException(JNIEnv* env, const char* msg) { ++ KAE_ThrowByName(env, "java/lang/ArrayIndexOutOfBoundsException", msg); ++} ++ ++void KAE_ThrowEvpException(JNIEnv* env, int reason, const char* msg, void (* defaultException)(JNIEnv*, const char*)) { ++ switch (reason) { ++ case EVP_R_UNSUPPORTED_ALGORITHM: ++ KAE_ThrowByName(env, "java/security/NoSuchAlgorithmException", msg); ++ break; ++ case EVP_R_MISSING_PARAMETERS: ++ KAE_ThrowByName(env, "java/security/InvalidKeyException", msg); ++ break; ++ case EVP_R_BAD_DECRYPT: ++ case EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH: ++ case EVP_F_EVP_PKEY_DECRYPT: ++ case EVP_R_PUBLIC_KEY_NOT_RSA: ++ case EVP_R_CTRL_NOT_IMPLEMENTED: ++ KAE_ThrowByName(env, "javax/crypto/BadPaddingException", msg); ++ break; ++ default: ++ defaultException(env, msg); ++ break; ++ } ++} ++ ++void KAE_ThrowRuntimeException(JNIEnv* env, const char* msg) { ++ KAE_ThrowByName(env, "java/lang/RuntimeException", msg); ++} ++ ++void KAE_ThrowBadPaddingException(JNIEnv* env, const char* msg) { ++ KAE_ThrowByName(env, "javax/crypto/BadPaddingException", msg); ++} ++ ++void KAE_ThrowInvalidKeyException(JNIEnv* env, const char* msg) { ++ KAE_ThrowByName(env, "java/security/InvalidKeyException", msg); ++} ++ ++void KAE_ThrowInvalidAlgorithmParameterException(JNIEnv* env, const char* msg) { ++ KAE_ThrowByName(env, "java/security/InvalidAlgorithmParameterException", msg); ++} ++ ++void KAE_ThrowFromOpenssl(JNIEnv* env, const char* msg, void (* defaultException)(JNIEnv*, const char*)) { ++ const char* file = NULL; ++ const char* data = NULL; ++ int line = 0; ++ int flags = 0; ++ unsigned long err; ++ static const int ESTRING_SIZE = 256; ++ ++ err = ERR_get_error_line_data(&file, &line, &data, &flags); ++ if (err == 0) { ++ defaultException(env, msg); ++ return; ++ } ++ ++ if (!(*env)->ExceptionCheck(env)) { ++ char estring[ESTRING_SIZE]; ++ ERR_error_string_n(err, estring, ESTRING_SIZE); ++ int lib = ERR_GET_LIB(err); ++ int func = ERR_GET_FUNC(err); ++ int reason = ERR_GET_REASON(err); ++ KAE_TRACE("OpenSSL error in %s: err=%lx, lib=%x, func=%x, reason=%x, file=%s, line=%d, estring=%s, data=%s", msg, err, ++ lib, func, reason, file, line, estring, (flags & ERR_TXT_STRING) ? data : "(no data)"); ++ // Ignore exceptions in RSA_verify_PKCS1_PSS_mgf1 function ++ if (lib == ERR_LIB_RSA && func == RSA_F_RSA_VERIFY_PKCS1_PSS_MGF1) { ++ return; ++ } ++ ++ if (lib == ERR_LIB_EVP || lib == ERR_LIB_RSA) { ++ KAE_ThrowEvpException(env, reason, estring, defaultException); ++ } else { ++ defaultException(env, estring); ++ } ++ } ++ ++ ERR_clear_error(); ++} ++ ++void KAE_ThrowAEADBadTagException(JNIEnv *env, const char *msg) { ++ KAE_ThrowByName(env, "javax/crypto/AEADBadTagException", msg); ++} ++ ++void KAE_ThrowSignatureException(JNIEnv* env, const char* msg) { ++ KAE_ThrowByName(env, "java/security/SignatureException", msg); ++} ++ ++void KAE_ThrowClassNotFoundException(JNIEnv* env, const char* msg) { ++ KAE_ThrowByName(env, "java/lang/ClassNotFoundException", msg); ++} +diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_exception.h b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_exception.h +new file mode 100644 +index 000000000..f33f993e4 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_exception.h +@@ -0,0 +1,57 @@ ++/* ++ * Copyright (c) 2023, 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 KAE_EXCEPTION_H ++#define KAE_EXCEPTION_H ++ ++#include ++ ++/* Throw a Java exception by name */ ++void KAE_ThrowByName(JNIEnv* env, const char* name, const char* msg); ++ ++void KAE_ThrowOOMException(JNIEnv* env, const char* msg); ++ ++void KAE_ThrowNullPointerException(JNIEnv* env, const char* msg); ++ ++void KAE_ThrowArrayIndexOutOfBoundsException(JNIEnv* env, const char* msg); ++ ++void KAE_ThrowFromOpenssl(JNIEnv* env, const char* msg, void (* defaultException)(JNIEnv*, const char*)); ++ ++void KAE_ThrowEvpException(JNIEnv* env, int reason, const char* msg, void (* defaultException)(JNIEnv*, const char*)); ++ ++void KAE_ThrowRuntimeException(JNIEnv* env, const char* msg); ++ ++void KAE_ThrowBadPaddingException(JNIEnv* env, const char* msg); ++ ++/* Throw InvalidKeyException */ ++void KAE_ThrowInvalidKeyException(JNIEnv* env, const char* msg); ++ ++/* Throw AlgorithmParameterException */ ++void KAE_ThrowInvalidAlgorithmParameterException(JNIEnv* env, const char* msg); ++ ++void KAE_ThrowAEADBadTagException(JNIEnv* env, const char* msg); ++ ++void KAE_ThrowSignatureException(JNIEnv* env, const char* msg); ++ ++void KAE_ThrowClassNotFoundException(JNIEnv* env, const char* msg); ++#endif +diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_hmac.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_hmac.c +new file mode 100644 +index 000000000..ad7b86ecd +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_hmac.c +@@ -0,0 +1,208 @@ ++/* ++ * Copyright (c) 2023, 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 ++#include ++#include ++#include "kae_exception.h" ++#include "kae_log.h" ++#include "kae_util.h" ++ ++static const EVP_MD* EVPGetDigestByName(JNIEnv* env, const char* algo) ++{ ++ static const EVP_MD* md5 = NULL; ++ static const EVP_MD* sha1 = NULL; ++ static const EVP_MD* sha224 = NULL; ++ static const EVP_MD* sha256 = NULL; ++ static const EVP_MD* sha384 = NULL; ++ static const EVP_MD* sha512 = NULL; ++ ++ if (strcasecmp(algo, "md5") == 0) { ++ return md5 == NULL ? md5 = EVP_get_digestbyname(algo) : md5; ++ } else if (strcasecmp(algo, "sha1") == 0) { ++ return sha1 == NULL ? sha1 = EVP_get_digestbyname(algo) : sha1; ++ } else if (strcasecmp(algo, "sha224") == 0) { ++ return sha224 == NULL ? sha224 = EVP_get_digestbyname(algo) : sha224; ++ } else if (strcasecmp(algo, "sha256") == 0) { ++ return sha256 == NULL ? sha256 = EVP_get_digestbyname(algo) : sha256; ++ } else if (strcasecmp(algo, "sha384") == 0) { ++ return sha384 == NULL ? sha384 = EVP_get_digestbyname(algo) : sha384; ++ } else if (strcasecmp(algo, "sha512") == 0) { ++ return sha512 == NULL ? sha512 = EVP_get_digestbyname(algo) : sha512; ++ } else { ++ KAE_ThrowRuntimeException(env, "EVPGetDigestByName error"); ++ return 0; ++ } ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAEHMac ++ * Method: nativeInit ++ * Signature: ([BILjava/lang/String;)J ++ */ ++JNIEXPORT jlong JNICALL Java_org_openeuler_security_openssl_KAEHMac_nativeInit ++ (JNIEnv* env, jclass cls, jbyteArray key, jint key_len, jstring algoStr) { ++ if (key == NULL || algoStr == NULL) { ++ KAE_ThrowNullPointerException(env, "param key or algoStr is null"); ++ return 0; ++ } ++ if (key_len <= 0) { ++ KAE_ThrowArrayIndexOutOfBoundsException(env, "key"); ++ return 0; ++ } ++ 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); ++ ++ kaeEngine = GetHmacEngineByAlgorithmName(algo); ++ KAE_TRACE("KAEHMac_nativeInit: kaeEngine => %p", kaeEngine); ++ ++ (*env)->ReleaseStringUTFChars(env, algoStr, algo); ++ if (md == NULL) { ++ KAE_ThrowRuntimeException(env, "algorithm unsupport"); ++ return 0; ++ } ++ ++ // get secret-key ++ key_buffer = malloc(key_len); ++ if (key_buffer == NULL) { ++ KAE_ThrowOOMException(env, "malloc failed"); ++ return 0; ++ } ++ (*env)->GetByteArrayRegion(env, key, 0, key_len, key_buffer); ++ ++ // create a hmac context ++ ctx = HMAC_CTX_new(); ++ if (ctx == NULL) { ++ KAE_ThrowRuntimeException(env, "Hmac_CTX_new invoked failed"); ++ goto cleanup; ++ } ++ ++ // init hmac context with sc_key and evp_md ++ 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; ++ } ++ free(key_buffer); ++ return (jlong) ctx; ++ ++cleanup: ++ free(key_buffer); ++ HMAC_CTX_free(ctx); ++ return 0; ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAEHMac ++ * Method: nativeUpdate ++ * Signature: (J[BII)V ++ */ ++JNIEXPORT void JNICALL Java_org_openeuler_security_openssl_KAEHMac_nativeUpdate ++ (JNIEnv* env, jclass cls, jlong hmac_ctx, jbyteArray input, jint in_offset, jint in_len) { ++ KAE_TRACE("KAEHMac_nativeUpdate(ctx = %p, input = %p, offset = %d, inLen = %d)", (void*) hmac_ctx, input, in_offset, in_len); ++ HMAC_CTX* ctx = (HMAC_CTX*) hmac_ctx; ++ if (ctx == NULL || input == NULL) { ++ KAE_ThrowNullPointerException(env, "param ctx or input is null"); ++ return; ++ } ++ int input_size = (*env)->GetArrayLength(env, input); ++ if ((in_offset < 0) || (in_len < 0) || (in_offset > input_size - in_len)) { ++ KAE_ThrowArrayIndexOutOfBoundsException(env, "input"); ++ return; ++ } ++ // do nothing while in_len is 0 ++ if (in_len == 0) { ++ return; ++ } ++ ++ jbyte* buffer = malloc(in_len); ++ if (buffer == NULL) { ++ KAE_ThrowOOMException(env, "malloc failed"); ++ return; ++ } ++ (*env)->GetByteArrayRegion(env, input, in_offset, in_len, buffer); ++ if (!HMAC_Update(ctx, (unsigned char*) buffer, in_len)) { ++ KAE_ThrowRuntimeException(env, "Hmac_Update invoked failed"); ++ } ++ free(buffer); ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAEHMac ++ * Method: nativeFinal ++ * Signature: (J[BII)I ++ */ ++JNIEXPORT jint JNICALL Java_org_openeuler_security_openssl_KAEHMac_nativeFinal ++ (JNIEnv* env, jclass cls, jlong hmac_ctx, jbyteArray output, jint out_offset, jint in_len) { ++ HMAC_CTX* ctx = (HMAC_CTX*) hmac_ctx; ++ if (ctx == NULL || output == NULL) { ++ KAE_ThrowNullPointerException(env, "param ctx or input is null"); ++ return 0; ++ } ++ int output_size = (*env)->GetArrayLength(env, output); ++ if ((out_offset < 0) || (in_len < 0) || (out_offset > output_size - in_len)) { ++ KAE_ThrowArrayIndexOutOfBoundsException(env, "output"); ++ return 0; ++ } ++ ++ jbyte* temp_result = NULL; ++ ++ temp_result = malloc(in_len); ++ if (temp_result == NULL) { ++ KAE_ThrowOOMException(env, "malloc failed"); ++ return 0; ++ } ++ // do final ++ unsigned int bytesWritten = 0; ++ int result_code = HMAC_Final(ctx, (unsigned char*) temp_result, &bytesWritten); ++ if (result_code == 0) { ++ KAE_ThrowRuntimeException(env, "Hmac_Final invoked failed"); ++ goto cleanup; ++ } ++ ++ // write back to output_array ++ (*env)->SetByteArrayRegion(env, output, out_offset, bytesWritten, (jbyte*) temp_result); ++ KAE_TRACE("KAEHMac_nativeFinal success, output_offset = %d, bytesWritten = %u", out_offset, bytesWritten); ++ ++cleanup: ++ free(temp_result); ++ return bytesWritten; ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAEHMac ++ * Method: nativeFree ++ * Signature: (J)V ++ */ ++JNIEXPORT void JNICALL Java_org_openeuler_security_openssl_KAEHMac_nativeFree ++ (JNIEnv* env, jclass cls, jlong hmac_ctx) { ++ HMAC_CTX* ctx = (HMAC_CTX*) hmac_ctx; ++ if (ctx != NULL) { ++ HMAC_CTX_free(ctx); ++ } ++} +diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keyagreement_dh.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keyagreement_dh.c +new file mode 100644 +index 000000000..e9dfa4094 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keyagreement_dh.c +@@ -0,0 +1,141 @@ ++/* ++ * Copyright (c) 2023, 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 ++#include ++#include ++#include ++#include ++#include ++#include "kae_util.h" ++#include "kae_exception.h" ++#include "kae_log.h" ++#include "org_openeuler_security_openssl_KAEDHKeyAgreement.h" ++ ++ ++/* ++ * Class: org_openeuler_security_openssl_KAEDHKeyAgreement ++ * Method: nativeComputeKey ++ */ ++JNIEXPORT jbyteArray JNICALL Java_org_openeuler_security_openssl_KAEDHKeyAgreement_nativeComputeKey(JNIEnv* env, ++ jobject obj, jbyteArray y, jbyteArray x, jbyteArray p, jbyteArray g, jint pSize) { ++ ++ KAE_TRACE("Java_org_openeuler_security_openssl_KAEDHKeyAgreement_nativeComputeKey start."); ++ ++ DH* dh = NULL; ++ BIGNUM* y_bn = NULL; ++ BIGNUM* x_bn = NULL; ++ BIGNUM* p_bn = NULL; ++ BIGNUM* g_bn = NULL; ++ BIGNUM* computeKeyRetBn = NULL; ++ int computekeyLength = 0; ++ unsigned char* secret = NULL; ++ jbyteArray retByteArray = NULL; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(DH_INDEX); ++ KAE_TRACE("KAEDHKeyAgreement_nativeComputeKey: kaeEngine => %p", kaeEngine); ++ ++ // bits to Bytes ++ int pSizeInByte = (pSize +7) >> 3; ++ ++ if ((secret = (unsigned char*)malloc(pSizeInByte)) == NULL) { ++ KAE_ThrowOOMException(env, "malloc secret failed."); ++ goto cleanup; ++ } ++ memset(secret, 0, pSizeInByte); ++ ++ if ((dh = DH_new_method(kaeEngine)) == NULL) { ++ KAE_ThrowOOMException(env, "Allocate DH failed in nativeComputeKey."); ++ goto cleanup; ++ } ++ ++ if ((y_bn = KAE_GetBigNumFromByteArray(env, y)) == NULL) { ++ KAE_ThrowOOMException(env, "Convert y to BIGNUM failed."); ++ goto cleanup; ++ } ++ ++ if ((x_bn = KAE_GetBigNumFromByteArray(env, x)) == NULL) { ++ KAE_ThrowOOMException(env, "Convert x to BIGNUM failed."); ++ goto cleanup; ++ } ++ ++ if ((p_bn = KAE_GetBigNumFromByteArray(env, p)) == NULL) { ++ KAE_ThrowOOMException(env, "Convert p to BIGNUM failed."); ++ goto cleanup; ++ } ++ ++ if ((g_bn = KAE_GetBigNumFromByteArray(env, g)) == NULL) { ++ KAE_ThrowOOMException(env, "Convert g to BIGNUM failed."); ++ goto cleanup; ++ } ++ ++ if ((computeKeyRetBn = BN_new()) == NULL) { ++ KAE_ThrowOOMException(env, "Allocate BN failed."); ++ goto cleanup; ++ } ++ ++ if (!DH_set0_pqg(dh, BN_dup(p_bn), NULL, BN_dup(g_bn))) { ++ KAE_ThrowRuntimeException(env, "DH_set0_pqg failed."); ++ goto cleanup; ++ } ++ ++ if (!DH_set0_key(dh, NULL, BN_dup(x_bn))) { ++ KAE_ThrowRuntimeException(env, "DH_set0_key failed."); ++ goto cleanup; ++ } ++ ++ computekeyLength = DH_compute_key(secret, y_bn, dh); ++ ++ if (computekeyLength <= 0 ) { ++ KAE_ThrowRuntimeException(env, "DH_compute_key failed."); ++ goto cleanup; ++ } ++ ++ BN_bin2bn(secret, computekeyLength, computeKeyRetBn); ++ ++ retByteArray = KAE_GetByteArrayFromBigNum(env, computeKeyRetBn); ++ if (retByteArray == NULL) { ++ KAE_ThrowRuntimeException(env, "GetByteArrayFromBigNum failed in nativeComputeKey."); ++ goto cleanup; ++ } ++ KAE_TRACE("Java_org_openeuler_security_openssl_KAEDHKeyAgreement_nativeComputeKey finished!"); ++ ++cleanup: ++ if (dh != NULL) ++ DH_free(dh); ++ if (y_bn != NULL) ++ KAE_ReleaseBigNumFromByteArray(y_bn); ++ if (x_bn != NULL) ++ KAE_ReleaseBigNumFromByteArray(x_bn); ++ if (p_bn != NULL) ++ KAE_ReleaseBigNumFromByteArray(p_bn); ++ if (g_bn != NULL) ++ KAE_ReleaseBigNumFromByteArray(g_bn); ++ if (secret != NULL) { ++ memset(secret, 0, pSizeInByte); ++ free(secret); ++ } ++ if (computeKeyRetBn != NULL) ++ BN_free(computeKeyRetBn); ++ ++ return retByteArray; ++} +\ No newline at end of file +diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keyagreement_ecdh.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keyagreement_ecdh.c +new file mode 100644 +index 000000000..b0877a25e +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keyagreement_ecdh.c +@@ -0,0 +1,121 @@ ++/* ++ * Copyright (c) 2023, 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 ++#include ++#include ++#include "kae_log.h" ++#include "kae_exception.h" ++#include "kae_util.h" ++#include "org_openeuler_security_openssl_KAEECDHKeyAgreement.h" ++ ++static void FreeGenerateSecretParam(BIGNUM* s, BIGNUM* wX, BIGNUM* wY, ++ EC_POINT* pub, EC_KEY* eckey, EC_GROUP* group, unsigned char* shareKey, int shareKeyLen) ++{ ++ KAE_ReleaseBigNumFromByteArray(s); ++ KAE_ReleaseBigNumFromByteArray(wX); ++ KAE_ReleaseBigNumFromByteArray(wY); ++ if (pub != NULL) { ++ EC_POINT_free(pub); ++ } ++ if (eckey != NULL) { ++ EC_KEY_free(eckey); ++ } ++ if (group != NULL) { ++ EC_GROUP_free(group); ++ } ++ if (shareKey != NULL) { ++ memset(shareKey, 0, shareKeyLen); ++ free(shareKey); ++ } ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAEECDHKeyAgreement ++ * Method: nativeGenerateSecret ++ * Signature: (Ljava/lang/String;[B[B[B)[B ++ */ ++JNIEXPORT jbyteArray JNICALL Java_org_openeuler_security_openssl_KAEECDHKeyAgreement_nativeGenerateSecret ++ (JNIEnv* env, jclass cls, jstring curveName, jbyteArray wXArr, jbyteArray wYArr, jbyteArray sArr) ++{ ++ EC_GROUP* group = NULL; ++ EC_KEY* eckey = NULL; ++ BIGNUM* wX = NULL; ++ BIGNUM* wY = NULL; ++ BIGNUM* s = NULL; ++ EC_POINT* pub = NULL; ++ jbyteArray javaBytes = NULL; ++ unsigned char* shareKey = NULL; ++ const char *curve = (*env)->GetStringUTFChars(env, curveName, 0); ++ int nid = OBJ_sn2nid(curve); ++ (*env)->ReleaseStringUTFChars(env, curveName, curve); ++ ++ // Initialization of secret key. ++ int expectSecretLen = 0; ++ ++ if ((nid == NID_undef) || (group = EC_GROUP_new_by_curve_name(nid)) == NULL) { ++ goto cleanup; ++ } ++ if ((s = KAE_GetBigNumFromByteArray(env, sArr)) == NULL || (wX = KAE_GetBigNumFromByteArray(env, wXArr)) == NULL ++ || (wY = KAE_GetBigNumFromByteArray(env, wYArr)) == NULL) { ++ KAE_ThrowOOMException(env, "failed to allocate BN_new"); ++ goto cleanup; ++ } ++ if ((eckey = EC_KEY_new()) == NULL || !EC_KEY_set_group(eckey, group)) { ++ goto cleanup; ++ } ++ if ((pub = EC_POINT_new(group)) == NULL) { ++ goto cleanup; ++ } ++ if (!EC_POINT_set_affine_coordinates_GFp(group, pub, wX, wY, NULL)) { ++ goto cleanup; ++ } ++ if (!EC_KEY_set_public_key(eckey, pub) || !EC_KEY_set_private_key(eckey, s)) { ++ goto cleanup; ++ } ++ ++ // Get the length of secret key, in bytes. ++ expectSecretLen = (EC_GROUP_get_degree(group) + 7) / 8; ++ ++ if ((shareKey = malloc(expectSecretLen)) == NULL) { ++ KAE_ThrowOOMException(env, "malloc error"); ++ goto cleanup; ++ } ++ memset(shareKey, 0, expectSecretLen); ++ ++ // Perform ecdh keyagreement. ++ if (ECDH_compute_key(shareKey, expectSecretLen, pub, eckey, NULL) != expectSecretLen) { ++ goto cleanup; ++ } ++ ++ if ((javaBytes = (*env)->NewByteArray(env, expectSecretLen)) == NULL) { ++ goto cleanup; ++ } ++ (*env)->SetByteArrayRegion(env, javaBytes, 0, expectSecretLen, (jbyte*)shareKey); ++ FreeGenerateSecretParam(s, wX, wY, pub, eckey, group, shareKey, expectSecretLen); ++ return javaBytes; ++ ++cleanup: ++ FreeGenerateSecretParam(s, wX, wY, pub, eckey, group, shareKey, expectSecretLen); ++ return NULL; ++} +\ No newline at end of file +diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_dh.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_dh.c +new file mode 100644 +index 000000000..6315cc6ee +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_dh.c +@@ -0,0 +1,132 @@ ++/* ++ * Copyright (c) 2023, 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 ++#include ++#include ++#include ++#include ++#include "kae_util.h" ++#include "kae_log.h" ++#include "org_openeuler_security_openssl_KAEDHKeyPairGenerator.h" ++#include "kae_exception.h" ++ ++ ++/* ++* Class: org_openeuler_security_openssl_KAEDHKeyPairGenerator ++* Method: nativeGenerateKeyPair ++* Signature: ([B[BI)[[B ++*/ ++ ++JNIEXPORT jobjectArray JNICALL Java_org_openeuler_security_openssl_KAEDHKeyPairGenerator_nativeGenerateKeyPair ++ (JNIEnv* env, jclass cls, jbyteArray p, jbyteArray g, jint lSize) ++{ ++ DH* dh = NULL; ++ BIGNUM* p_bn = NULL; ++ BIGNUM* g_bn = NULL; ++ const BIGNUM* pri_key_bn = NULL; ++ const BIGNUM* pub_key_bn = NULL; ++ jclass byteArrayClass = NULL; ++ jobjectArray keys = NULL; ++ jbyteArray pri_key = NULL; ++ jbyteArray pub_key = NULL; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(DH_INDEX); ++ KAE_TRACE("KAEDHKeyPairGenerator_nativeGenerateKeyPair: kaeEngine => %p", kaeEngine); ++ ++ KAE_TRACE("Java_org_openeuler_security_openssl_KAEDHKeyPairGenerator_nativeGenerateKeyPair start !"); ++ ++ if ((dh = DH_new_method(kaeEngine)) == NULL) { ++ KAE_ThrowOOMException(env, "Allocate DH failed in nativeGenerateKeyPair!"); ++ goto cleanup; ++ } ++ ++ if ((p_bn = KAE_GetBigNumFromByteArray(env, p)) == NULL) { ++ KAE_ThrowOOMException(env, "Allocate p_bn failed in nativeGenerateKeyPair!"); ++ goto cleanup; ++ } ++ ++ if ((g_bn = KAE_GetBigNumFromByteArray(env, g)) == NULL) { ++ KAE_ThrowOOMException(env, "Allocate g_bn failed in nativeGenerateKeyPair!"); ++ goto cleanup; ++ } ++ ++ if (!DH_set0_pqg(dh, BN_dup(p_bn), NULL, BN_dup(g_bn))) { ++ KAE_ThrowRuntimeException(env, "DH_set0_pqg failed in nativeGenerateKeyPair."); ++ goto cleanup; ++ } ++ ++ // Return value is fixed to 1, nothing to check. ++ DH_set_length(dh, lSize); ++ ++ if (!DH_generate_key(dh)) { ++ KAE_ThrowInvalidAlgorithmParameterException(env, "DH generate key failed in nativeGenerateKeyPair."); ++ goto cleanup; ++ } ++ ++ if ((byteArrayClass = (*env)->FindClass(env, "[B")) == NULL) { ++ KAE_ThrowClassNotFoundException(env, "Class byte[] not found."); ++ goto cleanup; ++ } ++ ++ if ((keys = (*env)->NewObjectArray(env, 2, byteArrayClass, NULL)) == NULL) { ++ KAE_ThrowOOMException(env, "Allocate ByteArray failed in nativeGenerateKeyPair!"); ++ goto cleanup; ++ } ++ ++ // Return the ptr of private key in dh. ++ pri_key_bn = DH_get0_priv_key(dh); ++ pub_key_bn = DH_get0_pub_key(dh); ++ ++ pub_key = KAE_GetByteArrayFromBigNum(env, pub_key_bn); ++ if (pub_key == NULL) { ++ KAE_ThrowOOMException(env, "PublicKey allocate failed in nativeGenerateKeyPair."); ++ goto cleanup; ++ } ++ ++ pri_key = KAE_GetByteArrayFromBigNum(env, pri_key_bn); ++ if (pri_key == NULL) { ++ KAE_ThrowRuntimeException(env, "GetByteArrayFromBigNum failed in nativeGenerateKeyPair."); ++ goto cleanup; ++ } ++ ++ (*env)->SetObjectArrayElement(env, keys, 0, pub_key); ++ (*env)->SetObjectArrayElement(env, keys, 1, pri_key); ++ ++ KAE_TRACE("Java_org_openeuler_security_openssl_KAEDHKeyPairGenerator_nativeGenerateKeyPair finished !"); ++ ++cleanup: ++ if (dh != NULL) ++ DH_free(dh); ++ if (p_bn != NULL) ++ KAE_ReleaseBigNumFromByteArray(p_bn); ++ if (g_bn != NULL) ++ KAE_ReleaseBigNumFromByteArray(g_bn); ++ if (byteArrayClass != NULL) ++ (*env)->DeleteLocalRef(env, byteArrayClass); ++ if (pub_key != NULL) ++ (*env)->DeleteLocalRef(env, pub_key); ++ if (pri_key != NULL) ++ (*env)->DeleteLocalRef(env, pri_key); ++ ++ return keys; ++} +\ No newline at end of file +diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_ec.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_ec.c +new file mode 100644 +index 000000000..0449f8a26 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_ec.c +@@ -0,0 +1,505 @@ ++/* ++ * Copyright (c) 2023, 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 ++#include ++#include ++#include "kae_util.h" ++#include "kae_exception.h" ++#include "kae_log.h" ++#include "org_openeuler_security_openssl_KAEECKeyPairGenerator.h" ++ ++#define KAE_EC_PARAM_NUM_SIZE 7 ++#define KAE_EC_KEY_NUM_SIZE 3 ++ ++// ECDH param index. ++typedef enum ECDHParamIndex { ++ ecdhP = 0, ++ ecdhA, ++ ecdhB, ++ ecdhX, ++ ecdhY, ++ ecdhOrder, ++ ecdhCofactor ++} ECDHParamIndex; ++ ++// ECDH Key index. ++typedef enum ECDHKeyIndex { ++ ecdhWX = 0, ++ ecdhWY, ++ ecdhS ++} ECDHKeyIndex; ++ ++static void FreeECDHCurveParam(JNIEnv* env, BIGNUM* p, BIGNUM* a, BIGNUM* b, jbyteArray paramP, ++ jbyteArray paramA, jbyteArray paramB) ++{ ++ if (p != NULL) { ++ BN_free(p); ++ } ++ if (a != NULL) { ++ BN_free(a); ++ } ++ if (b != NULL) { ++ BN_free(b); ++ } ++ if (paramP != NULL) { ++ (*env)->DeleteLocalRef(env, paramP); ++ } ++ if (paramA != NULL) { ++ (*env)->DeleteLocalRef(env, paramA); ++ } ++ if (paramB != NULL) { ++ (*env)->DeleteLocalRef(env, paramB); ++ } ++} ++ ++// Set p, a, b in group to params. ++static bool SetECDHCurve(JNIEnv* env, EC_GROUP* group, jobjectArray params) ++{ ++ BIGNUM* p = NULL; ++ BIGNUM* a = NULL; ++ BIGNUM* b = NULL; ++ jbyteArray paramP = NULL; ++ jbyteArray paramA = NULL; ++ jbyteArray paramB = NULL; ++ if ((p = BN_new()) == NULL || (a = BN_new()) == NULL || (b = BN_new()) == NULL) { ++ KAE_ThrowOOMException(env, "failed to allocate BN_new"); ++ goto cleanup; ++ } ++ if (!EC_GROUP_get_curve_GFp(group, p, a, b, NULL)) { ++ goto cleanup; ++ } ++ ++ // Set p. ++ if ((paramP = KAE_GetByteArrayFromBigNum(env, p)) == NULL) { ++ goto cleanup; ++ } ++ (*env)->SetObjectArrayElement(env, params, ecdhP, paramP); ++ ++ // Set a. ++ if ((paramA = KAE_GetByteArrayFromBigNum(env, a)) == NULL) { ++ goto cleanup; ++ } ++ (*env)->SetObjectArrayElement(env, params, ecdhA, paramA); ++ ++ // Set b. ++ if ((paramB = KAE_GetByteArrayFromBigNum(env, b)) == NULL) { ++ goto cleanup; ++ } ++ (*env)->SetObjectArrayElement(env, params, ecdhB, paramB); ++ FreeECDHCurveParam(env, p, a, b, paramP, paramA, paramB); ++ return true; ++ ++cleanup: ++ FreeECDHCurveParam(env, p, a, b, paramP, paramA, paramB); ++ return false; ++} ++ ++// Set generator(x, y) in group to params. ++static bool SetECDHPoint(JNIEnv* env, EC_GROUP* group, jobjectArray params) ++{ ++ BIGNUM* x = NULL; ++ BIGNUM* y = NULL; ++ const EC_POINT* generator = NULL; ++ jbyteArray paramX = NULL; ++ jbyteArray paramY = NULL; ++ if ((x = BN_new()) == NULL || (y = BN_new()) == NULL) { ++ KAE_ThrowOOMException(env, "failed to allocate BN_new"); ++ goto cleanup; ++ } ++ if ((generator = EC_GROUP_get0_generator(group)) == NULL) { ++ KAE_ThrowOOMException(env, "failed to allocate ec generator"); ++ goto cleanup; ++ } ++ if (!EC_POINT_get_affine_coordinates_GFp(group, generator, x, y, NULL)) { ++ KAE_ThrowFromOpenssl(env, "EC_POINT_set_affine_coordinates_GFp", KAE_ThrowRuntimeException); ++ goto cleanup; ++ } ++ ++ // Set x. ++ if ((paramX = KAE_GetByteArrayFromBigNum(env, x)) == NULL) { ++ goto cleanup; ++ } ++ (*env)->SetObjectArrayElement(env, params, ecdhX, paramX); ++ ++ // Set y. ++ if ((paramY = KAE_GetByteArrayFromBigNum(env, y)) == NULL) { ++ goto cleanup; ++ } ++ (*env)->SetObjectArrayElement(env, params, ecdhY, paramY); ++ BN_free(x); ++ BN_free(y); ++ (*env)->DeleteLocalRef(env, paramX); ++ (*env)->DeleteLocalRef(env, paramY); ++ return true; ++ ++cleanup: ++ if (x != NULL) { ++ BN_free(x); ++ } ++ if (y != NULL) { ++ BN_free(y); ++ } ++ if (paramX != NULL) { ++ (*env)->DeleteLocalRef(env, paramX); ++ } ++ if (paramY != NULL) { ++ (*env)->DeleteLocalRef(env, paramY); ++ } ++ return false; ++} ++ ++// Set order, cofactor in group to params. ++static bool SetECDHOrderAndCofactor(JNIEnv* env, EC_GROUP* group, jobjectArray params) ++{ ++ BIGNUM* order = NULL; ++ BIGNUM* cofactor = NULL; ++ jbyteArray paramOrder = NULL; ++ jbyteArray paramCofactor = NULL; ++ if ((order = BN_new()) == NULL || (cofactor = BN_new()) == NULL) { ++ goto cleanup; ++ } ++ if (!EC_GROUP_get_order(group, order, NULL)) { ++ goto cleanup; ++ } ++ ++ // Set order. ++ if ((paramOrder = KAE_GetByteArrayFromBigNum(env, order)) == NULL) { ++ goto cleanup; ++ } ++ (*env)->SetObjectArrayElement(env, params, ecdhOrder, paramOrder); ++ if (!EC_GROUP_get_cofactor(group, cofactor, NULL)) { ++ goto cleanup; ++ } ++ ++ // Set cofactor. ++ if ((paramCofactor = KAE_GetByteArrayFromBigNum(env, cofactor)) == NULL) { ++ goto cleanup; ++ } ++ (*env)->SetObjectArrayElement(env, params, ecdhCofactor, paramCofactor); ++ BN_free(order); ++ BN_free(cofactor); ++ (*env)->DeleteLocalRef(env, paramOrder); ++ (*env)->DeleteLocalRef(env, paramCofactor); ++ return true; ++ ++cleanup: ++ if (order != NULL) { ++ BN_free(order); ++ } ++ if (cofactor != NULL) { ++ BN_free(cofactor); ++ } ++ if (paramOrder != NULL) { ++ (*env)->DeleteLocalRef(env, paramOrder); ++ } ++ if (paramCofactor != NULL) { ++ (*env)->DeleteLocalRef(env, paramCofactor); ++ } ++ return false; ++} ++ ++static void FreeECDHKeyParam(JNIEnv* env, ++ BIGNUM* wX, BIGNUM* wY, jbyteArray keyWX, jbyteArray keyWY, jbyteArray keyS) ++{ ++ if (wX != NULL) { ++ BN_free(wX); ++ } ++ if (wY != NULL) { ++ BN_free(wY); ++ } ++ if (keyWX != NULL) { ++ (*env)->DeleteLocalRef(env, keyWX); ++ } ++ if (keyWY != NULL) { ++ (*env)->DeleteLocalRef(env, keyWY); ++ } ++ if (keyS != NULL) { ++ (*env)->DeleteLocalRef(env, keyS); ++ } ++} ++ ++// Set publicKey(wX, wY) and privateKey(s) in eckey to params. ++static bool SetECDHKey(JNIEnv* env, const EC_GROUP* group, jobjectArray params, ++ const EC_KEY* eckey) ++{ ++ BIGNUM* wX = NULL; ++ BIGNUM* wY = NULL; ++ const EC_POINT* pub = NULL; ++ const BIGNUM* s = NULL; ++ jbyteArray keyWX = NULL; ++ jbyteArray keyWY = NULL; ++ jbyteArray keyS = NULL; ++ if ((wX = BN_new()) == NULL || (wY = BN_new()) == NULL) { ++ KAE_ThrowOOMException(env, "failed to allocate array"); ++ goto cleanup; ++ } ++ ++ if ((pub = EC_KEY_get0_public_key(eckey)) == NULL || ++ !EC_POINT_get_affine_coordinates_GFp(group, pub, wX, wY, NULL)) { ++ goto cleanup; ++ } ++ if ((s = EC_KEY_get0_private_key(eckey)) == NULL) { ++ goto cleanup; ++ } ++ ++ // Set wX. ++ if ((keyWX = KAE_GetByteArrayFromBigNum(env, wX)) == NULL) { ++ goto cleanup; ++ } ++ (*env)->SetObjectArrayElement(env, params, ecdhWX, keyWX); ++ ++ // Set wY. ++ if ((keyWY = KAE_GetByteArrayFromBigNum(env, wY)) == NULL) { ++ goto cleanup; ++ } ++ (*env)->SetObjectArrayElement(env, params, ecdhWY, keyWY); ++ ++ // Set s. ++ if ((keyS = KAE_GetByteArrayFromBigNum(env, s)) == NULL) { ++ goto cleanup; ++ } ++ (*env)->SetObjectArrayElement(env, params, ecdhS, keyS); ++ FreeECDHKeyParam(env, wX, wY, keyWX, keyWY, keyS); ++ return true; ++ ++cleanup: ++ FreeECDHKeyParam(env, wX, wY, keyWX, keyWY, keyS); ++ return false; ++} ++ ++// Convert EC_GROUP in openssl to byte[][] in java ++static jobjectArray NewECDHParam(JNIEnv* env, EC_GROUP* group) ++{ ++ jclass byteArrayClass = (*env)->FindClass(env, "[B"); ++ jobjectArray params = (*env)->NewObjectArray(env, KAE_EC_PARAM_NUM_SIZE, byteArrayClass, NULL); ++ if (params == NULL) { ++ KAE_ThrowOOMException(env, "failed to allocate array"); ++ goto cleanup; ++ } ++ ++ if (!SetECDHCurve(env, group, params)) { ++ goto cleanup; ++ } ++ if (!SetECDHPoint(env, group, params)) { ++ goto cleanup; ++ } ++ if (!SetECDHOrderAndCofactor(env, group, params)) { ++ goto cleanup; ++ } ++ ++ (*env)->DeleteLocalRef(env, byteArrayClass); ++ return params; ++ ++cleanup: ++ if (byteArrayClass != NULL) { ++ (*env)->DeleteLocalRef(env, byteArrayClass); ++ } ++ if (params != NULL) { ++ (*env)->DeleteLocalRef(env, params); ++ } ++ return NULL; ++} ++ ++// Convert EC_KEY in openssl to byte[][] in java ++static jobjectArray NewECDHKey(JNIEnv* env, const EC_GROUP* group, const EC_KEY* eckey) ++{ ++ jclass byteArrayClass = (*env)->FindClass(env, "[B"); ++ jobjectArray params = (*env)->NewObjectArray(env, KAE_EC_KEY_NUM_SIZE, byteArrayClass, NULL); ++ if (params == NULL) { ++ KAE_ThrowOOMException(env, "failed to allocate array"); ++ goto cleanup; ++ } ++ if (!SetECDHKey(env, group, params, eckey)) { ++ goto cleanup; ++ } ++ ++ (*env)->DeleteLocalRef(env, byteArrayClass); ++ return params; ++ ++cleanup: ++ if (byteArrayClass != NULL) { ++ (*env)->DeleteLocalRef(env, byteArrayClass); ++ } ++ if (params != NULL) { ++ (*env)->DeleteLocalRef(env, params); ++ } ++ return NULL; ++} ++ ++static void FreeECDHParam(BIGNUM* p, BIGNUM* a, BIGNUM* b, BIGNUM* x, BIGNUM* y, BIGNUM* order, BIGNUM* cofactor) ++{ ++ KAE_ReleaseBigNumFromByteArray(p); ++ KAE_ReleaseBigNumFromByteArray(a); ++ KAE_ReleaseBigNumFromByteArray(b); ++ KAE_ReleaseBigNumFromByteArray(x); ++ KAE_ReleaseBigNumFromByteArray(y); ++ KAE_ReleaseBigNumFromByteArray(order); ++ KAE_ReleaseBigNumFromByteArray(cofactor); ++} ++ ++// Convert params in java to EC_GROUP in openssl ++static EC_GROUP* GetGroupByParam(JNIEnv* env, jbyteArray pArr, jbyteArray aArr, jbyteArray bArr, ++ jbyteArray xArr, jbyteArray yArr, jbyteArray orderArr, jint cofactorInt) ++{ ++ BIGNUM* p = NULL; ++ BIGNUM* a = NULL; ++ BIGNUM* b = NULL; ++ BIGNUM* x = NULL; ++ BIGNUM* y = NULL; ++ BIGNUM* order = NULL; ++ BIGNUM* cofactor = NULL; ++ EC_GROUP* group = NULL; ++ BN_CTX* ctx = NULL; ++ EC_POINT* generator = NULL; ++ if ((p = KAE_GetBigNumFromByteArray(env, pArr)) == NULL || (a = KAE_GetBigNumFromByteArray(env, aArr)) == NULL || ++ (b = KAE_GetBigNumFromByteArray(env, bArr)) == NULL || (x = KAE_GetBigNumFromByteArray(env, xArr)) == NULL || ++ (y = KAE_GetBigNumFromByteArray(env, yArr)) == NULL || (cofactor = BN_new()) == NULL || ++ (order = KAE_GetBigNumFromByteArray(env, orderArr)) == NULL || !BN_set_word(cofactor, cofactorInt)) { ++ goto cleanup; ++ } ++ ++ // Create the curve. ++ if ((ctx = BN_CTX_new()) == NULL || (group = EC_GROUP_new_curve_GFp(p, a, b, ctx)) == NULL) { ++ goto cleanup; ++ } ++ ++ // Create the generator and set x, y. ++ if ((generator = EC_POINT_new(group)) == NULL || ++ !EC_POINT_set_affine_coordinates_GFp(group, generator, x, y, ctx)) { ++ goto cleanup; ++ } ++ ++ // Set the generator, order and cofactor. ++ if (!EC_GROUP_set_generator(group, generator, order, cofactor)) { ++ goto cleanup; ++ } ++ ++ FreeECDHParam(p, a, b, x, y, order, cofactor); ++ EC_POINT_free(generator); ++ BN_CTX_free(ctx); ++ return group; ++ ++cleanup: ++ FreeECDHParam(p, a, b, x, y, order, cofactor); ++ if (group != NULL) { ++ EC_GROUP_free(group); ++ } ++ if (generator != NULL) { ++ EC_POINT_free(generator); ++ } ++ if (ctx != NULL) { ++ BN_CTX_free(ctx); ++ } ++ return NULL; ++} ++ ++ ++/* ++ * Class: org_openeuler_security_openssl_KAEECKeyPairGenerator ++ * Method: nativeGenerateParam ++ * Signature: (Ljava/lang/String;)[[B ++ */ ++JNIEXPORT jobjectArray JNICALL Java_org_openeuler_security_openssl_KAEECKeyPairGenerator_nativeGenerateParam( ++ JNIEnv* env, jclass cls, jstring curveName) ++{ ++ EC_GROUP* group = NULL; ++ jobjectArray ecdhParam = NULL; ++ ++ const char *curve = (*env)->GetStringUTFChars(env, curveName, 0); ++ KAE_TRACE("KAEECKeyPairGenerator_nativeGenerateParam(curveName = %s)", curve); ++ int nid = OBJ_sn2nid(curve); ++ (*env)->ReleaseStringUTFChars(env, curveName, curve); ++ if (nid == NID_undef) { ++ goto cleanup; ++ } ++ // Construct a builtin curve. ++ if ((group = EC_GROUP_new_by_curve_name(nid)) == NULL) { ++ goto cleanup; ++ } ++ ecdhParam = NewECDHParam(env, group); ++ ++ if (group != NULL) { ++ EC_GROUP_free(group); ++ } ++ KAE_TRACE("KAEECKeyPairGenerator_nativeGenerateParam success, ecdhParam = %p", ecdhParam); ++ return ecdhParam; ++ ++cleanup: ++ if (group != NULL) { ++ EC_GROUP_free(group); ++ } ++ if (ecdhParam != NULL) { ++ (*env)->DeleteLocalRef(env, ecdhParam); ++ } ++ return NULL; ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAEECKeyPairGenerator ++ * Method: nativeGenerateKeyPair ++ * Signature: ([B[B[B[B[B[BI)[[B ++ */ ++JNIEXPORT jobjectArray JNICALL Java_org_openeuler_security_openssl_KAEECKeyPairGenerator_nativeGenerateKeyPair( ++ JNIEnv* env, jclass cls, jbyteArray pArr, jbyteArray aArr, jbyteArray bArr, ++ jbyteArray xArr, jbyteArray yArr, jbyteArray orderArr, jint cofactorInt) ++{ ++ EC_GROUP* group = NULL; ++ EC_KEY* eckey = NULL; ++ jobjectArray ecdhKey = NULL; ++ ++ if ((group = GetGroupByParam(env, pArr, aArr, bArr, xArr, yArr, orderArr, cofactorInt)) == NULL) { ++ goto cleanup; ++ } ++ if ((eckey = EC_KEY_new()) == NULL) { ++ goto cleanup; ++ } ++ if (!EC_KEY_set_group(eckey, group)) { ++ goto cleanup; ++ } ++ // Generates a new public and private key for the supplied eckey object. ++ // Refer to {@link https://www.openssl.org/docs/man1.1.0/man3/EC_KEY_generate_key.html} for details. ++ if (!EC_KEY_generate_key(eckey)) { ++ goto cleanup; ++ } ++ ++ ecdhKey = NewECDHKey(env, group, eckey); ++ ++ EC_KEY_free(eckey); ++ EC_GROUP_free(group); ++ ++ KAE_TRACE("KAEECKeyPairGenerator_nativeGenerateKeyPair success, ecdhKey = %p", ecdhKey); ++ return ecdhKey; ++ ++cleanup: ++ if (eckey != NULL) { ++ EC_KEY_free(eckey); ++ } ++ if (group != NULL) { ++ EC_GROUP_free(group); ++ } ++ if (ecdhKey != NULL) { ++ (*env)->DeleteLocalRef(env, ecdhKey); ++ } ++ return NULL; ++} +\ No newline at end of file +diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_rsa.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_rsa.c +new file mode 100644 +index 000000000..84c2ed109 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_rsa.c +@@ -0,0 +1,173 @@ ++/* ++ * Copyright (c) 2023, 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 ++#include ++#include "kae_log.h" ++#include "kae_util.h" ++#include "kae_exception.h" ++#include "org_openeuler_security_openssl_KAERSAKeyPairGenerator.h" ++#define KAE_RSA_PARAM_SIZE 8 ++ ++// rsa param index ++typedef enum RSAParamIndex { ++ rsaN = 0, ++ rsaE, ++ rsaD, ++ rsaP, ++ rsaQ, ++ rsaDmp1, ++ rsaDmq1, ++ rsaIqmp ++} RSAParamIndex; ++ ++// rsa param name array ++static const char* rsaParamNames[] = {"n", "e", "d", "p", "q", "dmp1", "dmq1", "iqmp"}; ++ ++// rsa get rsa param function list ++static const BIGNUM* (* GetRSAParamFunctionList[])(const RSA*) = { ++ RSA_get0_n, ++ RSA_get0_e, ++ RSA_get0_d, ++ RSA_get0_p, ++ RSA_get0_q, ++ RSA_get0_dmp1, ++ RSA_get0_dmq1, ++ RSA_get0_iqmp ++}; ++ ++/* ++ * New RSA and generate rsa key, follow the steps below ++ * step 1.New RSA ++ * step 2.Convert publicExponent to BIGNUM ++ * step 3.Generate rsa key, and all key information is stored in RSA ++ */ ++static RSA* NewRSA(JNIEnv* env, jint keySize, jbyteArray publicExponent) { ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); ++ KAE_TRACE("NewRSA: kaeEngine => %p", kaeEngine); ++ ++ // new rsa ++ RSA* rsa = RSA_new_method(kaeEngine); ++ if (rsa == NULL) { ++ KAE_ThrowFromOpenssl(env, "RSA_new_method", KAE_ThrowRuntimeException); ++ return NULL; ++ } ++ ++ // convert publicExponent to BIGNUM ++ BIGNUM* exponent = KAE_GetBigNumFromByteArray(env, publicExponent); ++ if (exponent == NULL) { ++ return NULL; ++ } ++ ++ // generate rsa key ++ int result_code = RSA_generate_key_ex(rsa, keySize, exponent, NULL); ++ KAE_ReleaseBigNumFromByteArray(exponent); ++ if (result_code <= 0) { ++ RSA_free(rsa); ++ KAE_ThrowFromOpenssl(env, "RSA_generate_key_ex", KAE_ThrowRuntimeException); ++ return NULL; ++ } ++ return rsa; ++} ++ ++/* ++ * release RSA ++ */ ++static void ReleaseRSA(RSA* rsa) { ++ if (rsa != NULL) { ++ RSA_free(rsa); ++ } ++} ++ ++/* ++ * Set rsa key param, follow the steps below ++ * step 1. Get rsa param value ++ * step 2. Convert paramValue (BIGNUM) to jbyteArray ++ * step 3. Set the rsa param to the param array ++ */ ++static bool SetRSAKeyParam(JNIEnv* env, RSA* rsa, jobjectArray params, RSAParamIndex rsaParamIndex) { ++ // get rsa param value ++ const BIGNUM* rsaParamValue = GetRSAParamFunctionList[rsaParamIndex](rsa); ++ if (rsaParamValue == NULL) { ++ return false; ++ } ++ ++ // Convert paramValue to jbyteArray ++ jbyteArray param = KAE_GetByteArrayFromBigNum(env, rsaParamValue); ++ if (param == NULL) { ++ return false; ++ } ++ ++ // Set the rsa param to the param array ++ (*env)->SetObjectArrayElement(env, params, rsaParamIndex, param); ++ return true; ++} ++ ++/* ++ * New rsa key params, follow the steps below ++ * step 1. New rsa key param array ++ * step 2. Set rsa key param ++ */ ++static jobjectArray NewRSAKeyParams(JNIEnv* env, RSA* rsa) { ++ // new param array ++ jclass byteArrayClass = (*env)->FindClass(env, "[B"); ++ jobjectArray params = (*env)->NewObjectArray(env, KAE_RSA_PARAM_SIZE, byteArrayClass, NULL); ++ if (params == NULL) { ++ KAE_ThrowOOMException(env, "failed to allocate array"); ++ return NULL; ++ } ++ ++ // set rsa key param ++ RSAParamIndex paramIndex; ++ for (paramIndex = rsaN; paramIndex <= rsaIqmp; paramIndex++) { ++ if (!SetRSAKeyParam(env, rsa, params, paramIndex)) { ++ return NULL; ++ } ++ } ++ return params; ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAERSAKeyPairGenerator ++ * Method: nativeGenerateKeyPair ++ * Signature: (I[B)[[B ++ */ ++JNIEXPORT jobjectArray JNICALL Java_org_openeuler_security_openssl_KAERSAKeyPairGenerator_nativeGenerateKeyPair ( ++ JNIEnv* env, jclass cls, jint keySize, jbyteArray publicExponent) { ++ if (publicExponent == NULL) { ++ return NULL; ++ } ++ ++ // new RSA ++ RSA* rsa = NewRSA(env, keySize, publicExponent); ++ if (rsa == NULL) { ++ return NULL; ++ } ++ ++ // new RSA Key Parameters ++ jobjectArray rsaParm = NewRSAKeyParams(env, rsa); ++ ++ // release rsa ++ ReleaseRSA(rsa); ++ return rsaParm; ++} +\ No newline at end of file +diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_log.h b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_log.h +new file mode 100644 +index 000000000..b618546c5 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_log.h +@@ -0,0 +1,33 @@ ++/* ++ * Copyright (c) 2023, 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 KAE_LOG_H ++#define KAE_LOG_H ++ ++#ifdef KAE_DEBUG ++#define KAE_TRACE(...) { fprintf(stdout, __VA_ARGS__); fprintf(stdout, "\n"); } ++#else ++#define KAE_TRACE(...) ++#endif ++ ++#endif +diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_provider.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_provider.c +new file mode 100644 +index 000000000..f4f71005a +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_provider.c +@@ -0,0 +1,103 @@ ++/* ++ * Copyright (c) 2023, 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 ++#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, 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) { ++ ENGINE_free(e); ++ e = NULL; ++ } ++ ++ // determine whether KAE is loaded successfully ++ const char* id = (*env)->GetStringUTFChars(env, engineId, 0); ++ e = ENGINE_by_id(id); ++ (*env)->ReleaseStringUTFChars(env, engineId, id); ++ if (e == NULL) { ++ 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/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_signature_rsa.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_signature_rsa.c +new file mode 100644 +index 000000000..496ebc775 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_signature_rsa.c +@@ -0,0 +1,366 @@ ++/* ++ * Copyright (c) 2023, 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 ++#include ++#include ++#include ++#include "kae_log.h" ++#include "kae_util.h" ++#include "kae_exception.h" ++ ++// get EVP_MD by digestName ++static const EVP_MD* getEvpMd(JNIEnv* env, jstring digestName) { ++ const char* digestNameUtf = (*env)->GetStringUTFChars(env, digestName, 0); ++ const EVP_MD* md = (EVP_MD*)EVP_get_digestbyname(digestNameUtf); ++ (*env)->ReleaseStringUTFChars(env, digestName, digestNameUtf); ++ if (md == NULL) { ++ KAE_ThrowSignatureException(env, "Unsupported digest algorithm."); ++ } ++ return md; ++} ++ ++// sign release ++static void signRelease(JNIEnv* env, jbyteArray digestValue, jbyte* digestBytes, jbyte* sigBytes, ++ EVP_PKEY_CTX* pkeyCtx) { ++ if (digestBytes != NULL) { ++ (*env)->ReleaseByteArrayElements(env, digestValue, digestBytes, 0); ++ } ++ if (sigBytes != NULL) { ++ free(sigBytes); ++ } ++ if (pkeyCtx != NULL) { ++ EVP_PKEY_CTX_free(pkeyCtx); ++ } ++} ++ ++// verify release ++static void verifyRelease(JNIEnv* env, jbyteArray digestValue, jbyte* digestBytes, jbyteArray sigValue, jbyte* sigBytes, ++ EVP_PKEY_CTX* pkeyCtx) { ++ if (digestBytes != NULL) { ++ (*env)->ReleaseByteArrayElements(env, digestValue, digestBytes, 0); ++ } ++ if (sigBytes != NULL) { ++ (*env)->ReleaseByteArrayElements(env, sigValue, sigBytes, 0); ++ } ++ if (pkeyCtx != NULL) { ++ EVP_PKEY_CTX_free(pkeyCtx); ++ } ++} ++ ++// set rsa PkeyCtx parameters ++static bool setRsaPkeyCtxParameters(JNIEnv* env, EVP_PKEY_CTX* pkeyCtx, jint paddingType, jstring digestName) { ++ // set rsa padding ++ if (EVP_PKEY_CTX_set_rsa_padding(pkeyCtx, paddingType) <= 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_CTX_set_rsa_padding", KAE_ThrowSignatureException); ++ return false; ++ } ++ ++ // set signature md ++ const EVP_MD* md = getEvpMd(env, digestName); ++ if (md == NULL) { ++ return false; ++ } ++ ++ if (EVP_PKEY_CTX_set_signature_md(pkeyCtx, md) <= 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_CTX_set_signature_md", KAE_ThrowSignatureException); ++ return false; ++ } ++ return true; ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAERSASignatureNative ++ * Method: rsaSign ++ * Signature: (JLjava/lang/String;[BI)[B ++ */ ++JNIEXPORT jbyteArray JNICALL Java_org_openeuler_security_openssl_KAERSASignatureNative_rsaSign(JNIEnv* env, jclass cls, ++ jlong keyAddress, jstring digestName, jbyteArray digestValue, jint paddingType) { ++ EVP_PKEY* pkey = (EVP_PKEY*)keyAddress; ++ EVP_PKEY_CTX* pkeyCtx = NULL; ++ jbyte* digestBytes = NULL; ++ jbyte* sigBytes = NULL; ++ jbyteArray sigByteArray = NULL; ++ static ENGINE* kaeEngine = NULL; ++ kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ // new EVP_PKEY_CTX ++ if ((pkeyCtx = EVP_PKEY_CTX_new(pkey, kaeEngine)) == NULL) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_new", KAE_ThrowSignatureException); ++ goto cleanup; ++ } ++ ++ // sign init ++ if (EVP_PKEY_sign_init(pkeyCtx) <= 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_sign_init", KAE_ThrowSignatureException); ++ goto cleanup; ++ } ++ ++ // set rsa PkeyCtx parameters ++ if (!setRsaPkeyCtxParameters(env, pkeyCtx, paddingType, digestName)) { ++ goto cleanup; ++ } ++ ++ // sign ++ size_t sigLen = (size_t)EVP_PKEY_size(pkey); ++ if (sigLen <= 0) { ++ KAE_ThrowSignatureException(env, "The sigLen size cannot be zero or negative"); ++ goto cleanup; ++ } ++ if ((sigBytes = malloc(sigLen)) == NULL) { ++ KAE_ThrowOOMException(env, "malloc failed"); ++ goto cleanup; ++ } ++ if ((digestBytes = (*env)->GetByteArrayElements(env, digestValue, NULL)) == NULL) { ++ KAE_ThrowOOMException(env, "GetByteArrayElements failed"); ++ goto cleanup; ++ } ++ size_t digestLen = (size_t)(*env)->GetArrayLength(env, digestValue); ++ if (EVP_PKEY_sign(pkeyCtx, (unsigned char*)sigBytes, &sigLen, ++ (const unsigned char*)digestBytes, digestLen) <= 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_sign", KAE_ThrowSignatureException); ++ goto cleanup; ++ } ++ ++ // set signature byte to jbyteArray ++ if ((sigByteArray = (*env)->NewByteArray(env, (jsize)sigLen)) == NULL) { ++ KAE_ThrowOOMException(env, "NewByteArray failed"); ++ goto cleanup; ++ } ++ (*env)->SetByteArrayRegion(env, sigByteArray, 0, (jsize)sigLen, sigBytes); ++ ++cleanup: ++ signRelease(env, digestValue, digestBytes, sigBytes, pkeyCtx); ++ return sigByteArray; ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAERSASignatureNative ++ * Method: rsaVerify ++ * Signature: (JLjava/lang/String;[BI[B)Z ++ */ ++JNIEXPORT jboolean JNICALL Java_org_openeuler_security_openssl_KAERSASignatureNative_rsaVerify(JNIEnv* env, jclass cls, ++ jlong keyAddress, jstring digestName, jbyteArray digestValue, jint paddingType, jbyteArray sigValue) { ++ EVP_PKEY* pkey = (EVP_PKEY*)keyAddress; ++ EVP_PKEY_CTX* pkeyCtx = NULL; ++ jbyte* digestBytes = NULL; ++ jbyte* sigBytes = NULL; ++ jboolean isSuccess = JNI_FALSE; ++ static ENGINE* kaeEngine = NULL; ++ kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ // new EVP_PKEY_CTX ++ if ((pkeyCtx = EVP_PKEY_CTX_new(pkey, kaeEngine)) == NULL) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_new", KAE_ThrowSignatureException); ++ goto cleanup; ++ } ++ ++ // verify init ++ if (EVP_PKEY_verify_init(pkeyCtx) <= 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_sign_init", KAE_ThrowSignatureException); ++ goto cleanup; ++ } ++ ++ // set rsa PkeyCtx parameters ++ if (!setRsaPkeyCtxParameters(env, pkeyCtx, paddingType, digestName)) { ++ goto cleanup; ++ } ++ ++ // verify ++ if ((digestBytes = (*env)->GetByteArrayElements(env, digestValue, NULL)) == NULL) { ++ KAE_ThrowOOMException(env, "GetByteArrayElements failed"); ++ goto cleanup; ++ } ++ if ((sigBytes = (*env)->GetByteArrayElements(env, sigValue, NULL)) == NULL) { ++ KAE_ThrowOOMException(env, "GetByteArrayElements failed"); ++ goto cleanup; ++ } ++ size_t sigLen = (size_t)(*env)->GetArrayLength(env, sigValue); ++ size_t digestLen = (size_t)(*env)->GetArrayLength(env, digestValue); ++ if (EVP_PKEY_verify(pkeyCtx, (const unsigned char*)sigBytes, sigLen, ++ (const unsigned char*)digestBytes, digestLen) <= 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_verify", KAE_ThrowSignatureException); ++ goto cleanup; ++ } ++ isSuccess = JNI_TRUE; ++ ++cleanup: ++ verifyRelease(env, digestValue, digestBytes, sigValue, sigBytes, pkeyCtx); ++ return isSuccess; ++} ++ ++// set pss pkeyCtx parameters ++static bool setPssPkeyCtxParameters(JNIEnv* env, EVP_PKEY_CTX* pkeyCtx, jint paddingType, jstring digestName, ++ jstring mgf1DigestName, jint saltLen) { ++ // set rsa padding ++ if (EVP_PKEY_CTX_set_rsa_padding(pkeyCtx, paddingType) <= 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_CTX_set_rsa_padding", KAE_ThrowSignatureException); ++ return false; ++ } ++ ++ // set signature md ++ const EVP_MD* md = getEvpMd(env, digestName); ++ if (md == NULL) { ++ return false; ++ } ++ if (EVP_PKEY_CTX_set_signature_md(pkeyCtx, md) <= 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_CTX_set_signature_md", KAE_ThrowSignatureException); ++ return false; ++ } ++ ++ // set rsa mgf1 md ++ const EVP_MD* mgf1Md = getEvpMd(env, mgf1DigestName); ++ if (mgf1Md == NULL) { ++ return false; ++ } ++ if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkeyCtx, mgf1Md) <= 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_CTX_set_rsa_mgf1_md", KAE_ThrowSignatureException); ++ return false; ++ } ++ ++ // set salt len ++ if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkeyCtx, saltLen) <= 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_CTX_set_rsa_pss_saltlen", KAE_ThrowSignatureException); ++ return false; ++ } ++ return true; ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAERSASignatureNative ++ * Method: pssSign ++ * Signature: (JLjava/lang/String;[BILjava/lang/String;I)[B ++ */ ++JNIEXPORT jbyteArray JNICALL Java_org_openeuler_security_openssl_KAERSASignatureNative_pssSign(JNIEnv* env, jclass cls, ++ jlong keyAddress, jstring digestName, jbyteArray digestValue, jint paddingType, jstring mgf1DigestName, ++ jint saltLen) { ++ EVP_PKEY* pkey = (EVP_PKEY*)keyAddress; ++ EVP_PKEY_CTX* pkeyCtx = NULL; ++ jbyte* digestBytes = NULL; ++ jbyte* sigBytes = NULL; ++ jbyteArray sigByteArray = NULL; ++ 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); ++ goto cleanup; ++ } ++ ++ // sign init ++ if (EVP_PKEY_sign_init(pkeyCtx) <= 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_sign_init", KAE_ThrowSignatureException); ++ goto cleanup; ++ } ++ ++ // set pss pkeyCtx parameters ++ if (!setPssPkeyCtxParameters(env, pkeyCtx, paddingType, digestName, mgf1DigestName, saltLen)) { ++ goto cleanup; ++ } ++ ++ // sign ++ size_t sigLen = (size_t)EVP_PKEY_size(pkey); ++ if (sigLen <= 0) { ++ KAE_ThrowSignatureException(env, "The sigLen size cannot be zero or negative"); ++ goto cleanup; ++ } ++ if ((sigBytes = malloc(sigLen)) == NULL) { ++ KAE_ThrowOOMException(env, "malloc failed"); ++ goto cleanup; ++ } ++ if ((digestBytes = (*env)->GetByteArrayElements(env, digestValue, NULL)) == NULL) { ++ KAE_ThrowOOMException(env, "GetByteArrayElements failed"); ++ goto cleanup; ++ } ++ size_t digestLen = (size_t)(*env)->GetArrayLength(env, digestValue); ++ if (EVP_PKEY_sign(pkeyCtx, (unsigned char*)sigBytes, &sigLen, ++ (const unsigned char*)digestBytes, digestLen) <= 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_sign", KAE_ThrowSignatureException); ++ goto cleanup; ++ } ++ ++ // set signature byte to jbyteArray ++ if ((sigByteArray = (*env)->NewByteArray(env, (jsize)sigLen)) == NULL) { ++ KAE_ThrowOOMException(env, "NewByteArray failed"); ++ goto cleanup; ++ } ++ (*env)->SetByteArrayRegion(env, sigByteArray, 0, (jsize)sigLen, sigBytes); ++ ++cleanup: ++ signRelease(env, digestValue, digestBytes, sigBytes, pkeyCtx); ++ return sigByteArray; ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAERSASignatureNative ++ * Method: pssVerify ++ * Signature: (JLjava/lang/String;[BILjava/lang/String;I[B)Z ++ */ ++JNIEXPORT jboolean JNICALL Java_org_openeuler_security_openssl_KAERSASignatureNative_pssVerify(JNIEnv* env, jclass cls, ++ jlong keyAddress, jstring digestName, jbyteArray digestValue, jint paddingType, jstring mgf1DigestName, ++ jint saltLen, jbyteArray sigValue) { ++ EVP_PKEY* pkey = (EVP_PKEY*)keyAddress; ++ EVP_PKEY_CTX* pkeyCtx = NULL; ++ jbyte* digestBytes = NULL; ++ jbyte* sigBytes = NULL; ++ jboolean isSuccess = JNI_FALSE; ++ 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); ++ goto cleanup; ++ } ++ ++ // verify init ++ if (EVP_PKEY_verify_init(pkeyCtx) <= 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_sign_init", KAE_ThrowSignatureException); ++ goto cleanup; ++ } ++ ++ // set pkeyCtx parameters ++ if (!setPssPkeyCtxParameters(env, pkeyCtx, paddingType, digestName, mgf1DigestName, saltLen)) { ++ goto cleanup; ++ } ++ ++ // verify ++ if ((digestBytes = (*env)->GetByteArrayElements(env, digestValue, NULL)) == NULL) { ++ KAE_ThrowOOMException(env, "GetByteArrayElements failed"); ++ goto cleanup; ++ } ++ if ((sigBytes = (*env)->GetByteArrayElements(env, sigValue, NULL)) == NULL) { ++ KAE_ThrowOOMException(env, "GetByteArrayElements failed"); ++ goto cleanup; ++ } ++ size_t sigLen = (size_t)(*env)->GetArrayLength(env, sigValue); ++ size_t digestLen = (size_t)(*env)->GetArrayLength(env, digestValue); ++ if (EVP_PKEY_verify(pkeyCtx, (const unsigned char*)sigBytes, sigLen, ++ (const unsigned char*)digestBytes, digestLen) <= 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_PKEY_verify", KAE_ThrowSignatureException); ++ goto cleanup; ++ } ++ isSuccess = JNI_TRUE; ++ ++cleanup: ++ verifyRelease(env, digestValue, digestBytes, sigValue, sigBytes, pkeyCtx); ++ return isSuccess; ++} +\ No newline at end of file +diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_symmetric_cipher.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_symmetric_cipher.c +new file mode 100644 +index 000000000..67151f53a +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_symmetric_cipher.c +@@ -0,0 +1,415 @@ ++/* ++ * Copyright (c) 2023, 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 ++#include ++#include ++#include ++#include ++#include "kae_exception.h" ++#include "kae_log.h" ++#include "kae_util.h" ++#include "org_openeuler_security_openssl_KAESymmetricCipherBase.h" ++ ++bool StartsWith(const char* str1, const char* str2) ++{ ++ if (str1 == NULL || str2 == NULL) { ++ return 0; ++ } ++ int len1 = strlen(str1); ++ int len2 = strlen(str2); ++ if (len1 > len2 || (len1 == 0 || len2 == 0)) { ++ return false; ++ } ++ const char *cur = str1; ++ int i = 0; ++ while (*cur != '\0') { ++ if (*cur != str2[i]) { ++ return 0; ++ } ++ cur++; ++ i++; ++ } ++ return true; ++} ++ ++static const EVP_CIPHER* EVPGetSm4CipherByName(JNIEnv* env, const char* algo) ++{ ++ static const EVP_CIPHER* sm4Ecb = NULL; ++ static const EVP_CIPHER* sm4Cbc = NULL; ++ static const EVP_CIPHER* sm4Ctr = NULL; ++ static const EVP_CIPHER* sm4Ofb = NULL; ++ ++ if (strcasecmp(algo, "sm4-ecb") == 0) { ++ return sm4Ecb == NULL ? sm4Ecb = EVP_get_cipherbyname(algo) : sm4Ecb; ++ } else if (strcasecmp(algo, "sm4-cbc") == 0) { ++ return sm4Cbc == NULL ? sm4Cbc = EVP_get_cipherbyname(algo) : sm4Cbc; ++ } else if (strcasecmp(algo, "sm4-ctr") == 0) { ++ return sm4Ctr == NULL ? sm4Ctr = EVP_get_cipherbyname(algo) : sm4Ctr; ++ } else if (strcasecmp(algo, "sm4-ofb") == 0) { ++ return sm4Ofb == NULL ? sm4Ofb = EVP_get_cipherbyname(algo) : sm4Ofb; ++ } else { ++ KAE_ThrowRuntimeException(env, "EVPGetSm4CipherByName error"); ++ return 0; ++ } ++} ++ ++static const EVP_CIPHER* EVPGetAesCipherByName(JNIEnv* env, const char* algo) ++{ ++ static const EVP_CIPHER* aes128Ecb = NULL; ++ static const EVP_CIPHER* aes128Cbc = NULL; ++ static const EVP_CIPHER* aes128Ctr = NULL; ++ static const EVP_CIPHER* aes128Gcm = NULL; ++ static const EVP_CIPHER* aes192Ecb = NULL; ++ static const EVP_CIPHER* aes192Cbc = NULL; ++ static const EVP_CIPHER* aes192Ctr = NULL; ++ static const EVP_CIPHER* aes192Gcm = NULL; ++ static const EVP_CIPHER* aes256Ecb = NULL; ++ static const EVP_CIPHER* aes256Cbc = NULL; ++ static const EVP_CIPHER* aes256Ctr = NULL; ++ static const EVP_CIPHER* aes256Gcm = NULL; ++ ++ if (strcasecmp(algo, "aes-128-ecb") == 0) { ++ return aes128Ecb == NULL ? aes128Ecb = EVP_get_cipherbyname(algo) : aes128Ecb; ++ } else if (strcasecmp(algo, "aes-128-cbc") == 0) { ++ return aes128Cbc == NULL ? aes128Cbc = EVP_get_cipherbyname(algo) : aes128Cbc; ++ } else if (strcasecmp(algo, "aes-128-ctr") == 0) { ++ return aes128Ctr == NULL ? aes128Ctr = EVP_get_cipherbyname(algo) : aes128Ctr; ++ } else if (strcasecmp(algo, "aes-128-gcm") == 0) { ++ return aes128Gcm == NULL ? aes128Gcm = EVP_get_cipherbyname(algo) : aes128Gcm; ++ } else if (strcasecmp(algo, "aes-192-ecb") == 0) { ++ return aes192Ecb == NULL ? aes192Ecb = EVP_get_cipherbyname(algo) : aes192Ecb; ++ } else if (strcasecmp(algo, "aes-192-cbc") == 0) { ++ return aes192Cbc == NULL ? aes192Cbc = EVP_get_cipherbyname(algo) : aes192Cbc; ++ } else if (strcasecmp(algo, "aes-192-ctr") == 0) { ++ return aes192Ctr == NULL ? aes192Ctr = EVP_get_cipherbyname(algo) : aes192Ctr; ++ } else if (strcasecmp(algo, "aes-192-gcm") == 0) { ++ return aes192Gcm == NULL ? aes192Gcm = EVP_get_cipherbyname(algo) : aes192Gcm; ++ } else if (strcasecmp(algo, "aes-256-ecb") == 0) { ++ return aes256Ecb == NULL ? aes256Ecb = EVP_get_cipherbyname(algo) : aes256Ecb; ++ } else if (strcasecmp(algo, "aes-256-cbc") == 0) { ++ return aes256Cbc == NULL ? aes256Cbc = EVP_get_cipherbyname(algo) : aes256Cbc; ++ } else if (strcasecmp(algo, "aes-256-ctr") == 0) { ++ return aes256Ctr == NULL ? aes256Ctr = EVP_get_cipherbyname(algo) : aes256Ctr; ++ } else if (strcasecmp(algo, "aes-256-gcm") == 0) { ++ return aes256Gcm == NULL ? aes256Gcm = EVP_get_cipherbyname(algo) : aes256Gcm; ++ } else { ++ KAE_ThrowRuntimeException(env, "EVPGetAesCipherByName error"); ++ return 0; ++ } ++} ++ ++void FreeMemoryFromInit(JNIEnv* env, jbyteArray iv, jbyte* ivBytes, jbyteArray key, jbyte* keyBytes, ++ int keyLength) ++{ ++ if (ivBytes != NULL) { ++ (*env)->ReleaseByteArrayElements(env, iv, ivBytes, 0); ++ } ++ if (keyBytes != NULL) { ++ memset(keyBytes, 0, keyLength); ++ (*env)->ReleaseByteArrayElements(env, key, keyBytes, JNI_ABORT); ++ } ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAESymmetricCipherBase ++ * Method: nativeInit ++ * Signature: (Ljava/lang/String;Z[B[B)J ++ */ ++JNIEXPORT jlong JNICALL ++Java_org_openeuler_security_openssl_KAESymmetricCipherBase_nativeInit(JNIEnv* env, jclass cls, ++ jstring cipherType, jboolean encrypt, jbyteArray key, jbyteArray iv, jboolean padding) ++{ ++ EVP_CIPHER_CTX* ctx = NULL; ++ jbyte* keyBytes = NULL; ++ jbyte* ivBytes = NULL; ++ const EVP_CIPHER* cipher = NULL; ++ ENGINE* kaeEngine = NULL; ++ int keyLength = (*env)->GetArrayLength(env, key); ++ ++ const char* algo = (*env)->GetStringUTFChars(env, cipherType, 0); ++ if (StartsWith("aes", algo)) { ++ cipher = EVPGetAesCipherByName(env, algo); ++ kaeEngine = GetAesEngineByAlgorithmName(algo); ++ } else { ++ cipher = EVPGetSm4CipherByName(env, algo); ++ 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"); ++ goto cleanup; ++ } ++ if ((ctx = EVP_CIPHER_CTX_new()) == NULL) { ++ KAE_ThrowOOMException(env, "create EVP_CIPHER_CTX fail"); ++ goto cleanup; ++ } ++ ++ if (iv != NULL) { ++ ivBytes = (*env)->GetByteArrayElements(env, iv, NULL); ++ } ++ if (key != NULL) { ++ keyBytes = (*env)->GetByteArrayElements(env, key, NULL); ++ } ++ ++ if (!EVP_CipherInit_ex(ctx, cipher, kaeEngine, (const unsigned char*)keyBytes, ++ (const unsigned char*)ivBytes, encrypt ? 1 : 0)) { ++ KAE_ThrowFromOpenssl(env, "EVP_CipherInit_ex failed", KAE_ThrowRuntimeException); ++ goto cleanup; ++ } ++ ++ EVP_CIPHER_CTX_set_padding(ctx, padding ? 1 : 0); ++ ++ FreeMemoryFromInit(env, iv, ivBytes, key, keyBytes, keyLength); ++ return (jlong)ctx; ++ ++cleanup: ++ if (ctx != NULL) { ++ EVP_CIPHER_CTX_free(ctx); ++ } ++ FreeMemoryFromInit(env, iv, ivBytes, key, keyBytes, keyLength); ++ return 0; ++} ++ ++static void FreeMemoryFromUpdate(unsigned char* in, unsigned char* aad, unsigned char* out) ++{ ++ if (in != NULL) { ++ free(in); ++ } ++ if (out != NULL) { ++ free(out); ++ } ++ if (aad != NULL) { ++ free(aad); ++ } ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAESymmetricCipherBase ++ * Method: nativeUpdate ++ * Signature: (J[BII[BIZ[B)I ++ */ ++JNIEXPORT jint JNICALL ++Java_org_openeuler_security_openssl_KAESymmetricCipherBase_nativeUpdate(JNIEnv* env, jclass cls, jlong ctxAddress, ++ jbyteArray inArr, jint inOfs, jint inLen, jbyteArray outArr, jint outOfs, jboolean gcm, jbyteArray gcmAAD) ++{ ++ unsigned char* in = NULL; ++ unsigned char* aad = NULL; ++ unsigned char* out = NULL; ++ ++ EVP_CIPHER_CTX* ctx = (EVP_CIPHER_CTX*)ctxAddress; ++ if (ctx == NULL || inArr == NULL || outArr == NULL) { ++ goto cleanup; ++ } ++ ++ in = (unsigned char*)malloc(inLen); ++ int outLen = (*env)->GetArrayLength(env, outArr) - outOfs; ++ out = (unsigned char*)malloc(outLen); ++ if (in == NULL || out == NULL) { ++ KAE_ThrowOOMException(env, "malloc error"); ++ goto cleanup; ++ } ++ memset(in, 0, inLen); ++ memset(out, 0, outLen); ++ (*env)->GetByteArrayRegion(env, inArr, inOfs, inLen, (jbyte*)in); ++ ++ int bytesWritten = 0; ++ if (gcm && (gcmAAD != NULL)) { ++ int aadLen = (*env)->GetArrayLength(env, gcmAAD); ++ if ((aad = (unsigned char*)malloc(aadLen)) == NULL) { ++ KAE_ThrowOOMException(env, "malloc error"); ++ goto cleanup; ++ } ++ memset(aad, 0, aadLen); ++ (*env)->GetByteArrayRegion(env, gcmAAD, 0, aadLen, (jbyte*)aad); ++ ++ // Specify aad. ++ if (EVP_CipherUpdate(ctx, NULL, &bytesWritten, aad, aadLen) == 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_CipherUpdate failed", KAE_ThrowRuntimeException); ++ goto cleanup; ++ } ++ } ++ ++ if (EVP_CipherUpdate(ctx, out, &bytesWritten, in, inLen) == 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_CipherUpdate failed", KAE_ThrowRuntimeException); ++ goto cleanup; ++ } ++ (*env)->SetByteArrayRegion(env, outArr, outOfs, bytesWritten, (jbyte*)out); ++ ++ FreeMemoryFromUpdate(in, aad, out); ++ return bytesWritten; ++ ++cleanup: ++ FreeMemoryFromUpdate(in, aad, out); ++ return 0; ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAESymmetricCipherBase ++ * Method: nativeFinal ++ * Signature: (JZ[BI)I ++ */ ++JNIEXPORT jint JNICALL ++Java_org_openeuler_security_openssl_KAESymmetricCipherBase_nativeFinal(JNIEnv* env, jclass cls, ++ jlong ctxAddress, jbyteArray outArr, jint outOfs) ++{ ++ unsigned char* out = NULL; ++ EVP_CIPHER_CTX* ctx = (EVP_CIPHER_CTX*)ctxAddress; ++ KAE_TRACE("KAESymmetricCipherBase_nativeFinal(ctxAddress = %p, outArr = %p, outOfs = %d)", ++ ctx, outArr, outOfs); ++ if (ctx == NULL || outArr == NULL) { ++ goto cleanup; ++ } ++ int outLen = (*env)->GetArrayLength(env, outArr) - outOfs; ++ out = (unsigned char*)malloc(outLen); ++ if (out == NULL) { ++ KAE_ThrowOOMException(env, "malloc error"); ++ goto cleanup; ++ } ++ memset(out, 0, outLen); ++ int bytesWritten = 0; ++ int result_code = EVP_CipherFinal_ex(ctx, out, &bytesWritten); ++ if (result_code == 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_CipherFinal_ex failed", KAE_ThrowBadPaddingException); ++ goto cleanup; ++ } ++ KAE_TRACE("KAESymmetricCipherBase_nativeFinal EVP_CipherFinal_ex success, bytesWritten = %d", bytesWritten); ++ (*env)->SetByteArrayRegion(env, outArr, outOfs, bytesWritten, (jbyte*)out); ++ free(out); ++ KAE_TRACE("KAESymmetricCipherBase_nativeFinal: finished"); ++ return bytesWritten; ++ ++cleanup: ++ if (out != NULL) { ++ free(out); ++ } ++ return 0; ++} ++ ++static void FreeMemoryFromFinalGcm(unsigned char* out, unsigned char* gcmTag, unsigned char* gcmOut) ++{ ++ if (out != NULL) { ++ free(out); ++ } ++ if (gcmTag != NULL) { ++ free(gcmTag); ++ } ++ if (gcmOut != NULL) { ++ free(gcmOut); ++ } ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAECipherAES ++ * Method: nativeFinalGcm ++ * Signature: (J[BIZI[BZ)I ++ */ ++JNIEXPORT jint JNICALL Java_org_openeuler_security_openssl_KAESymmetricCipherBase_nativeFinalGcm(JNIEnv* env, ++ jclass cls, jlong ctxAddress, jbyteArray outArr, jint outOfs, jboolean gcm, jint tagLength, ++ jbyteArray gcmTagArr, jboolean encrypt) ++{ ++ unsigned char* out = NULL; ++ unsigned char* gcmTag = NULL; ++ unsigned char* gcmOut = NULL; ++ ++ EVP_CIPHER_CTX* ctx = (EVP_CIPHER_CTX*)ctxAddress; ++ if (ctx == NULL || outArr == NULL) { ++ goto cleanup; ++ } ++ ++ int bytesWritten = 0; ++ if (encrypt) { ++ int outLen = (*env)->GetArrayLength(env, outArr) - outOfs; ++ if ((out = malloc(outLen)) == NULL) { ++ KAE_ThrowOOMException(env, "malloc error"); ++ goto cleanup; ++ } ++ memset(out, 0, outLen); ++ if (EVP_CipherFinal_ex(ctx, out, &bytesWritten) == 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_CipherFinal_ex failed", KAE_ThrowBadPaddingException); ++ goto cleanup; ++ } ++ (*env)->SetByteArrayRegion(env, outArr, outOfs, bytesWritten, (jbyte*)out); ++ ++ // Writes tagLength bytes of the tag value to the buffer. ++ // Refer to {@link https://www.openssl.org/docs/man1.1.0/man3/EVP_CIPHER_CTX_ctrl.html} for details. ++ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tagLength, out + bytesWritten) == 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_CIPHER_CTX_ctrl failed", KAE_ThrowRuntimeException); ++ goto cleanup; ++ } ++ (*env)->SetByteArrayRegion(env, outArr, outOfs + bytesWritten, tagLength, (jbyte*)(out + bytesWritten)); ++ bytesWritten += tagLength; ++ } else { ++ // gcmOut is the plaintext that has been decrypted in the EVP_CipherUpdate. ++ // outOfs is the length of the gcmOut, where it's always > 0. ++ if ((gcmTag = (unsigned char*)malloc(tagLength)) == NULL || (gcmOut = (unsigned char*)malloc(outOfs)) == NULL) { ++ KAE_ThrowOOMException(env, "malloc error"); ++ goto cleanup; ++ } ++ memset(gcmTag, 0, tagLength); ++ memset(gcmOut, 0, outOfs); ++ ++ (*env)->GetByteArrayRegion(env, gcmTagArr, 0, tagLength, (jbyte*)gcmTag); ++ // Sets the expected gcmTag to tagLength bytes from gcmTag. ++ // Refer to {@link https://www.openssl.org/docs/man1.1.0/man3/EVP_CIPHER_CTX_ctrl.html} for details. ++ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tagLength, gcmTag) == 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_CTRL_AEAD_SET_TAG failed", KAE_ThrowRuntimeException); ++ goto cleanup; ++ } ++ ++ (*env)->GetByteArrayRegion(env, outArr, 0, outOfs, (jbyte*)gcmOut); ++ // Finalise: note get no output for GCM ++ if (EVP_CipherFinal_ex(ctx, gcmOut, &bytesWritten) == 0) { ++ KAE_ThrowFromOpenssl(env, "EVP_CipherFinal_ex failed", KAE_ThrowAEADBadTagException); ++ goto cleanup; ++ } ++ } ++ FreeMemoryFromFinalGcm(out, gcmTag, gcmOut); ++ ++ return bytesWritten; ++ ++cleanup: ++ FreeMemoryFromFinalGcm(out, gcmTag, gcmOut); ++ return 0; ++} ++ ++/* ++ * Class: org_openeuler_security_openssl_KAESymmetricCipherBase ++ * Method: nativeFree ++ * Signature: (J)V ++ */ ++JNIEXPORT void JNICALL ++Java_org_openeuler_security_openssl_KAESymmetricCipherBase_nativeFree(JNIEnv* env, jclass cls, jlong ctxAddress) ++{ ++ EVP_CIPHER_CTX* ctx = (EVP_CIPHER_CTX*)ctxAddress; ++ KAE_TRACE("KAESymmetricCipherBase_nativeFree(ctx = %p)", ctx); ++ if (ctx != NULL) { ++ EVP_CIPHER_CTX_free(ctx); ++ ctx = NULL; ++ } ++ ++ KAE_TRACE("KAESymmetricCipherBase_nativeFree: finished"); ++} +diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_util.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_util.c +new file mode 100644 +index 000000000..471ae834b +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_util.c +@@ -0,0 +1,246 @@ ++/* ++ * Copyright (c) 2023, 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 ++#include ++#include "kae_util.h" ++#include "kae_exception.h" ++ ++static ENGINE* kaeEngine = NULL; ++ ++void SetKaeEngine(ENGINE* engine) { ++ kaeEngine = engine; ++} ++ ++ENGINE* GetKaeEngine() { ++ return kaeEngine; ++} ++ ++BIGNUM* KAE_GetBigNumFromByteArray(JNIEnv* env, jbyteArray byteArray) { ++ if (byteArray == NULL) { ++ KAE_ThrowNullPointerException(env, "KAE_GetBigNumFromByteArray byteArray is null"); ++ return NULL; ++ } ++ ++ jsize len = (*env)->GetArrayLength(env, byteArray); ++ if (len == 0) { ++ KAE_ThrowRuntimeException(env, "KAE_GetBigNumFromByteArray byteArray is empty"); ++ return NULL; ++ } ++ ++ BIGNUM* bn = BN_new(); ++ if (bn == NULL) { ++ KAE_ThrowFromOpenssl(env, "BN_new", KAE_ThrowRuntimeException); ++ return NULL; ++ } ++ ++ jbyte* bytes = (*env)->GetByteArrayElements(env, byteArray, NULL); ++ if (bytes == NULL) { ++ KAE_ThrowNullPointerException(env,"GetByteArrayElements failed"); ++ goto cleanup; ++ } ++ BIGNUM* result = BN_bin2bn((const unsigned char*) bytes, len, bn); ++ (*env)->ReleaseByteArrayElements(env, byteArray, bytes, 0); ++ if (result == NULL) { ++ KAE_ThrowFromOpenssl(env, "BN_bin2bn", KAE_ThrowRuntimeException); ++ goto cleanup; ++ } ++ return bn; ++ ++cleanup: ++ BN_free(bn); ++ return NULL; ++} ++ ++void KAE_ReleaseBigNumFromByteArray(BIGNUM* bn) { ++ if (bn != NULL) { ++ BN_free(bn); ++ } ++} ++ ++jbyteArray KAE_GetByteArrayFromBigNum(JNIEnv* env, const BIGNUM* bn) { ++ if (bn == NULL) { ++ return NULL; ++ } ++ // bn size need plus 1, for example 65535 , BN_num_bytes return 2 ++ int bnSize = BN_num_bytes(bn); ++ if (bnSize <= 0) { ++ return NULL; ++ } ++ bnSize += 1; ++ jbyteArray javaBytes = (*env)->NewByteArray(env, bnSize); ++ if (javaBytes == NULL) { ++ KAE_ThrowOOMException(env, "New byte array failed."); ++ return NULL; ++ } ++ jbyte* bytes = (*env)->GetByteArrayElements(env, javaBytes, NULL); ++ if (bytes == NULL) { ++ KAE_ThrowNullPointerException(env,"GetByteArrayElements failed."); ++ return NULL; ++ } ++ unsigned char* tmp = (unsigned char*) bytes; ++ if (BN_bn2bin(bn, tmp + 1) <= 0) { ++ KAE_ThrowFromOpenssl(env, "BN_bn2bin", KAE_ThrowRuntimeException); ++ javaBytes = NULL; ++ goto cleanup; ++ } ++ (*env)->SetByteArrayRegion(env, javaBytes, 0, bnSize, bytes); ++ ++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/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_util.h b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_util.h +new file mode 100644 +index 000000000..6eb980d62 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_util.h +@@ -0,0 +1,94 @@ ++/* ++ * Copyright (c) 2023, 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 KAE_UTIL_H ++#define KAE_UTIL_H ++ ++#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); ++ ++/* release BIGNUM allocat from */ ++void KAE_ReleaseBigNumFromByteArray(BIGNUM* bn); ++ ++/* BIGNUM convert to jbyteArray */ ++jbyteArray KAE_GetByteArrayFromBigNum(JNIEnv* env, const BIGNUM* bn); ++ ++void SetKaeEngine(ENGINE* engine); ++ ++ENGINE* GetKaeEngine(); ++ ++void initEngines(JNIEnv* env, jbooleanArray algorithmKaeFlags); ++ ++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/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/openssl_ad.h b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/openssl_ad.h +new file mode 100644 +index 000000000..f4b552137 +--- /dev/null ++++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/openssl_ad.h +@@ -0,0 +1,10 @@ ++#include ++#include ++ ++#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 ++ ++// ERR_GET_FUNC() was removed since Openssl3 ++#ifndef ERR_GET_FUNC ++#define ERR_GET_FUNC(e) (int)(((e) >> 12L) & 0xFFFL) ++#endif ++#endif +diff --git a/test/jdk/TEST.groups b/test/jdk/TEST.groups +index 386ea4160..338234faf 100644 +--- a/test/jdk/TEST.groups ++++ b/test/jdk/TEST.groups +@@ -255,6 +255,9 @@ jdk_security = \ + jdk_security_infra = \ + security/infra/java/security/cert/CertPathValidator/certification + ++jdk_kae_security = \ ++ org/openeuler/security/openssl ++ + jdk_text = \ + java/text \ + sun/text +diff --git a/test/jdk/java/security/KeyAgreement/KeyAgreementTest.java b/test/jdk/java/security/KeyAgreement/KeyAgreementTest.java +index 1e5eb9680..398e3ea2a 100644 +--- a/test/jdk/java/security/KeyAgreement/KeyAgreementTest.java ++++ b/test/jdk/java/security/KeyAgreement/KeyAgreementTest.java +@@ -39,6 +39,7 @@ import java.security.KeyPairGenerator; + import java.security.spec.NamedParameterSpec; + import java.security.spec.AlgorithmParameterSpec; + import java.security.spec.ECGenParameterSpec; ++import java.security.Security; + import java.util.ArrayList; + import java.util.Arrays; + import java.util.List; +@@ -53,7 +54,13 @@ public class KeyAgreementTest { + String kpgAlgo = args[1]; + String provider = System.getProperty("test.provider.name", args[2]); + System.out.println("Testing " + kaAlgo); +- AlgoSpec aSpec = AlgoSpec.valueOf(AlgoSpec.class, kaAlgo); ++ AlgoSpec aSpec; ++ if (Security.getProperty("security.provider.1").equals("KAEProvider") && ++ kaAlgo.equals("ECDH")) { ++ aSpec = AlgoSpec.valueOf(AlgoSpec.class, "KAEECDH"); ++ } else { ++ aSpec = AlgoSpec.valueOf(AlgoSpec.class, kaAlgo); ++ } + List specs = aSpec.getAlgorithmParameterSpecs(); + for (AlgorithmParameterSpec spec : specs) { + testKeyAgreement(provider, kaAlgo, kpgAlgo, spec); +@@ -70,6 +77,7 @@ public class KeyAgreementTest { + // "java.base/share/classes/sun/security/util/CurveDB.java" + + ECDH("secp256r1", "secp384r1", "secp521r1"), ++ KAEECDH("secp224r1", "secp256r1", "secp384r1", "secp521r1"), + XDH("X25519", "X448", "x25519"), + // There is no curve for DiffieHellman + DiffieHellman(new String[]{}); +@@ -81,6 +89,7 @@ public class KeyAgreementTest { + for (String crv : curves) { + switch (this.name()) { + case "ECDH": ++ case "KAEECDH": + specs.add(new ECGenParameterSpec(crv)); + break; + case "XDH": +diff --git a/test/jdk/java/security/Signature/SignatureGetInstance.java b/test/jdk/java/security/Signature/SignatureGetInstance.java +index 821c20602..536c3ba67 100644 +--- a/test/jdk/java/security/Signature/SignatureGetInstance.java ++++ b/test/jdk/java/security/Signature/SignatureGetInstance.java +@@ -54,9 +54,13 @@ public class SignatureGetInstance { + MyPrivKey testPriv = new MyPrivKey(); + MyPubKey testPub = new MyPubKey(); + ++ Provider kaeProvider = Security.getProvider("KAEProvider"); ++ String expectedProvName = kaeProvider != null ? "KAEProvider" : "SunRsaSign"; ++ ++ + testDblInit(testPriv, testPub, true, "TestProvider"); + testDblInit(kp.getPrivate(), kp.getPublic(), true, +- System.getProperty("test.provider.name", "SunRsaSign")); ++ System.getProperty("test.provider.name", expectedProName)); + testDblInit(testPriv, kp.getPublic(), false, null); + testDblInit(kp.getPrivate(), testPub, false, null); + +diff --git a/test/jdk/org/openeuler/security/openssl/AESTest.java b/test/jdk/org/openeuler/security/openssl/AESTest.java +new file mode 100644 +index 000000000..aac14568d +--- /dev/null ++++ b/test/jdk/org/openeuler/security/openssl/AESTest.java +@@ -0,0 +1,115 @@ ++/* ++ * Copyright (c) 2023, 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 ++ * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl ++ * @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/test/jdk/org/openeuler/security/openssl/DHTest.java b/test/jdk/org/openeuler/security/openssl/DHTest.java +new file mode 100644 +index 000000000..51dab7664 +--- /dev/null ++++ b/test/jdk/org/openeuler/security/openssl/DHTest.java +@@ -0,0 +1,122 @@ ++/* ++ * Copyright (c) 2023, 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. 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. ++ */ ++ ++import java.io.Serializable; ++import java.math.BigInteger; ++import java.security.*; ++import java.util.Arrays; ++import javax.crypto.KeyAgreement; ++import javax.crypto.spec.*; ++import org.openeuler.security.openssl.KAEProvider; ++ ++/** ++ * This class implements the Diffie-Hellman key exchange algorithm. ++ * D-H means combining your private key with your partners public key to ++ * generate a number. The peer does the same with its private key and our ++ * public key. Through the magic of Diffie-Hellman we both come up with the ++ * same number. This number is secret (discounting MITM attacks) and hence ++ * called the shared secret. It has the same length as the modulus, e.g. 512 ++ * or 1024 bit. Man-in-the-middle attacks are typically countered by an ++ * independent authentication step using certificates (RSA, DSA, etc.). ++ * ++ * The thing to note is that the shared secret is constant for two partners ++ * with constant private keys. This is often not what we want, which is why ++ * it is generally a good idea to create a new private key for each session. ++ * Generating a private key involves one modular exponentiation assuming ++ * suitable D-H parameters are available. ++ * ++ * General usage of this class (TLS DHE case): ++ * . if we are server, call DHCrypt(keyLength,random). This generates ++ * an ephemeral keypair of the request length. ++ * . if we are client, call DHCrypt(modulus, base, random). This ++ * generates an ephemeral keypair using the parameters specified by ++ * the server. ++ * . send parameters and public value to remote peer ++ * . receive peers ephemeral public key ++ * . call getAgreedSecret() to calculate the shared secret ++ * ++ * In TLS the server chooses the parameter values itself, the client must use ++ * those sent to it by the server. ++ * ++ * The use of ephemeral keys as described above also achieves what is called ++ * "forward secrecy". This means that even if the authentication keys are ++ * broken at a later date, the shared secret remains secure. The session is ++ * compromised only if the authentication keys are already broken at the ++ * time the key exchange takes place and an active MITM attack is used. ++ * This is in contrast to straightforward encrypting RSA key exchanges. ++ * ++ */ ++ ++/** ++ * @test ++ * @summary Basic test for DH ++ * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl ++ * @requires os.arch=="aarch64" ++ * @run main DHTest ++ */ ++ ++public class DHTest implements Serializable { ++ private static BigInteger g512; ++ private static BigInteger p512; ++ ++ private static volatile Provider sunJceProvider; ++ private static volatile Provider kaeProvider; ++ ++ public static void main(String[] args) throws Exception { ++ Security.addProvider(new KAEProvider()); ++ sunJceProvider = Security.getProvider("SunJCE"); ++ kaeProvider = Security.getProvider("KAEProvider"); ++ ++ g512 = new BigInteger("30270326776012916323988175351831539351616124181011347910931933302640902603480679235129557617566480716138395926949700593986872757726720164601940036524221141391913433558162442022339559255078339658108149162251643458301671465579040759659507434340437396584664407572026953757806341363255195983514333141770938654900099033797866272818739547343977583089845850158637618703095710047154252655157633638171416516716598940884520592858762209135010804267830977334033327483815694794951984230264309784679409488441905236794443014066406150649287037909246107758452315504212879842042858577191624250834553614056794526338841821045329189780334"); ++ ++ p512 = new BigInteger("27672987386729926592037876826877634387173876890702920770064392919138769821035856568775311919542560094764667151024449425954917954337048895981297730855891532066350935045229294626339548842381843985759061682551900379979643117695834175891578650111093016914264824311693147701566019122696621248493126219217339690346346921463135605151471303957324058301097079967414639146647429422884520134312590056632178576758580657240245655739869017244657144448267757255018625514803292549109401806336918448001843022629625467069714240279603204909633404992842479161100500474744098408277938070656334892106100534117209709263785505019003765693651"); ++ ++ DHParameterSpec dhParams = new DHParameterSpec(p512, g512); ++ KeyPairGenerator SunJCEkeyGen = KeyPairGenerator.getInstance("DH", sunJceProvider); ++ KeyPairGenerator KAEkeyGen = KeyPairGenerator.getInstance("DH", kaeProvider); ++ SunJCEkeyGen.initialize(dhParams, new SecureRandom()); ++ KAEkeyGen.initialize(dhParams, new SecureRandom()); ++ KeyAgreement aKeyAgree = KeyAgreement.getInstance("DH", sunJceProvider); ++ KeyPair aPair = SunJCEkeyGen.generateKeyPair(); ++ KeyAgreement bKeyAgree = KeyAgreement.getInstance("DH", kaeProvider); ++ KeyPair bPair = KAEkeyGen.generateKeyPair(); ++ ++ aKeyAgree.init(aPair.getPrivate()); ++ bKeyAgree.init(bPair.getPrivate()); ++ ++ aKeyAgree.doPhase(bPair.getPublic(), true); ++ bKeyAgree.doPhase(aPair.getPublic(), true); ++ ++ MessageDigest hash = MessageDigest.getInstance("MD5"); ++ byte[] b1 = hash.digest(aKeyAgree.generateSecret()); ++ byte[] b2 = hash.digest(bKeyAgree.generateSecret()); ++ if(Arrays.equals(b1, b2)){ ++ System.out.println("SUCCESS!"); ++ }else{ ++ System.out.println("Failed!"); ++ throw new RuntimeException("Not Equal DH keyagreement ouput from SunJCE and KAE Provider!"); ++ } ++ } ++} +\ No newline at end of file +diff --git a/test/jdk/org/openeuler/security/openssl/DigestTest.java b/test/jdk/org/openeuler/security/openssl/DigestTest.java +new file mode 100644 +index 000000000..120b6ac47 +--- /dev/null ++++ b/test/jdk/org/openeuler/security/openssl/DigestTest.java +@@ -0,0 +1,61 @@ ++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 ++ * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl ++ * @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/test/jdk/org/openeuler/security/openssl/ECDHTest.java b/test/jdk/org/openeuler/security/openssl/ECDHTest.java +new file mode 100644 +index 000000000..791fec252 +--- /dev/null ++++ b/test/jdk/org/openeuler/security/openssl/ECDHTest.java +@@ -0,0 +1,114 @@ ++/* ++ * Copyright (c) 2023, 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 sun.security.ec.ECPrivateKeyImpl; ++import sun.security.ec.ECPublicKeyImpl; ++ ++import javax.crypto.KeyAgreement; ++import java.math.BigInteger; ++import java.security.KeyPair; ++import java.security.KeyPairGenerator; ++import java.security.Provider; ++import java.security.Security; ++import java.security.spec.ECFieldFp; ++import java.security.spec.ECParameterSpec; ++import java.security.spec.ECPoint; ++import java.security.spec.EllipticCurve; ++import java.util.Arrays; ++import java.nio.charset.StandardCharsets; ++import java.security.spec.*; ++import java.security.KeyFactory; ++import java.security.interfaces.ECPrivateKey; ++import java.security.interfaces.ECPublicKey; ++ ++/** ++ * @test ++ * @summary Basic test for ECDH ++ * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl ++ * @modules jdk.crypto.ec/sun.security.ec ++ * @requires os.arch=="aarch64" ++ * @run main ECDHTest ++ */ ++ ++public class ECDHTest { ++ ++ private static KeyPairGenerator keyPairGenerator; ++ private static String algorithm = "EC"; ++ private static int[] keyArr = {224, 256, 384, 521}; ++ ++ public static void main(String[] args) throws Exception { ++ Security.insertProviderAt(new KAEProvider(), 1); ++ ++ BigInteger a = new BigInteger("26959946667150639794667015087019630673557916260026308143510066298878"); ++ BigInteger b = new BigInteger("18958286285566608000408668544493926415504680968679321075787234672564"); ++ BigInteger p = new BigInteger("26959946667150639794667015087019630673557916260026308143510066298881"); ++ BigInteger x = new BigInteger("19277929113566293071110308034699488026831934219452440156649784352033"); ++ BigInteger y = new BigInteger("19926808758034470970197974370888749184205991990603949537637343198772"); ++ EllipticCurve CURVE = new EllipticCurve(new ECFieldFp(p), a, b); ++ ECPoint POINT = new ECPoint(x, y); ++ BigInteger ORDER = new BigInteger("26959946667150639794667015087019625940457807714424391721682722368061"); ++ int COFACTOR = 1; ++ ECParameterSpec PARAMS = new ECParameterSpec(CURVE, POINT, ORDER, COFACTOR); ++ ++ testKeyPairByParam(PARAMS); ++ for (int keySize : keyArr) { ++ testKeyPairByKeySize(keySize); ++ } ++ ++ KeyFactory keyFactory = KeyFactory.getInstance("EC"); ++ ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger("20135071615800221517902437867016717688420688735490569283842831828983"), PARAMS); ++ ECPrivateKeyImpl ecPrivKey = (ECPrivateKeyImpl)keyFactory.generatePrivate(privateKeySpec); ++ ++ ECPoint ecPoint = new ECPoint(new BigInteger("9490267631555585552004372465967099662885480699902812460349461311384"), new BigInteger("1974573604976093871117393045089050409882519645527397292712281520811")); ++ ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(ecPoint,PARAMS); ++ ECPublicKeyImpl ecPublicKey = (ECPublicKeyImpl)keyFactory.generatePublic(publicKeySpec); ++ testKeyAgreement(ecPrivKey, ecPublicKey, new byte[]{-88, -65, 43, -84, 26, 43, 46, 106, 20, 39, -76, 30, -71, 72, -102, 120, 108, -92, -86, -14, -96, -42, 93, -40, -43, -25, 15, -62}); ++ } ++ ++ public static void testKeyPairByParam(ECParameterSpec PARAMS) throws Exception { ++ keyPairGenerator = KeyPairGenerator.getInstance(algorithm); ++ keyPairGenerator.initialize(PARAMS); ++ KeyPair keyPair = keyPairGenerator.generateKeyPair(); ++ ECPrivateKeyImpl ecPrivKey = (ECPrivateKeyImpl) keyPair.getPrivate(); ++ ECPublicKeyImpl ecPublicKey = (ECPublicKeyImpl) keyPair.getPublic(); ++ } ++ ++ public static void testKeyPairByKeySize(int keySize) throws Exception { ++ keyPairGenerator = KeyPairGenerator.getInstance(algorithm); ++ keyPairGenerator.initialize(keySize); ++ KeyPair keyPair = keyPairGenerator.generateKeyPair(); ++ ECPrivateKeyImpl ecPrivKey = (ECPrivateKeyImpl) keyPair.getPrivate(); ++ ECPublicKeyImpl ecPublicKey = (ECPublicKeyImpl) keyPair.getPublic(); ++ } ++ ++ public static void testKeyAgreement(ECPrivateKeyImpl ecPrivKey, ECPublicKeyImpl ecPublicKey, byte[] expectRes) throws Exception { ++ KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH"); ++ keyAgreement.init(ecPrivKey); ++ keyAgreement.doPhase(ecPublicKey, true); ++ byte[] res = keyAgreement.generateSecret(); ++ if (!Arrays.equals(res, expectRes)) { ++ throw new RuntimeException("keyagreement failed"); ++ } ++ } ++} +\ No newline at end of file +diff --git a/test/jdk/org/openeuler/security/openssl/HmacTest.java b/test/jdk/org/openeuler/security/openssl/HmacTest.java +new file mode 100644 +index 000000000..bddc37b4c +--- /dev/null ++++ b/test/jdk/org/openeuler/security/openssl/HmacTest.java +@@ -0,0 +1,89 @@ ++/* ++ * Copyright (c) 2023, 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 ++ * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl ++ * @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/test/jdk/org/openeuler/security/openssl/KAEConfTest.java b/test/jdk/org/openeuler/security/openssl/KAEConfTest.java +new file mode 100644 +index 000000000..4076aa2fc +--- /dev/null ++++ b/test/jdk/org/openeuler/security/openssl/KAEConfTest.java +@@ -0,0 +1,122 @@ ++/* ++ * Copyright (c) 2023, 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 ++ * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl ++ * @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 + "conf" + 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/test/jdk/org/openeuler/security/openssl/KAEDisabledAlgorithmsTest.java b/test/jdk/org/openeuler/security/openssl/KAEDisabledAlgorithmsTest.java +new file mode 100644 +index 000000000..e8d767f0c +--- /dev/null ++++ b/test/jdk/org/openeuler/security/openssl/KAEDisabledAlgorithmsTest.java +@@ -0,0 +1,165 @@ ++/* ++ * Copyright (c) 2023, 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 ++ * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl ++ * @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/test/jdk/org/openeuler/security/openssl/KAEEngineIdTest.java b/test/jdk/org/openeuler/security/openssl/KAEEngineIdTest.java +new file mode 100644 +index 000000000..75248a432 +--- /dev/null ++++ b/test/jdk/org/openeuler/security/openssl/KAEEngineIdTest.java +@@ -0,0 +1,77 @@ ++/* ++ * Copyright (c) 2023, 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 ++ * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl ++ * @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/test/jdk/org/openeuler/security/openssl/KAELogTest.java b/test/jdk/org/openeuler/security/openssl/KAELogTest.java +new file mode 100644 +index 000000000..f635d96f3 +--- /dev/null ++++ b/test/jdk/org/openeuler/security/openssl/KAELogTest.java +@@ -0,0 +1,127 @@ ++/* ++ * Copyright (c) 2023, 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 ++ * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl ++ * @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/test/jdk/org/openeuler/security/openssl/KAETestHelper.java b/test/jdk/org/openeuler/security/openssl/KAETestHelper.java +new file mode 100644 +index 000000000..59ad91ddc +--- /dev/null ++++ b/test/jdk/org/openeuler/security/openssl/KAETestHelper.java +@@ -0,0 +1,209 @@ ++/* ++ * Copyright (c) 2023, 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/test/jdk/org/openeuler/security/openssl/KAEUseEngineTest.java b/test/jdk/org/openeuler/security/openssl/KAEUseEngineTest.java +new file mode 100644 +index 000000000..a5b9b5386 +--- /dev/null ++++ b/test/jdk/org/openeuler/security/openssl/KAEUseEngineTest.java +@@ -0,0 +1,263 @@ ++/* ++ * Copyright (c) 2023, 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 ++ * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl ++ * @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)); ++ } ++ } ++} +\ No newline at end of file +diff --git a/test/jdk/org/openeuler/security/openssl/KaeDebugLogTest.java b/test/jdk/org/openeuler/security/openssl/KaeDebugLogTest.java +new file mode 100644 +index 000000000..7bffbd6ec +--- /dev/null ++++ b/test/jdk/org/openeuler/security/openssl/KaeDebugLogTest.java +@@ -0,0 +1,89 @@ ++/* ++ * Copyright (c) 2023, 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 ++ * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl ++ * @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/test/jdk/org/openeuler/security/openssl/KaeProviderTest.java b/test/jdk/org/openeuler/security/openssl/KaeProviderTest.java +new file mode 100644 +index 000000000..0f4425b6d +--- /dev/null ++++ b/test/jdk/org/openeuler/security/openssl/KaeProviderTest.java +@@ -0,0 +1,171 @@ ++/* ++ * Copyright (c) 2023, 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" ++ * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl ++ * @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!"); ++ } ++ } ++ } ++} +\ No newline at end of file +diff --git a/test/jdk/org/openeuler/security/openssl/RSATest.java b/test/jdk/org/openeuler/security/openssl/RSATest.java +new file mode 100644 +index 000000000..8a787e1f7 +--- /dev/null ++++ b/test/jdk/org/openeuler/security/openssl/RSATest.java +@@ -0,0 +1,115 @@ ++/* ++ * Copyright (c) 2023, 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 ++ * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl ++ * @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); ++ } ++ } ++} +\ No newline at end of file +diff --git a/test/jdk/org/openeuler/security/openssl/SM3Test.java b/test/jdk/org/openeuler/security/openssl/SM3Test.java +new file mode 100644 +index 000000000..e1c5346c9 +--- /dev/null ++++ b/test/jdk/org/openeuler/security/openssl/SM3Test.java +@@ -0,0 +1,55 @@ ++/* ++ * Copyright (c) 2023, 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 ++ * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl ++ * @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/test/jdk/org/openeuler/security/openssl/SM4Test.java b/test/jdk/org/openeuler/security/openssl/SM4Test.java +new file mode 100644 +index 000000000..ac81831ba +--- /dev/null ++++ b/test/jdk/org/openeuler/security/openssl/SM4Test.java +@@ -0,0 +1,153 @@ ++/* ++ * Copyright (c) 2023, 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.ByteBuffer; ++import java.nio.charset.StandardCharsets; ++import java.util.Arrays; ++import java.security.Security; ++import javax.crypto.Cipher; ++import javax.crypto.spec.IvParameterSpec; ++import javax.crypto.spec.SecretKeySpec; ++ ++/** ++ * @test ++ * @summary Basic test for sm4 ++ * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl ++ * @run main SM4Test ++ */ ++ ++public class SM4Test { ++ ++ private static SecretKeySpec ks = new SecretKeySpec("sm4EncryptionKey".getBytes(StandardCharsets.UTF_8), "SM4"); // key has 16 bytes ++ private static IvParameterSpec iv = new IvParameterSpec("abcdefghabcdefgh".getBytes(StandardCharsets.UTF_8)); // iv has 16 bytes ++ private static IvParameterSpec shortIv = new IvParameterSpec("abcdefgh".getBytes(StandardCharsets.UTF_8)); // CTR support >= 8bytes iv ++ 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); ++ test(plainText, "SM4/CBC/NOPADDING", new byte[]{86, 69, 47, -115, -63, 54, 35, 24, -2, 114, 113, 102, 82, 20, 69, 59}); ++ test(shortPlainText, "SM4/CBC/PKCS5Padding", new byte[]{10, 105, 75, -80, -85, -68, 13, -53, 42, 91, -64, 99, 104, 35, -85, 8}); ++ test(plainText, "SM4/ECB/NOPADDING", new byte[]{103, 36, -31, -53, -109, -12, -71, -79, -54, 106, 10, -3, -35, -22, -122, -67}); ++ test(shortPlainText, "SM4/ECB/PKCS5Padding", new byte[]{-10, 99, -9, 90, 58, -36, -109, 54, -55, -52, 7, -49, 110, -88, 72, 40}); ++ test(plainText, "SM4/CTR/NOPADDING", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110, -100, -113, -46, -29, -11, 71}); ++ test(plainText, "SM4/OFB/NOPADDING", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110, -100, -113, -46, -29, -11, 71}); ++ 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 { ++ Cipher encryptCipher = Cipher.getInstance(algo); ++ if (algo.contains("ECB")) { ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks); ++ } else { ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks, iv); ++ } ++ byte[] cipherText = encryptCipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)); ++ if (!Arrays.equals(cipherText, expectRes)) { ++ throw new RuntimeException("sm4 encryption failed, algo = " + algo); ++ } ++ ++ Cipher decryptCipher = Cipher.getInstance(algo); ++ decryptCipher.init(Cipher.DECRYPT_MODE, ks, encryptCipher.getParameters()); ++ String decryptPlainText = new String(decryptCipher.doFinal(cipherText)); ++ if (!plainText.equals(decryptPlainText)) { ++ throw new RuntimeException("sm4 decryption failed, algo = " + algo); ++ } ++ } ++ ++ public static void testCtrShortIv(String plainText, String algo, byte[] expectRes) throws Exception { ++ Cipher encryptCipher = Cipher.getInstance(algo); ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks, shortIv); ++ byte[] cipherText = encryptCipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)); ++ if (!Arrays.equals(cipherText, expectRes)) { ++ throw new RuntimeException("sm4 encryption failed, algo = " + algo); ++ } ++ ++ Cipher decryptCipher = Cipher.getInstance(algo); ++ decryptCipher.init(Cipher.DECRYPT_MODE, ks, encryptCipher.getParameters()); ++ String decryptPlainText = new String(decryptCipher.doFinal(cipherText)); ++ if (!plainText.equals(decryptPlainText)) { ++ 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(); ++ } ++} +\ No newline at end of file +diff --git a/test/jdk/sun/security/jca/PreferredProviderNegativeTest.java b/test/jdk/sun/security/jca/PreferredProviderNegativeTest.java +index cd74b5164..52cfbab4e 100644 +--- a/test/jdk/sun/security/jca/PreferredProviderNegativeTest.java ++++ b/test/jdk/sun/security/jca/PreferredProviderNegativeTest.java +@@ -107,7 +107,12 @@ public class PreferredProviderNegativeTest { + + String expected; + String value = args[1]; +- expected = System.getProperty("test.provider.name", "SunJCE"); ++ ++ if (Security.getProperty("security.provider.1").equals("KAEProvider")) { ++ expected = "KAEProvider"; ++ } else { ++ expected = "SunJCE"; ++ } + + if (args.length >= 2) { + switch (args[0]) { +diff --git a/test/jdk/sun/security/krb5/auto/BasicProc.java b/test/jdk/sun/security/krb5/auto/BasicProc.java +index 0ba0b907b..c67efa446 100644 +--- a/test/jdk/sun/security/krb5/auto/BasicProc.java ++++ b/test/jdk/sun/security/krb5/auto/BasicProc.java +@@ -298,7 +298,9 @@ public class BasicProc { + Proc p = Proc.create("BasicProc") + .inheritProp("jdk.net.hosts.file") + .prop("java.security.manager", "") +- .perm(new javax.security.auth.AuthPermission("doAs")); ++ .perm(new javax.security.auth.AuthPermission("doAs")) ++ .perm(new java.util.PropertyPermission( ++ "kae.disableKaeDispose", "read")); + if (lib != null) { + p.env("KRB5_CONFIG", CONF) + .env("KRB5_TRACE", Platform.isWindows() ? "CON" : "/dev/stderr") +diff --git a/test/jdk/sun/security/pkcs11/Secmod/policy b/test/jdk/sun/security/pkcs11/Secmod/policy +index e4c95ca6d..60488fd06 100644 +--- a/test/jdk/sun/security/pkcs11/Secmod/policy ++++ b/test/jdk/sun/security/pkcs11/Secmod/policy +@@ -3,4 +3,5 @@ grant { + permission java.io.FilePermission "${test.src}/-", "read"; + permission java.io.FilePermission "${pkcs11test.nss.db}/-", "read"; + permission java.io.FilePermission "${pkcs11test.nss.libdir}/-", "read"; ++ permission java.util.PropertyPermission "kae.disableKaeDispose", "read"; + }; +\ No newline at end of file +diff --git a/test/jdk/sun/security/pkcs11/policy b/test/jdk/sun/security/pkcs11/policy +index d5a78b6ba..42d22643e 100644 +--- a/test/jdk/sun/security/pkcs11/policy ++++ b/test/jdk/sun/security/pkcs11/policy +@@ -1,4 +1,5 @@ + grant { + permission java.lang.RuntimePermission "setSecurityManager"; + permission java.util.PropertyPermission "test.provider.name", "read"; ++ permission java.util.PropertyPermission "kae.disableKaeDispose", "read"; + }; +diff --git a/test/jdk/sun/security/ssl/CipherSuite/DisabledCurve.java b/test/jdk/sun/security/ssl/CipherSuite/DisabledCurve.java +index 26304c5df..ca618ccfe 100644 +--- a/test/jdk/sun/security/ssl/CipherSuite/DisabledCurve.java ++++ b/test/jdk/sun/security/ssl/CipherSuite/DisabledCurve.java +@@ -91,6 +91,10 @@ public class DisabledCurve extends SSLSocketTemplate { + } + + public static void main(String[] args) throws Exception { ++ // KAEProvider does not support sect283r1 ++ if (Security.getProperty("security.provider.1").equals("KAEProvider")) { ++ return; ++ } + String expected = args[1]; + String disabledName = ("DISABLE_NONE".equals(args[0]) ? "" : args[0]); + boolean disabled = false; +diff --git a/test/jdk/sun/security/ssl/SSLSocketImpl/NotifyHandshakeTest.policy b/test/jdk/sun/security/ssl/SSLSocketImpl/NotifyHandshakeTest.policy +index b426b173f..6f65e48a3 100644 +--- a/test/jdk/sun/security/ssl/SSLSocketImpl/NotifyHandshakeTest.policy ++++ b/test/jdk/sun/security/ssl/SSLSocketImpl/NotifyHandshakeTest.policy +@@ -33,5 +33,7 @@ grant codeBase "file:com.jar" { + "javax.net.ssl.trustStore", "write"; + permission java.util.PropertyPermission + "javax.net.ssl.trustStorePassword", "write"; ++ permission java.util.PropertyPermission ++ "kae.disableKaeDispose", "read"; + }; + +diff --git a/test/micro/org/openeuler/bench/security/openssl/AESBenchmark.java b/test/micro/org/openeuler/bench/security/openssl/AESBenchmark.java +new file mode 100644 +index 000000000..8a6e5658d +--- /dev/null ++++ b/test/micro/org/openeuler/bench/security/openssl/AESBenchmark.java +@@ -0,0 +1,108 @@ ++/* ++ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2023, 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.bench.security.openssl; ++ ++import org.openjdk.jmh.annotations.Benchmark; ++import org.openjdk.jmh.annotations.Fork; ++import org.openjdk.jmh.annotations.Param; ++import org.openjdk.jmh.annotations.Setup; ++import org.openjdk.jmh.annotations.Warmup; ++ ++import java.security.InvalidKeyException; ++import java.security.InvalidAlgorithmParameterException; ++import java.security.NoSuchAlgorithmException; ++import java.security.Provider; ++import java.security.Security; ++import java.util.concurrent.TimeUnit; ++ ++import javax.crypto.BadPaddingException; ++import javax.crypto.Cipher; ++import javax.crypto.IllegalBlockSizeException; ++import javax.crypto.NoSuchPaddingException; ++import javax.crypto.spec.SecretKeySpec; ++ ++@Warmup(iterations = 10, time = 2, timeUnit = TimeUnit.SECONDS) ++public class AESBenchmark extends BenchmarkBase { ++ ++ @Param({"AES/ECB/PKCS5Padding", "AES/ECB/NoPadding", "AES/CBC/NoPadding", "AES/CBC/PKCS5Padding", "AES/CTR/NoPadding"}) ++ private String algorithm; ++ ++ @Param({"128", "192", "256"}) ++ private int keyLength; ++ ++ @Param({"" + 1024, "" + 10 * 1024, "" + 100 * 1024, "" + 1024 * 1024}) ++ private int dataSize; ++ ++ private byte[][] encryptedData; ++ private Cipher encryptCipher; ++ private Cipher decryptCipher; ++ ++ @Setup ++ public void setup() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, ++ InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { ++ setupProvider(); ++ ++ byte[] keystring = fillSecureRandom(new byte[keyLength / 8]); ++ SecretKeySpec ks = new SecretKeySpec(keystring, "AES"); ++ ++ encryptCipher = (prov == null) ? Cipher.getInstance(algorithm) : Cipher.getInstance(algorithm, prov); ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks); ++ decryptCipher = (prov == null) ? Cipher.getInstance(algorithm) : Cipher.getInstance(algorithm, prov); ++ decryptCipher.init(Cipher.DECRYPT_MODE, ks, encryptCipher.getParameters()); ++ ++ data = fillRandom(new byte[SET_SIZE][dataSize]); ++ encryptedData = fillEncrypted(data, encryptCipher); ++ } ++ ++ @Benchmark ++ public byte[] encrypt() throws IllegalBlockSizeException, BadPaddingException { ++ byte[] d = data[index]; ++ index = (index + 1) % SET_SIZE; ++ return encryptCipher.doFinal(d); ++ } ++ ++ @Benchmark ++ @Fork(jvmArgsPrepend = {"-Xms100G", "-Xmx100G", "-XX:+AlwaysPreTouch", "-Dkae.disableKaeDispose=true"}, value = 5) ++ public byte[] encryptDispose() throws IllegalBlockSizeException, BadPaddingException { ++ byte[] d = data[index]; ++ index = (index + 1) % SET_SIZE; ++ return encryptCipher.doFinal(d); ++ } ++ ++ @Benchmark ++ public byte[] decrypt() throws IllegalBlockSizeException, BadPaddingException { ++ byte[] e = encryptedData[index]; ++ index = (index + 1) % SET_SIZE; ++ return decryptCipher.doFinal(e); ++ } ++ ++ @Benchmark ++ @Fork(jvmArgsPrepend = {"-Xms100G", "-Xmx100G", "-XX:+AlwaysPreTouch", "-Dkae.disableKaeDispose=true"}, value = 5) ++ public byte[] decryptDispose() throws IllegalBlockSizeException, BadPaddingException { ++ byte[] e = encryptedData[index]; ++ index = (index + 1) % SET_SIZE; ++ return decryptCipher.doFinal(e); ++ } ++} ++ +diff --git a/test/micro/org/openeuler/bench/security/openssl/AESGCMBenchmark.java b/test/micro/org/openeuler/bench/security/openssl/AESGCMBenchmark.java +new file mode 100644 +index 000000000..222405235 +--- /dev/null ++++ b/test/micro/org/openeuler/bench/security/openssl/AESGCMBenchmark.java +@@ -0,0 +1,133 @@ ++/* ++ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2023, 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. 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.openeuler.bench.security.openssl; ++ ++import org.openjdk.jmh.annotations.Benchmark; ++import org.openjdk.jmh.annotations.Param; ++import org.openjdk.jmh.annotations.Setup; ++import org.openjdk.jmh.annotations.Fork; ++ ++import javax.crypto.BadPaddingException; ++import javax.crypto.Cipher; ++import javax.crypto.IllegalBlockSizeException; ++import javax.crypto.NoSuchPaddingException; ++import javax.crypto.spec.GCMParameterSpec; ++import javax.crypto.spec.SecretKeySpec; ++import java.security.InvalidAlgorithmParameterException; ++import java.security.InvalidKeyException; ++import java.security.NoSuchAlgorithmException; ++import java.security.spec.InvalidParameterSpecException; ++ ++import java.security.Provider; ++ ++ ++public class AESGCMBenchmark extends BenchmarkBase{ ++ ++ @Param({"AES/GCM/NoPadding","AES/GCM/PKCS5Padding"}) ++ private String algorithm; ++ ++ @Param({"128", "192", "256"}) ++ private int keyLength; ++ ++ @Param({""+1024, "" + 10 * 1024, "" + 100 * 1024, "" + 1024 * 1024}) ++ private int dataSize; ++ ++ byte[] data; ++ byte[] encryptedData; ++ private Cipher encryptCipher; ++ private Cipher decryptCipher; ++ SecretKeySpec ks; ++ GCMParameterSpec gcm_spec; ++ byte[] aad; ++ byte[] iv; ++ ++ public static final int IV_BUFFER_SIZE = 32; ++ public static final int IV_MODULO = IV_BUFFER_SIZE - 16; ++ int iv_index = 0; ++ ++ private int next_iv_index() { ++ int r = iv_index; ++ iv_index = (iv_index + 1) % IV_MODULO; ++ return r; ++ } ++ ++ @Setup ++ public void setup() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, InvalidParameterSpecException { ++ setupProvider(); ++ assert algorithm.split("/")[1].compareToIgnoreCase("GCM") == 0; ++ ++ 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); ++ aad = fillSecureRandom(new byte[5]); ++ encryptCipher = makeCipher(prov, algorithm); ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); ++ encryptCipher.updateAAD(aad); ++ decryptCipher = makeCipher(prov, algorithm); ++ decryptCipher.init(Cipher.DECRYPT_MODE, ks, encryptCipher.getParameters().getParameterSpec(GCMParameterSpec.class)); ++ decryptCipher.updateAAD(aad); ++ data = fillRandom(new byte[dataSize]); ++ encryptedData = encryptCipher.doFinal(data); ++ } ++ ++ @Benchmark ++ public byte[] encrypt() throws BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, InvalidKeyException { ++ gcm_spec = new GCMParameterSpec(96, iv, next_iv_index(), 16); ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); ++ encryptCipher.updateAAD(aad); ++ return encryptCipher.doFinal(data); ++ } ++ ++ @Benchmark ++ @Fork(jvmArgsPrepend = {"-Xms100G", "-Xmx100G", "-XX:+AlwaysPreTouch", "-Dkae.disableKaeDispose=true"}, value = 5) ++ public byte[] encrypt_arg() throws BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, InvalidKeyException { ++ gcm_spec = new GCMParameterSpec(96, iv, next_iv_index(), 16); ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); ++ encryptCipher.updateAAD(aad); ++ return encryptCipher.doFinal(data); ++ } ++ ++ @Benchmark ++ public byte[] decrypt() throws BadPaddingException, IllegalBlockSizeException, InvalidParameterSpecException, InvalidAlgorithmParameterException, InvalidKeyException { ++ decryptCipher.init(Cipher.DECRYPT_MODE, ks, gcm_spec); ++ decryptCipher.updateAAD(aad); ++ return decryptCipher.doFinal(encryptedData); ++ } ++ ++ @Benchmark ++ @Fork(jvmArgsPrepend = {"-Xms100G", "-Xmx100G", "-XX:+AlwaysPreTouch", "-Dkae.disableKaeDispose=true"}, value = 5) ++ public byte[] decrypt_arg() throws BadPaddingException, IllegalBlockSizeException, InvalidParameterSpecException, InvalidAlgorithmParameterException, InvalidKeyException { ++ decryptCipher.init(Cipher.DECRYPT_MODE, ks, gcm_spec); ++ decryptCipher.updateAAD(aad); ++ return decryptCipher.doFinal(encryptedData); ++ } ++ ++ public static Cipher makeCipher(Provider prov, String algorithm) throws NoSuchPaddingException, NoSuchAlgorithmException { ++ return (prov == null) ? Cipher.getInstance(algorithm) : Cipher.getInstance(algorithm, prov); ++ } ++} ++ +diff --git a/test/micro/org/openeuler/bench/security/openssl/BenchmarkBase.java b/test/micro/org/openeuler/bench/security/openssl/BenchmarkBase.java +new file mode 100644 +index 000000000..4e3e7f817 +--- /dev/null ++++ b/test/micro/org/openeuler/bench/security/openssl/BenchmarkBase.java +@@ -0,0 +1,106 @@ ++/* ++ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2023, 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.bench.security.openssl; ++ ++import org.openeuler.security.openssl.KAEProvider; ++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.Threads; ++import org.openjdk.jmh.annotations.Warmup; ++ ++import javax.crypto.BadPaddingException; ++import javax.crypto.Cipher; ++import javax.crypto.IllegalBlockSizeException; ++import java.security.Provider; ++import java.security.SecureRandom; ++import java.security.Security; ++import java.util.Random; ++import java.util.concurrent.TimeUnit; ++ ++@BenchmarkMode(Mode.Throughput) ++@OutputTimeUnit(TimeUnit.SECONDS) ++@Warmup(iterations = 3, time = 3, timeUnit = TimeUnit.SECONDS) ++@Measurement(iterations = 8, time = 2, timeUnit = TimeUnit.SECONDS) ++@Fork(jvmArgsPrepend = {"-Xms100G", "-Xmx100G", "-XX:+AlwaysPreTouch"}, value = 5) ++@Threads(1) ++@State(Scope.Thread) ++public class BenchmarkBase { ++ public static final int SET_SIZE = 128; ++ ++ byte[][] data; ++ int index = 0; ++ ++ @Param({"", "KAEProvider"}) ++ private String provider; ++ ++ public Provider prov = null; ++ ++ @Setup ++ public void setupProvider() { ++ Security.addProvider(new KAEProvider()); ++ if (provider != null && !provider.isEmpty()) { ++ prov = Security.getProvider(provider); ++ if (prov == null) { ++ throw new RuntimeException("Can't find provider \"" + provider + "\""); ++ } ++ } ++ } ++ ++ 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 IllegalBlockSizeException, BadPaddingException { ++ byte[][] encryptedData = new byte[data.length][]; ++ for (int i = 0; i < encryptedData.length; i++) { ++ encryptedData[i] = encryptCipher.doFinal(data[i]); ++ } ++ return encryptedData; ++ } ++ ++} +diff --git a/test/micro/org/openeuler/bench/security/openssl/DHKeyAgreementBenchMark.java b/test/micro/org/openeuler/bench/security/openssl/DHKeyAgreementBenchMark.java +new file mode 100644 +index 000000000..eb5ece804 +--- /dev/null ++++ b/test/micro/org/openeuler/bench/security/openssl/DHKeyAgreementBenchMark.java +@@ -0,0 +1,139 @@ ++/* ++ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2023, 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.bench.security.openssl; ++ ++import java.math.BigInteger; ++import java.security.*; ++import javax.crypto.*; ++import javax.crypto.spec.*; ++import org.openjdk.jmh.annotations.Benchmark; ++import org.openjdk.jmh.annotations.Param; ++import org.openjdk.jmh.annotations.Setup; ++import java.util.concurrent.TimeUnit; ++import org.openjdk.jmh.annotations.Warmup; ++ ++import java.security.KeyPairGenerator; ++ ++public class DHKeyAgreementBenchMark extends BenchmarkBase { ++ @Param({"DH"}) ++ private String algorithm; ++ ++ @Param({"512", "1024", "2048", "3072", "4096"}) ++ private int keySize; ++ ++ private KeyPairGenerator aliceKpairGen; ++ private KeyPairGenerator bobKpairGen; ++ private KeyPairGenerator carolKpairGen; ++ ++ private KeyPair aliceKpair; ++ private KeyPair bobKpair; ++ private KeyPair carolKpair; ++ ++ private DHParameterSpec dhSkipParamSpec; ++ ++ @Setup ++ public void setUp() throws Exception { ++ setupProvider(); ++ aliceKpairGen = createKeyPairGenerator(); ++ bobKpairGen = createKeyPairGenerator(); ++ carolKpairGen = createKeyPairGenerator(); ++ ++ // Alice creates her own DH key pair ++ aliceKpairGen.initialize(keySize); ++ aliceKpair = aliceKpairGen.generateKeyPair(); ++ // Bob creates his own DH key pair ++ bobKpairGen.initialize(keySize); ++ bobKpair = bobKpairGen.generateKeyPair(); ++ // Carol creates her own DH key pair ++ carolKpairGen.initialize(keySize); ++ carolKpair = carolKpairGen.generateKeyPair(); ++ } ++ ++ @Benchmark ++ public void KeyAgreement() throws Exception { ++ ++ // Alice initialize ++ KeyAgreement aliceKeyAgree = (prov == null ? KeyAgreement.getInstance("DH") : KeyAgreement.getInstance("DH", prov)); ++ aliceKeyAgree.init(aliceKpair.getPrivate()); ++ // Bob initialize ++ KeyAgreement bobKeyAgree = (prov == null ? KeyAgreement.getInstance("DH") : KeyAgreement.getInstance("DH", prov)); ++ bobKeyAgree.init(bobKpair.getPrivate()); ++ // Carol initialize ++ KeyAgreement carolKeyAgree = (prov == null ? KeyAgreement.getInstance("DH") : KeyAgreement.getInstance("DH", prov)); ++ carolKeyAgree.init(carolKpair.getPrivate()); ++ // Alice uses Carol's public key ++ Key ac = aliceKeyAgree.doPhase(carolKpair.getPublic(), false); ++ // Bob uses Alice's public key ++ Key ba = bobKeyAgree.doPhase(aliceKpair.getPublic(), false); ++ // Carol uses Bob's public key ++ Key cb = carolKeyAgree.doPhase(bobKpair.getPublic(), false); ++ // Alice uses Carol's result from above ++ aliceKeyAgree.doPhase(cb, true); ++ // Bob uses Alice's result from above ++ bobKeyAgree.doPhase(ac, true); ++ // Carol uses Bob's result from above ++ carolKeyAgree.doPhase(ba, true); ++ ++ // Alice, Bob and Carol compute their secrets ++ byte[] aliceSharedSecret = aliceKeyAgree.generateSecret(); ++ int aliceLen = aliceSharedSecret.length; ++ ++ byte[] bobSharedSecret = bobKeyAgree.generateSecret(); ++ int bobLen = bobSharedSecret.length; ++ ++ byte[] carolSharedSecret = carolKeyAgree.generateSecret(); ++ int carolLen = carolSharedSecret.length; ++ ++ // Compare Alice and Bob ++ if (aliceLen != bobLen) { ++ throw new Exception("Alice and Bob have different lengths"); ++ } ++ for (int i=0; i pssParameterSpecMap; ++ ++ static { ++ initPSSParameterSpecMap(); ++ } ++ ++ @Param({"SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512"}) ++ private String algorithm; ++ ++ @Param({"2048", "3072", "4096"}) ++ private int keySize; ++ ++ @Param({"" + 1024, "" + 10 * 1024, "" + 100 * 1024, "" + 256 * 1024, "" + 1024 * 1024, "" + 10 * 1024 * 1024}) ++ private int dataSize; ++ ++ private KeyPair keyPair; ++ ++ private byte[][] sigData; ++ ++ ++ private static void initPSSParameterSpecMap() { ++ pssParameterSpecMap = new HashMap<>(); ++ pssParameterSpecMap.put("SHA-1", new PSSParameterSpec("SHA-1", ++ "MGF1", MGF1ParameterSpec.SHA1, 20, PSSParameterSpec.TRAILER_FIELD_BC)); ++ pssParameterSpecMap.put("SHA-224", new PSSParameterSpec("SHA-224", ++ "MGF1", MGF1ParameterSpec.SHA224, 28, PSSParameterSpec.TRAILER_FIELD_BC)); ++ pssParameterSpecMap.put("SHA-256", new PSSParameterSpec("SHA-256", ++ "MGF1", MGF1ParameterSpec.SHA256, 32, PSSParameterSpec.TRAILER_FIELD_BC)); ++ pssParameterSpecMap.put("SHA-384", new PSSParameterSpec("SHA-384", ++ "MGF1", MGF1ParameterSpec.SHA384, 48, PSSParameterSpec.TRAILER_FIELD_BC)); ++ pssParameterSpecMap.put("SHA-512", new PSSParameterSpec("SHA-512", ++ "MGF1", MGF1ParameterSpec.SHA512, 64, PSSParameterSpec.TRAILER_FIELD_BC)); ++ } ++ ++ @Setup ++ public void setup() throws Exception { ++ setupProvider(); ++ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); ++ keyPairGenerator.initialize(keySize); ++ keyPair = keyPairGenerator.generateKeyPair(); ++ data = new byte[SET_SIZE][dataSize]; ++ sigData = getSigBytes(data); ++ } ++ ++ private byte[][] getSigBytes(byte[][] data) throws Exception { ++ byte[][] sigBytes = new byte[data.length][]; ++ Signature signature = prov != null ? Signature.getInstance("RSASSA-PSS", prov) : ++ Signature.getInstance("RSASSA-PSS"); ++ signature.setParameter(pssParameterSpecMap.get(algorithm)); ++ signature.initSign(keyPair.getPrivate()); ++ for (int i = 0; i < sigBytes.length; i++) { ++ signature.update(data[i]); ++ sigBytes[i] = signature.sign(); ++ } ++ return sigBytes; ++ } ++ ++ @Benchmark ++ public void sign() throws Exception { ++ Signature signature = prov != null ? Signature.getInstance("RSASSA-PSS", prov) : ++ Signature.getInstance("RSASSA-PSS"); ++ signature.setParameter(pssParameterSpecMap.get(algorithm)); ++ signature.initSign(keyPair.getPrivate()); ++ signature.update(data[index]); ++ signature.sign(); ++ index = (index + 1) % SET_SIZE; ++ } ++ ++ @Benchmark ++ public void verify() throws Exception { ++ Signature signature = prov != null ? Signature.getInstance("RSASSA-PSS", prov) : ++ Signature.getInstance("RSASSA-PSS"); ++ signature.setParameter(pssParameterSpecMap.get(algorithm)); ++ signature.initVerify(keyPair.getPublic()); ++ signature.update(data[index]); ++ signature.verify(sigData[index]); ++ index = (index + 1) % SET_SIZE; ++ } ++} +diff --git a/test/micro/org/openeuler/bench/security/openssl/RSASignatureBenchmark.java b/test/micro/org/openeuler/bench/security/openssl/RSASignatureBenchmark.java +new file mode 100644 +index 000000000..31112ea57 +--- /dev/null ++++ b/test/micro/org/openeuler/bench/security/openssl/RSASignatureBenchmark.java +@@ -0,0 +1,90 @@ ++/* ++ * Copyright (c) 2023, Huawei Technologies Co., Ltd. All rights reserved. ++ * 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.openeuler.bench.security.openssl; ++ ++import org.openjdk.jmh.annotations.Benchmark; ++import org.openjdk.jmh.annotations.Param; ++import org.openjdk.jmh.annotations.Setup; ++ ++import java.security.*; ++ ++/** ++ * RSA Signature Benchmark ++ */ ++public class RSASignatureBenchmark extends BenchmarkBase { ++ @Param({"MD5withRSA", "SHA1withRSA", "SHA224withRSA", "SHA384withRSA", "SHA256withRSA", "SHA512withRSA"}) ++ private String algorithm; ++ ++ @Param({"2048", "3072", "4096"}) ++ private int keySize; ++ ++ @Param({"" + 1024, "" + 10 * 1024, "" + 100 * 1024, "" + 256 * 1024, "" + 1024 * 1024, "" + 10 * 1024 * 1024}) ++ private int dataSize; ++ ++ private KeyPair keyPair; ++ ++ private byte[][] sigData; ++ ++ @Setup ++ public void setup() throws Exception { ++ setupProvider(); ++ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); ++ keyPairGenerator.initialize(keySize); ++ keyPair = keyPairGenerator.generateKeyPair(); ++ data = new byte[SET_SIZE][dataSize]; ++ sigData = getSigBytes(data); ++ } ++ ++ private byte[][] getSigBytes(byte[][] data) throws Exception { ++ byte[][] sigBytes = new byte[data.length][]; ++ Signature signature = prov != null ? Signature.getInstance(algorithm, prov) : ++ Signature.getInstance(algorithm); ++ signature.initSign(keyPair.getPrivate()); ++ for (int i = 0; i < sigBytes.length; i++) { ++ signature.update(data[i]); ++ sigBytes[i] = signature.sign(); ++ } ++ return sigBytes; ++ } ++ ++ @Benchmark ++ public void sign() throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { ++ Signature signature = prov != null ? Signature.getInstance(algorithm, prov) : ++ Signature.getInstance(algorithm); ++ signature.initSign(keyPair.getPrivate()); ++ signature.update(data[index]); ++ signature.sign(); ++ index = (index + 1) % SET_SIZE; ++ } ++ ++ @Benchmark ++ public void verify() throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { ++ Signature signature = prov != null ? Signature.getInstance(algorithm, prov) : ++ Signature.getInstance(algorithm); ++ signature.initVerify(keyPair.getPublic()); ++ signature.update(data[index]); ++ signature.verify(sigData[index]); ++ index = (index + 1) % SET_SIZE; ++ } ++} +diff --git a/test/micro/org/openeuler/bench/security/openssl/SM3Benchmark.java b/test/micro/org/openeuler/bench/security/openssl/SM3Benchmark.java +new file mode 100644 +index 000000000..eea830dbe +--- /dev/null ++++ b/test/micro/org/openeuler/bench/security/openssl/SM3Benchmark.java +@@ -0,0 +1,98 @@ ++/* ++ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2023, 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.bench.security.openssl; ++ ++import org.openeuler.security.openssl.KAEProvider; ++import org.openjdk.jmh.annotations.BenchmarkMode; ++import org.openjdk.jmh.annotations.Benchmark; ++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.Scope; ++import org.openjdk.jmh.annotations.Param; ++import org.openjdk.jmh.annotations.Setup; ++import org.openjdk.jmh.annotations.State; ++import org.openjdk.jmh.annotations.Threads; ++import org.openjdk.jmh.annotations.Warmup; ++ ++import java.security.MessageDigest; ++import java.security.NoSuchAlgorithmException; ++import java.security.Provider; ++import java.security.Security; ++import java.util.Random; ++import java.util.concurrent.TimeUnit; ++ ++@BenchmarkMode(Mode.Throughput) ++@OutputTimeUnit(TimeUnit.SECONDS) ++@Warmup(iterations = 3, time = 3, timeUnit = TimeUnit.SECONDS) ++@Measurement(iterations = 8, time = 2, timeUnit = TimeUnit.SECONDS) ++@Fork(jvmArgsPrepend = {"-Xms100G", "-Xmx100G", "-XX:+AlwaysPreTouch"}, value = 5) ++@Threads(1) ++@State(Scope.Thread) ++public class SM3Benchmark { ++ public static final int SET_SIZE = 128; ++ byte[][] data; ++ int index = 0; ++ ++ @Param({"SM3"}) ++ private String algorithm; ++ ++ @Param({"" + 1024, "" + 10 * 1024, "" + 100 * 1024, "" + 1024 * 1024}) ++ int dataSize; ++ ++ MessageDigest md; ++ ++ @Setup ++ public void setup() throws NoSuchAlgorithmException { ++ Security.addProvider(new KAEProvider()); ++ Provider prov = Security.getProvider("KAEProvider"); ++ data = fillRandom(new byte[SET_SIZE][dataSize]); ++ md = (prov == null) ? MessageDigest.getInstance(algorithm) : MessageDigest.getInstance(algorithm, prov); ++ } ++ ++ @Benchmark ++ public byte[] digest() { ++ byte[] d = data[index]; ++ index = (index + 1) % SET_SIZE; ++ return md.digest(d); ++ } ++ ++ @Benchmark ++ @Fork(jvmArgsPrepend = {"-Xms100G", "-Xmx100G", "-XX:+AlwaysPreTouch", "-Dkae.disableKaeDispose=true"}, value = 5) ++ public byte[] digestDispose() { ++ byte[] d = data[index]; ++ index = (index + 1) % SET_SIZE; ++ return md.digest(d); ++ } ++ ++ public static byte[][] fillRandom(byte[][] data) { ++ Random rnd = new Random(); ++ for (byte[] d : data) { ++ rnd.nextBytes(d); ++ } ++ return data; ++ } ++} ++ +diff --git a/test/micro/org/openeuler/bench/security/openssl/SM4Benchmark.java b/test/micro/org/openeuler/bench/security/openssl/SM4Benchmark.java +new file mode 100644 +index 000000000..32de2b235 +--- /dev/null ++++ b/test/micro/org/openeuler/bench/security/openssl/SM4Benchmark.java +@@ -0,0 +1,157 @@ ++/* ++ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2023, 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.bench.security.openssl; ++ ++import org.openeuler.security.openssl.KAEProvider; ++import org.openjdk.jmh.annotations.BenchmarkMode; ++import org.openjdk.jmh.annotations.Benchmark; ++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.Scope; ++import org.openjdk.jmh.annotations.Param; ++import org.openjdk.jmh.annotations.Setup; ++import org.openjdk.jmh.annotations.State; ++import org.openjdk.jmh.annotations.Threads; ++import org.openjdk.jmh.annotations.Warmup; ++ ++import java.security.InvalidKeyException; ++import java.security.InvalidAlgorithmParameterException; ++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; ++ ++import javax.crypto.BadPaddingException; ++import javax.crypto.Cipher; ++import javax.crypto.IllegalBlockSizeException; ++import javax.crypto.NoSuchPaddingException; ++import javax.crypto.spec.SecretKeySpec; ++ ++@BenchmarkMode(Mode.Throughput) ++@OutputTimeUnit(TimeUnit.SECONDS) ++@Warmup(iterations = 3, time = 3, timeUnit = TimeUnit.SECONDS) ++@Measurement(iterations = 8, time = 2, timeUnit = TimeUnit.SECONDS) ++@Fork(jvmArgsPrepend = {"-Xms100G", "-Xmx100G", "-XX:+AlwaysPreTouch"}, value = 5) ++@Threads(1) ++@State(Scope.Thread) ++public class SM4Benchmark { ++ public static final int SET_SIZE = 128; ++ byte[][] data; ++ int index = 0; ++ ++ @Param({"SM4/ECB/NoPadding", "SM4/ECB/PKCS5Padding", "SM4/CBC/NoPadding", "SM4/CBC/PKCS5Padding", "SM4/CTR/NoPadding", "SM4/OFB/NoPadding", "SM4/OFB/PKCS5Padding"}) ++ private String algorithm; ++ ++ @Param({"128"}) ++ private int keyLength; ++ ++ @Param({"" + 1024, "" + 10 * 1024, "" + 100 * 1024, "" + 1024 * 1024}) ++ private int dataSize; ++ ++ private byte[][] encryptedData; ++ private Cipher encryptCipher; ++ private Cipher decryptCipher; ++ ++ @Setup ++ public void setup() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, ++ InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { ++ Security.addProvider(new KAEProvider()); ++ Provider prov = Security.getProvider("KAEProvider"); ++ ++ byte[] keystring = fillSecureRandom(new byte[keyLength / 8]); ++ SecretKeySpec ks = new SecretKeySpec(keystring, "SM4"); ++ ++ encryptCipher = (prov == null) ? Cipher.getInstance(algorithm) : Cipher.getInstance(algorithm, prov); ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks); ++ decryptCipher = (prov == null) ? Cipher.getInstance(algorithm) : Cipher.getInstance(algorithm, prov); ++ decryptCipher.init(Cipher.DECRYPT_MODE, ks, encryptCipher.getParameters()); ++ ++ data = fillRandom(new byte[SET_SIZE][dataSize]); ++ encryptedData = fillEncrypted(data, encryptCipher); ++ } ++ ++ @Benchmark ++ public byte[] encrypt() throws IllegalBlockSizeException, BadPaddingException { ++ byte[] d = data[index]; ++ index = (index + 1) % SET_SIZE; ++ return encryptCipher.doFinal(d); ++ } ++ ++ @Benchmark ++ @Fork(jvmArgsPrepend = {"-Xms100G", "-Xmx100G", "-XX:+AlwaysPreTouch", "-Dkae.disableKaeDispose=true"}, value = 5) ++ public byte[] encryptDispose() throws IllegalBlockSizeException, BadPaddingException { ++ byte[] d = data[index]; ++ index = (index + 1) % SET_SIZE; ++ return encryptCipher.doFinal(d); ++ } ++ ++ @Benchmark ++ public byte[] decrypt() throws IllegalBlockSizeException, BadPaddingException { ++ byte[] e = encryptedData[index]; ++ index = (index + 1) % SET_SIZE; ++ return decryptCipher.doFinal(e); ++ } ++ ++ @Benchmark ++ @Fork(jvmArgsPrepend = {"-Xms100G", "-Xmx100G", "-XX:+AlwaysPreTouch", "-Dkae.disableKaeDispose=true"}, value = 5) ++ public byte[] decryptDispose() throws IllegalBlockSizeException, BadPaddingException { ++ byte[] e = encryptedData[index]; ++ index = (index + 1) % SET_SIZE; ++ return decryptCipher.doFinal(e); ++ } ++ ++ 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 IllegalBlockSizeException, BadPaddingException { ++ byte[][] encryptedData = new byte[data.length][]; ++ for (int i = 0; i < encryptedData.length; i++) { ++ encryptedData[i] = encryptCipher.doFinal(data[i]); ++ } ++ return encryptedData; ++ } ++} ++ +-- +2.47.0.windows.2 + diff --git a/huawei-Add-KAE-zip-feature.patch b/huawei-Add-KAE-zip-feature.patch new file mode 100644 index 0000000000000000000000000000000000000000..49d556c68da1dd766e7b0bb3ef81935ad4b65582 --- /dev/null +++ b/huawei-Add-KAE-zip-feature.patch @@ -0,0 +1,569 @@ +Date: Thu, 13 Mar 2025 11:52:43 +0800 +Subject: Add KAE zip feature + +--- + make/autoconf/libraries.m4 | 4 ++ + make/autoconf/spec.gmk.in | 1 + + make/modules/java.base/lib/CoreLibraries.gmk | 39 ++++++++++-- + src/hotspot/share/runtime/arguments.cpp | 9 +++ + .../share/classes/java/util/zip/Deflater.java | 27 ++++++++ + .../java/util/zip/GZIPInputStream.java | 62 ++++++++++++++++++- + .../java/util/zip/GZIPOutputStream.java | 35 ++++++++++- + .../share/classes/java/util/zip/Inflater.java | 23 +++++++ + .../java/util/zip/InflaterInputStream.java | 21 +++++++ + src/java.base/share/conf/security/java.policy | 4 ++ + src/java.base/share/native/libzip/Deflater.c | 38 ++++++++++++ + src/java.base/share/native/libzip/Inflater.c | 37 ++++++++++- + .../zip/GZIP/GZIPOutputStreamHeaderTest.java | 4 ++ + 13 files changed, 296 insertions(+), 8 deletions(-) + +diff --git a/make/autoconf/libraries.m4 b/make/autoconf/libraries.m4 +index 51d4f724c..b4f62ca19 100644 +--- a/make/autoconf/libraries.m4 ++++ b/make/autoconf/libraries.m4 +@@ -210,4 +210,8 @@ AC_DEFUN_ONCE([LIB_SETUP_MISC_LIBS], + # Control if libzip can use mmap. Available for purposes of overriding. + LIBZIP_CAN_USE_MMAP=true + AC_SUBST(LIBZIP_CAN_USE_MMAP) ++ ++ # Control if libz can use mmap. Available for purposes of overriding. ++ LIBZ_CAN_USE_MMAP=true ++ AC_SUBST(LIBZ_CAN_USE_MMAP) + ]) +diff --git a/make/autoconf/spec.gmk.in b/make/autoconf/spec.gmk.in +index 8336b287a..53642529a 100644 +--- a/make/autoconf/spec.gmk.in ++++ b/make/autoconf/spec.gmk.in +@@ -811,6 +811,7 @@ USE_EXTERNAL_LIBZ:=@USE_EXTERNAL_LIBZ@ + LIBZ_CFLAGS:=@LIBZ_CFLAGS@ + LIBZ_LIBS:=@LIBZ_LIBS@ + LIBZIP_CAN_USE_MMAP:=@LIBZIP_CAN_USE_MMAP@ ++LIBZ_CAN_USE_MMAP:=@LIBZ_CAN_USE_MMAP@ + MSVCR_DLL:=@MSVCR_DLL@ + VCRUNTIME_1_DLL:=@VCRUNTIME_1_DLL@ + MSVCP_DLL:=@MSVCP_DLL@ +diff --git a/make/modules/java.base/lib/CoreLibraries.gmk b/make/modules/java.base/lib/CoreLibraries.gmk +index 6aa9fd658..67d1192e1 100644 +--- a/make/modules/java.base/lib/CoreLibraries.gmk ++++ b/make/modules/java.base/lib/CoreLibraries.gmk +@@ -84,9 +84,39 @@ $(BUILD_LIBJAVA): $(BUILD_LIBVERIFY) + + ########################################################################################## + +-BUILD_LIBZIP_EXCLUDES := +-ifeq ($(USE_EXTERNAL_LIBZ), true) +- LIBZIP_EXCLUDES += zlib ++ifeq ($(USE_EXTERNAL_LIBZ), false) ++ ifeq ($(OPENJDK_TARGET_OS), linux) ++ ifeq ($(LIBZ_CAN_USE_MMAP), true) ++ BUILD_LIBZ_MMAP := -DUSE_MMAP ++ endif ++ ++ $(eval $(call SetupJdkLibrary, BUILD_LIBZ, \ ++ NAME := z, \ ++ SRC := $(TOPDIR)/src/java.base/share/native/libzip/zlib, \ ++ OPTIMIZATION := LOW, \ ++ CFLAGS := $(filter-out -fvisibility=hidden,$(CFLAGS_JDKLIB) $(LIBZ_CFLAGS)), \ ++ CFLAGS_unix := $(BUILD_LIBZ_MMAP) -UDEBUG, \ ++ DISABLED_WARNINGS_gcc := unused-function implicit-fallthrough, \ ++ DISABLED_WARNINGS_gcc_gzlib.c := implicit-function-declaration, \ ++ DISABLED_WARNINGS_gcc_gzwrite.c := implicit-function-declaration, \ ++ DISABLED_WARNINGS_gcc_gzread.c := implicit-function-declaration, \ ++ DISABLED_WARNINGS_clang := format-nonliteral, \ ++ LDFLAGS := $(LDFLAGS_JDKLIB) \ ++ $(call SET_SHARED_LIBRARY_ORIGIN), \ ++ LIBS_unix := , \ ++ LIBS_windows := jvm.lib $(WIN_JAVA_LIB), \ ++ )) ++ ++ $(BUILD_LIBZ): $(BUILD_LIBJAVA) ++ ++ TARGETS += $(BUILD_LIBZ) ++ endif ++endif ++ ++########################################################################################## ++ ++ifeq ($(OPENJDK_TARGET_OS), linux) ++ LIBZIP_EXCLUDES := zlib + endif + + ifeq ($(LIBZIP_CAN_USE_MMAP), true) +@@ -105,10 +135,11 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBZIP, \ + DISABLED_WARNINGS_clang_gzwrite.c := format-nonliteral, \ + LDFLAGS := $(LDFLAGS_JDKLIB) \ + $(call SET_SHARED_LIBRARY_ORIGIN), \ +- LIBS_unix := -ljvm -ljava $(LIBZ_LIBS), \ ++ LIBS_unix := -ljvm -ljava -lz, \ + LIBS_windows := jvm.lib $(WIN_JAVA_LIB), \ + )) + ++$(BUILD_LIBZIP): $(BUILD_LIBZ) + $(BUILD_LIBZIP): $(BUILD_LIBJAVA) + + TARGETS += $(BUILD_LIBZIP) +diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp +index 9b8e5aa87..2e2a9a0f2 100644 +--- a/src/hotspot/share/runtime/arguments.cpp ++++ b/src/hotspot/share/runtime/arguments.cpp +@@ -2551,6 +2551,15 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m + // -D + } else if (match_option(option, "-D", &tail)) { + const char* value; ++#ifndef AARCH64 ++ if (match_option(option, "-DGZIP_USE_KAE=", &value)) { ++ if (strcmp(value, "true") == 0) { ++ jio_fprintf(defaultStream::output_stream(), ++ "-DGZIP_USE_KAE is not supported. This system propertiy is valid only on aarch64 architecture machines.\n" ++ "The compression action is performed using the native compression capability of the JDK.\n"); ++ } ++ } ++#endif + if (match_option(option, "-Djava.endorsed.dirs=", &value) && + *value!= '\0' && strcmp(value, "\"\"") != 0) { + // abort if -Djava.endorsed.dirs is set +diff --git a/src/java.base/share/classes/java/util/zip/Deflater.java b/src/java.base/share/classes/java/util/zip/Deflater.java +index 5b57112bf..84688ad1a 100644 +--- a/src/java.base/share/classes/java/util/zip/Deflater.java ++++ b/src/java.base/share/classes/java/util/zip/Deflater.java +@@ -203,6 +203,20 @@ public class Deflater { + init(level, DEFAULT_STRATEGY, nowrap)); + } + ++ /** ++ * Creates a new compressor using the specified compression level ++ * and windowBits. ++ * This method is mainly used to support the KAE-zip feature. ++ * @param level the compression level (0-9) ++ * @param windowBits compression format (-15~31) ++ */ ++ public Deflater(int level, int windowBits) { ++ this.level = level; ++ this.strategy = DEFAULT_STRATEGY; ++ this.zsRef = new DeflaterZStreamRef(this, ++ initKAE(level, DEFAULT_STRATEGY, windowBits)); ++ } ++ + /** + * Creates a new compressor using the specified compression level. + * Compressed data will be generated in ZLIB format. +@@ -882,6 +896,18 @@ public class Deflater { + } + } + ++ /** ++ * Resets deflater so that a new set of input data can be processed. ++ * Java fields are not initialized. ++ * This method is mainly used to support the KAE-zip feature. ++ */ ++ public void resetKAE() { ++ synchronized (zsRef) { ++ ensureOpen(); ++ reset(zsRef.address()); ++ } ++ } ++ + /** + * Closes the compressor and discards any unprocessed input. + * +@@ -913,6 +939,7 @@ public class Deflater { + } + + private static native long init(int level, int strategy, boolean nowrap); ++ private static native long initKAE(int level, int strategy, int windowBits); + private static native void setDictionary(long addr, byte[] b, int off, + int len); + private static native void setDictionaryBuffer(long addr, long bufAddress, int len); +diff --git a/src/java.base/share/classes/java/util/zip/GZIPInputStream.java b/src/java.base/share/classes/java/util/zip/GZIPInputStream.java +index d704fd3c9..850691e02 100644 +--- a/src/java.base/share/classes/java/util/zip/GZIPInputStream.java ++++ b/src/java.base/share/classes/java/util/zip/GZIPInputStream.java +@@ -54,6 +54,23 @@ public class GZIPInputStream extends InflaterInputStream { + + private boolean closed = false; + ++ /** ++ * The field is mainly used to support the KAE-zip feature. ++ */ ++ private static boolean GZIP_USE_KAE = false; ++ ++ private static int WINDOWBITS = 31; ++ ++ private static int FLUSHKAE = 2; ++ ++ static { ++ if ("aarch64".equals(System.getProperty("os.arch"))) { ++ GZIP_USE_KAE = Boolean.parseBoolean(System.getProperty("GZIP_USE_KAE", "false")); ++ WINDOWBITS = Integer.parseInt(System.getProperty("WINDOWBITS", "31")); ++ FLUSHKAE = Integer.parseInt(System.getProperty("FLUSHKAE", "2")); ++ } ++ } ++ + /** + * Check to make sure that this stream has not been closed + */ +@@ -74,8 +91,13 @@ public class GZIPInputStream extends InflaterInputStream { + * @throws IllegalArgumentException if {@code size <= 0} + */ + public GZIPInputStream(InputStream in, int size) throws IOException { +- super(in, in != null ? new Inflater(true) : null, size); ++ super(in, in != null ? (GZIP_USE_KAE ? new Inflater(WINDOWBITS, FLUSHKAE) : new Inflater(true)) : null, size); + usesDefaultInflater = true; ++ ++ // When GZIP_USE_KAE is true, the header of the file is readed ++ // through the native zlib library, not in java code. ++ if (GZIP_USE_KAE) return; ++ + readHeader(in); + } + +@@ -127,13 +149,16 @@ public class GZIPInputStream extends InflaterInputStream { + } + int n = super.read(buf, off, len); + if (n == -1) { +- if (readTrailer()) ++ if (GZIP_USE_KAE ? readTrailerKAE() : readTrailer()) + eos = true; + else + return this.read(buf, off, len); + } else { + crc.update(buf, off, n); + } ++ if (GZIP_USE_KAE && inf.finished()) { ++ if (readTrailerKAE()) eos = true; ++ } + return n; + } + +@@ -254,6 +279,39 @@ public class GZIPInputStream extends InflaterInputStream { + return true; + } + ++ /* ++ * Reads GZIP member trailer and returns true if the eos ++ * reached, false if there are more (concatenated gzip ++ * data set) ++ * ++ * This method is mainly used to support the KAE-zip feature. ++ */ ++ private boolean readTrailerKAE() throws IOException { ++ InputStream in = this.in; ++ int n = inf.getRemaining(); ++ if (n > 0) { ++ in = new SequenceInputStream( ++ new ByteArrayInputStream(buf, len - n, n), ++ new FilterInputStream(in) { ++ public void close() throws IOException {} ++ }); ++ } ++ // If there are more bytes available in "in" or the leftover in the "inf" is > 18 bytes: ++ // next.header.min(10) + next.trailer(8), try concatenated case ++ ++ if (n > 18) { ++ inf.reset(); ++ inf.setInput(buf, len - n, n); ++ } else { ++ try { ++ fillKAE(n); ++ } catch (IOException e) { ++ return true; ++ } ++ } ++ return false; ++ } ++ + /* + * Reads unsigned integer in Intel byte order. + */ +diff --git a/src/java.base/share/classes/java/util/zip/GZIPOutputStream.java b/src/java.base/share/classes/java/util/zip/GZIPOutputStream.java +index cdfac329c..f9570265e 100644 +--- a/src/java.base/share/classes/java/util/zip/GZIPOutputStream.java ++++ b/src/java.base/share/classes/java/util/zip/GZIPOutputStream.java +@@ -55,6 +55,20 @@ public class GZIPOutputStream extends DeflaterOutputStream { + // Represents the default "unknown" value for OS header, per RFC-1952 + private static final byte OS_UNKNOWN = (byte) 255; + ++ /** ++ * The field is mainly used to support the KAE-zip feature. ++ */ ++ private static boolean GZIP_USE_KAE = false; ++ ++ private static int WINDOWBITS = 31; ++ ++ static { ++ if ("aarch64".equals(System.getProperty("os.arch"))) { ++ GZIP_USE_KAE = Boolean.parseBoolean(System.getProperty("GZIP_USE_KAE", "false")); ++ WINDOWBITS = Integer.parseInt(System.getProperty("WINDOWBITS", "31")); ++ } ++ } ++ + /** + * Creates a new output stream with the specified buffer size. + * +@@ -90,10 +104,16 @@ public class GZIPOutputStream extends DeflaterOutputStream { + public GZIPOutputStream(OutputStream out, int size, boolean syncFlush) + throws IOException + { +- super(out, out != null ? new Deflater(Deflater.DEFAULT_COMPRESSION, true) : null, ++ super(out, out != null ? ++ (GZIP_USE_KAE ? new Deflater(Deflater.DEFAULT_COMPRESSION, WINDOWBITS) : ++ new Deflater(Deflater.DEFAULT_COMPRESSION, true)) : null, + size, + syncFlush); + usesDefaultDeflater = true; ++ ++ // When GZIP_USE_KAE is true, the header of the file is written ++ // through the native zlib library, not in java code. ++ if (GZIP_USE_KAE) return; + writeHeader(); + crc.reset(); + } +@@ -163,6 +183,13 @@ public class GZIPOutputStream extends DeflaterOutputStream { + int len = def.deflate(buf, 0, buf.length); + if (def.finished() && len <= buf.length - TRAILER_SIZE) { + // last deflater buffer. Fit trailer at the end ++ // When GZIP_USE_KAE is true, the trailer of the file is written ++ // through the native zlib library, not in java code. ++ if (GZIP_USE_KAE) { ++ out.write(buf, 0, len); ++ def.resetKAE(); ++ return; ++ } + writeTrailer(buf, len); + len = len + TRAILER_SIZE; + out.write(buf, 0, len); +@@ -173,6 +200,12 @@ public class GZIPOutputStream extends DeflaterOutputStream { + } + // if we can't fit the trailer at the end of the last + // deflater buffer, we write it separately ++ // When GZIP_USE_KAE is true, the trailer of the file is written ++ // through the native zlib library, not in java code. ++ if (GZIP_USE_KAE) { ++ def.resetKAE(); ++ return; ++ } + byte[] trailer = new byte[TRAILER_SIZE]; + writeTrailer(trailer, 0); + out.write(trailer); +diff --git a/src/java.base/share/classes/java/util/zip/Inflater.java b/src/java.base/share/classes/java/util/zip/Inflater.java +index 1b9152531..ae4740482 100644 +--- a/src/java.base/share/classes/java/util/zip/Inflater.java ++++ b/src/java.base/share/classes/java/util/zip/Inflater.java +@@ -134,6 +134,17 @@ public class Inflater { + this.zsRef = new InflaterZStreamRef(this, init(nowrap)); + } + ++ /** ++ * Creates a new decompressor. ++ * This method is mainly used to support the KAE-zip feature. ++ * ++ * @param windowBits compression format (-15~31) ++ * @param flushKAE inflate flush type (0~6) ++ */ ++ public Inflater(int windowBits, int flushKAE) { ++ this.zsRef = new InflaterZStreamRef(this, initKAE(windowBits, flushKAE)); ++ } ++ + /** + * Creates a new decompressor. + */ +@@ -702,6 +713,17 @@ public class Inflater { + } + } + ++ /** ++ * Resets inflater so that a new set of input data can be processed. ++ * This method is mainly used to support the KAE-zip feature. ++ */ ++ public void resetKAE() { ++ synchronized (zsRef) { ++ ensureOpen(); ++ reset(zsRef.address()); ++ } ++ } ++ + /** + * Closes the decompressor and discards any unprocessed input. + * +@@ -730,6 +752,7 @@ public class Inflater { + + private static native void initIDs(); + private static native long init(boolean nowrap); ++ private static native long initKAE(int windowBits, int flushKAE); + private static native void setDictionary(long addr, byte[] b, int off, + int len); + private static native void setDictionaryBuffer(long addr, long bufAddress, int len); +diff --git a/src/java.base/share/classes/java/util/zip/InflaterInputStream.java b/src/java.base/share/classes/java/util/zip/InflaterInputStream.java +index d60b04470..2a6ffabe8 100644 +--- a/src/java.base/share/classes/java/util/zip/InflaterInputStream.java ++++ b/src/java.base/share/classes/java/util/zip/InflaterInputStream.java +@@ -268,6 +268,27 @@ public class InflaterInputStream extends FilterInputStream { + inf.setInput(buf, 0, len); + } + ++ /** ++ * Fills input buffer with more data to decompress. ++ * This method is mainly used to support the KAE-zip feature. ++ * @param n Maximum Read Bytes ++ * @throws IOException if an I/O error has occurred ++ */ ++ protected void fillKAE(int n) throws IOException { ++ ensureOpen(); ++ byte[] buftmp = new byte[buf.length]; ++ if (n != 0) { ++ System.arraycopy(buf, buf.length - n, buftmp, 0, n); ++ } ++ int kaelen = in.read(buftmp, n, buf.length - n); ++ if (kaelen == -1) { ++ throw new EOFException("Unexpected end of ZLIB input stream"); ++ } ++ System.arraycopy(buftmp, 0, buf, buf.length - n - kaelen, n + kaelen); ++ inf.reset(); ++ inf.setInput(buf, buf.length - n - kaelen, n + kaelen); ++ } ++ + /** + * Tests if this input stream supports the {@code mark} and + * {@code reset} methods. The {@code markSupported} +diff --git a/src/java.base/share/conf/security/java.policy b/src/java.base/share/conf/security/java.policy +index 88e8753de..99e8de1b4 100644 +--- a/src/java.base/share/conf/security/java.policy ++++ b/src/java.base/share/conf/security/java.policy +@@ -43,4 +43,8 @@ grant { + permission java.util.PropertyPermission "java.vm.version", "read"; + permission java.util.PropertyPermission "java.vm.vendor", "read"; + permission java.util.PropertyPermission "java.vm.name", "read"; ++ ++ permission java.util.PropertyPermission "GZIP_USE_KAE", "read"; ++ permission java.util.PropertyPermission "WINDOWBITS", "read"; ++ permission java.util.PropertyPermission "FLUSHKAE", "read"; + }; +diff --git a/src/java.base/share/native/libzip/Deflater.c b/src/java.base/share/native/libzip/Deflater.c +index 1ed1994d4..c04d5c42a 100644 +--- a/src/java.base/share/native/libzip/Deflater.c ++++ b/src/java.base/share/native/libzip/Deflater.c +@@ -76,6 +76,44 @@ Java_java_util_zip_Deflater_init(JNIEnv *env, jclass cls, jint level, + } + } + ++JNIEXPORT jlong JNICALL ++Java_java_util_zip_Deflater_initKAE(JNIEnv *env, jclass cls, jint level, ++ jint strategy, jint windowBits) ++{ ++ z_stream *strm = calloc(1, sizeof(z_stream)); ++ ++ if (strm == 0) { ++ JNU_ThrowOutOfMemoryError(env, 0); ++ return jlong_zero; ++ } else { ++ const char *msg; ++ int ret = deflateInit2(strm, level, Z_DEFLATED, ++ windowBits, ++ DEF_MEM_LEVEL, strategy); ++ switch (ret) { ++ case Z_OK: ++ return ptr_to_jlong(strm); ++ case Z_MEM_ERROR: ++ free(strm); ++ JNU_ThrowOutOfMemoryError(env, 0); ++ return jlong_zero; ++ case Z_STREAM_ERROR: ++ free(strm); ++ JNU_ThrowIllegalArgumentException(env, 0); ++ return jlong_zero; ++ default: ++ msg = ((strm->msg != NULL) ? strm->msg : ++ (ret == Z_VERSION_ERROR) ? ++ "zlib returned Z_VERSION_ERROR: " ++ "compile time and runtime zlib implementations differ" : ++ "unknown error initializing zlib library"); ++ free(strm); ++ JNU_ThrowInternalError(env, msg); ++ return jlong_zero; ++ } ++ } ++} ++ + static void throwInternalErrorHelper(JNIEnv *env, z_stream *strm, const char *fixmsg) { + const char *msg = NULL; + msg = (strm->msg != NULL) ? strm->msg : fixmsg; +diff --git a/src/java.base/share/native/libzip/Inflater.c b/src/java.base/share/native/libzip/Inflater.c +index a41e9775b..4835bfac2 100644 +--- a/src/java.base/share/native/libzip/Inflater.c ++++ b/src/java.base/share/native/libzip/Inflater.c +@@ -44,6 +44,7 @@ + + static jfieldID inputConsumedID; + static jfieldID outputConsumedID; ++static jint inflaterFlushType = Z_PARTIAL_FLUSH; + + JNIEXPORT void JNICALL + Java_java_util_zip_Inflater_initIDs(JNIEnv *env, jclass cls) +@@ -87,6 +88,40 @@ Java_java_util_zip_Inflater_init(JNIEnv *env, jclass cls, jboolean nowrap) + } + } + ++JNIEXPORT jlong JNICALL ++Java_java_util_zip_Inflater_initKAE(JNIEnv *env, jclass cls, jint windowBits, jint flushKAE) ++{ ++ z_stream *strm = calloc(1, sizeof(z_stream)); ++ inflaterFlushType = flushKAE; ++ ++ if (strm == NULL) { ++ JNU_ThrowOutOfMemoryError(env, 0); ++ return jlong_zero; ++ } else { ++ const char *msg; ++ int ret = inflateInit2(strm, windowBits); ++ switch (ret) { ++ case Z_OK: ++ return ptr_to_jlong(strm); ++ case Z_MEM_ERROR: ++ free(strm); ++ JNU_ThrowOutOfMemoryError(env, 0); ++ return jlong_zero; ++ default: ++ msg = ((strm->msg != NULL) ? strm->msg : ++ (ret == Z_VERSION_ERROR) ? ++ "zlib returned Z_VERSION_ERROR: " ++ "compile time and runtime zlib implementations differ" : ++ (ret == Z_STREAM_ERROR) ? ++ "inflateInit2 returned Z_STREAM_ERROR" : ++ "unknown error initializing zlib library"); ++ free(strm); ++ JNU_ThrowInternalError(env, msg); ++ return jlong_zero; ++ } ++ } ++} ++ + static void checkSetDictionaryResult(JNIEnv *env, jlong addr, int res) + { + switch (res) { +@@ -137,7 +172,7 @@ static jint doInflate(jlong addr, + strm->avail_in = inputLen; + strm->avail_out = outputLen; + +- ret = inflate(strm, Z_PARTIAL_FLUSH); ++ ret = inflate(strm, inflaterFlushType); + return ret; + } + +diff --git a/test/jdk/java/util/zip/GZIP/GZIPOutputStreamHeaderTest.java b/test/jdk/java/util/zip/GZIP/GZIPOutputStreamHeaderTest.java +index 93c2e91fe..4038ad9a7 100644 +--- a/test/jdk/java/util/zip/GZIP/GZIPOutputStreamHeaderTest.java ++++ b/test/jdk/java/util/zip/GZIP/GZIPOutputStreamHeaderTest.java +@@ -35,6 +35,10 @@ import java.util.zip.GZIPOutputStream; + * @bug 8244706 + * @summary Verify that the OS header flag in the stream written out by java.util.zip.GZIPOutputStream + * has the correct expected value ++ * @comment This test case is not suitable for GZIP-zip feature testing, the ninth byte in the header ++ * of the gzip file identifies the operating system that generated the file. By default, the byte of ++ * the compressed package generated by JDK is 0xff (OS_UNKNOWN). KAE-zip relies on the underlying ++ * zlib library to write header files, this field is set to 0x03 (OS_UNIX). Therefore, this case will fail + * @run testng GZIPOutputStreamHeaderTest + */ + public class GZIPOutputStreamHeaderTest { +-- +2.47.0.windows.2 + diff --git a/huawei-Fix-arm32-build-failed-undefined-reference.patch b/huawei-Fix-arm32-build-failed-undefined-reference.patch new file mode 100644 index 0000000000000000000000000000000000000000..403d7969cb72266a37db6a9f0a5dc724f05c0c79 --- /dev/null +++ b/huawei-Fix-arm32-build-failed-undefined-reference.patch @@ -0,0 +1,23 @@ +Date: Thu, 13 Mar 2025 11:54:22 +0800 +Subject: Fix arm32 build failed undefined reference + +--- + src/hotspot/os_cpu/linux_arm/linux_arm_32.S | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/src/hotspot/os_cpu/linux_arm/linux_arm_32.S b/src/hotspot/os_cpu/linux_arm/linux_arm_32.S +index eb560d8f0..cfac173b1 100644 +--- a/src/hotspot/os_cpu/linux_arm/linux_arm_32.S ++++ b/src/hotspot/os_cpu/linux_arm/linux_arm_32.S +@@ -28,8 +28,6 @@ + # point or use it in the same manner as does the server + # compiler. + +- .globl _Copy_conjoint_bytes +- .type _Copy_conjoint_bytes, %function + .globl _Copy_arrayof_conjoint_bytes + .type _Copy_arrayof_conjoint_bytes, %function + .globl _Copy_disjoint_words +-- +2.47.0.windows.2 + diff --git a/huawei-fix-build-fail-realpath.patch b/huawei-fix-build-fail-realpath.patch new file mode 100644 index 0000000000000000000000000000000000000000..e2ccc4e1e2466c081f3aaaacd83dc7b575966ba6 --- /dev/null +++ b/huawei-fix-build-fail-realpath.patch @@ -0,0 +1,57 @@ +--- + src/hotspot/cpu/s390/vm_version_s390.cpp | 9 --------- + src/hotspot/share/jbolt/jBoltDcmds.cpp | 9 ++++++++- + 2 files changed, 8 insertions(+), 10 deletions(-) + +diff --git a/src/hotspot/cpu/s390/vm_version_s390.cpp b/src/hotspot/cpu/s390/vm_version_s390.cpp +index 32b6a07ff..5ee2b66d3 100644 +--- a/src/hotspot/cpu/s390/vm_version_s390.cpp ++++ b/src/hotspot/cpu/s390/vm_version_s390.cpp +@@ -301,16 +301,6 @@ void VM_Version::initialize() { + FLAG_SET_DEFAULT(UsePopCountInstruction, true); + } + +- if (UnlockExperimentalVMOptions && UseHashMapIntegerCache && !FLAG_IS_DEFAULT(UseHashMapIntegerCache)) { +- FLAG_SET_DEFAULT(UseHashMapIntegerCache, false); +- warning("HashMap optimization is not supported in this VM."); +- } +- +- if (UnlockExperimentalVMOptions && UseFastSerializer && !FLAG_IS_DEFAULT(UseFastSerializer)) { +- FLAG_SET_DEFAULT(UseFastSerializer, false); +- warning("Serializer optimization is not supported in this VM."); +- } +- + // z/Architecture supports 8-byte compare-exchange operations + // (see Atomic::cmpxchg) + // and 'atomic long memory ops' (see Unsafe_GetLongVolatile). +diff --git a/src/hotspot/share/jbolt/jBoltDcmds.cpp b/src/hotspot/share/jbolt/jBoltDcmds.cpp +index 0cf1c75b4..7e9ba52a8 100644 +--- a/src/hotspot/share/jbolt/jBoltDcmds.cpp ++++ b/src/hotspot/share/jbolt/jBoltDcmds.cpp +@@ -24,6 +24,9 @@ + #include "jbolt/jBoltDcmds.hpp" + #include "jbolt/jBoltControlThread.hpp" + #include "jbolt/jBoltManager.hpp" ++#ifdef __linux__ ++#include "os_posix.hpp" ++#endif + + bool register_jbolt_dcmds() { + uint32_t full_export = DCmd_Source_Internal | DCmd_Source_AttachAPI | DCmd_Source_MBean; +@@ -198,8 +201,12 @@ void JBoltDumpDCmd::execute(DCmdSource source, TRAPS) { + output()->print_cr("Failed: File open error or NULL: %s", path); + break; + case JBoltOK: +- rp = realpath(path, buffer); ++#ifdef __linux__ ++ rp = os::Posix::realpath(path, buffer, sizeof(buffer)); + output()->print_cr("Successful: Dump to %s", buffer); ++#else ++ output()->print_cr("Successful: Dump to %s", path); ++#endif + break; + default: + ShouldNotReachHere(); +-- +2.31.1 + diff --git a/huawei-heap-dump-redact-support.patch b/huawei-heap-dump-redact-support.patch new file mode 100644 index 0000000000000000000000000000000000000000..d2666fa8c5bacce0481b51647f447ad4f3318ad1 --- /dev/null +++ b/huawei-heap-dump-redact-support.patch @@ -0,0 +1,4084 @@ +Date: Fri, 14 Mar 2025 22:08:38 +0800 +Subject: heap-dump-redact-support + +--- + src/hotspot/os/linux/os_linux.cpp | 37 +- + src/hotspot/os/linux/os_linux.hpp | 54 ++ + src/hotspot/share/oops/annotations.hpp | 1 + + src/hotspot/share/runtime/arguments.cpp | 24 + + src/hotspot/share/runtime/globals.hpp | 18 + + src/hotspot/share/runtime/vmStructs.cpp | 12 +- + src/hotspot/share/services/heapDumper.cpp | 693 +++++++++++++++++- + src/hotspot/share/services/heapRedactor.cpp | 676 +++++++++++++++++ + src/hotspot/share/services/heapRedactor.hpp | 225 ++++++ + src/hotspot/share/services/writeableFlags.cpp | 7 + + .../share/classes/sun/jvm/hotspot/HSDB.java | 19 +- + .../classes/sun/jvm/hotspot/SALauncher.java | 46 +- + .../sun/jvm/hotspot/oops/Annotation.java | 70 ++ + .../classes/sun/jvm/hotspot/oops/Field.java | 10 + + .../sun/jvm/hotspot/oops/InstanceKlass.java | 6 + + .../classes/sun/jvm/hotspot/tools/JMap.java | 63 +- + .../hotspot/utilities/AnnotationArray2D.java | 62 ++ + .../hotspot/utilities/HeapHprofBinWriter.java | 419 ++++++++++- + .../jvm/hotspot/utilities/HeapRedactor.java | 454 ++++++++++++ + .../share/classes/sun/tools/jmap/JMap.java | 258 ++++++- + 20 files changed, 3088 insertions(+), 66 deletions(-) + create mode 100644 src/hotspot/share/services/heapRedactor.cpp + create mode 100644 src/hotspot/share/services/heapRedactor.hpp + create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Annotation.java + create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/AnnotationArray2D.java + create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapRedactor.java + +diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp +index 3cb529bc8..43ee4b7db 100644 +--- a/src/hotspot/os/linux/os_linux.cpp ++++ b/src/hotspot/os/linux/os_linux.cpp +@@ -4587,6 +4587,12 @@ os::Linux::jboltLog_precalc_t os::Linux::_jboltLog_precalc; + os::Linux::jboltLog_do_t os::Linux::_jboltLog_do; + os::Linux::jboltMerge_judge_t os::Linux::_jboltMerge_judge; + #endif // INCLUDE_JBOLT ++os::Linux::heap_dict_add_t os::Linux::_heap_dict_add; ++os::Linux::heap_dict_lookup_t os::Linux::_heap_dict_lookup; ++os::Linux::heap_dict_free_t os::Linux::_heap_dict_free; ++os::Linux::heap_vector_add_t os::Linux::_heap_vector_add; ++os::Linux::heap_vector_get_next_t os::Linux::_heap_vector_get_next; ++os::Linux::heap_vector_free_t os::Linux::_heap_vector_free; + + void os::Linux::load_plugin_library() { + +@@ -4595,6 +4601,12 @@ void os::Linux::load_plugin_library() { + _jboltLog_do = CAST_TO_FN_PTR(jboltLog_do_t, dlsym(RTLD_DEFAULT, "JBoltLog_DO")); + _jboltMerge_judge = CAST_TO_FN_PTR(jboltMerge_judge_t, dlsym(RTLD_DEFAULT, "JBoltMerge_Judge")); + #endif // INCLUDE_JBOLT ++ _heap_dict_add = CAST_TO_FN_PTR(heap_dict_add_t, dlsym(RTLD_DEFAULT, "HeapDict_Add")); ++ _heap_dict_lookup = CAST_TO_FN_PTR(heap_dict_lookup_t, dlsym(RTLD_DEFAULT, "HeapDict_Lookup")); ++ _heap_dict_free = CAST_TO_FN_PTR(heap_dict_free_t, dlsym(RTLD_DEFAULT, "HeapDict_Free")); ++ _heap_vector_add = CAST_TO_FN_PTR(heap_vector_add_t, dlsym(RTLD_DEFAULT, "HeapVector_Add")); ++ _heap_vector_get_next = CAST_TO_FN_PTR(heap_vector_get_next_t, dlsym(RTLD_DEFAULT, "HeapVector_GetNext")); ++ _heap_vector_free= CAST_TO_FN_PTR(heap_vector_free_t, dlsym(RTLD_DEFAULT, "HeapVector_Free")); + + char path[JVM_MAXPATHLEN]; + char ebuf[1024]; +@@ -4612,12 +4624,31 @@ void os::Linux::load_plugin_library() { + _jboltLog_do = CAST_TO_FN_PTR(jboltLog_do_t, dlsym(handle, "JBoltLog_DO")); + } + if (_jboltMerge_judge == NULL) { +- _jboltMerge_judge = CAST_TO_FN_PTR(jboltMerge_judge_t, dlsym(handle, "JBoltMerge_Judge")); ++ _jboltMerge_judge = CAST_TO_FN_PTR(jboltMerge_judge_t, dlsym(handle, "JBoltMerge_Judge")); + } + #endif // INCLUDE_JBOLT ++ ++ if(_heap_dict_add == NULL) { ++ _heap_dict_add = CAST_TO_FN_PTR(heap_dict_add_t, dlsym(handle, "HeapDict_Add")); ++ } ++ if(_heap_dict_lookup == NULL) { ++ _heap_dict_lookup = CAST_TO_FN_PTR(heap_dict_lookup_t, dlsym(handle, "HeapDict_Lookup")); ++ } ++ if(_heap_dict_free == NULL) { ++ _heap_dict_free = CAST_TO_FN_PTR(heap_dict_free_t, dlsym(handle, "HeapDict_Free")); ++ } ++ if(_heap_vector_add == NULL) { ++ _heap_vector_add = CAST_TO_FN_PTR(heap_vector_add_t, dlsym(handle, "HeapVector_Add")); ++ } ++ if(_heap_vector_get_next == NULL) { ++ _heap_vector_get_next = CAST_TO_FN_PTR(heap_vector_get_next_t, dlsym(handle, "HeapVector_GetNext")); ++ } ++ if(_heap_vector_free == NULL) { ++ _heap_vector_free= CAST_TO_FN_PTR(heap_vector_free_t, dlsym(handle, "HeapVector_Free")); ++ } + } + +- JBOLT_ONLY(log_debug(jbolt)("Plugin library for JBolt: %s %s %s", BOOL_TO_STR(_jboltLog_precalc != nullptr), ++ JBOLT_ONLY(log_debug(jbolt)("Plugin library for JBolt: %s %s %s", BOOL_TO_STR(_jboltLog_precalc != nullptr), + BOOL_TO_STR(_jboltLog_do != nullptr), + BOOL_TO_STR(_jboltMerge_judge != nullptr));) + } +@@ -4747,6 +4778,8 @@ jint os::init_2(void) { + init_adjust_stacksize_for_guard_pages(); + #endif + ++ Linux::load_plugin_library(); ++ + if (UseNUMA || UseNUMAInterleaving) { + Linux::numa_init(); + } +diff --git a/src/hotspot/os/linux/os_linux.hpp b/src/hotspot/os/linux/os_linux.hpp +index 4192c90bd..871581f58 100644 +--- a/src/hotspot/os/linux/os_linux.hpp ++++ b/src/hotspot/os/linux/os_linux.hpp +@@ -223,6 +223,20 @@ class os::Linux { + static jboltLog_do_t _jboltLog_do; + static jboltMerge_judge_t _jboltMerge_judge; + #endif ++ ++ typedef void* (*heap_dict_add_t)(void* key, void* val, void* heap_dict, uint8_t type); ++ typedef void* (*heap_dict_lookup_t)(void* key, void* heap_dict, bool deletable); ++ typedef void (*heap_dict_free_t)(void* heap_dict, bool is_nested); ++ typedef void* (*heap_vector_add_t)(void* val, void* heap_vector, bool &_inserted); ++ typedef void* (*heap_vector_get_next_t)(void* heap_vector, void* heap_vector_node, int &_cnt, void** &_items); ++ typedef void (*heap_vector_free_t)(void* heap_vector); ++ static heap_dict_add_t _heap_dict_add; ++ static heap_dict_lookup_t _heap_dict_lookup; ++ static heap_dict_free_t _heap_dict_free; ++ static heap_vector_add_t _heap_vector_add; ++ static heap_vector_get_next_t _heap_vector_get_next; ++ static heap_vector_free_t _heap_vector_free; ++ + static sched_getcpu_func_t _sched_getcpu; + static numa_node_to_cpus_func_t _numa_node_to_cpus; + static numa_node_to_cpus_v2_func_t _numa_node_to_cpus_v2; +@@ -459,6 +473,46 @@ class os::Linux { + return -1; + } + #endif // INCLUDE_JBOLT ++ ++ static void* heap_dict_add(void* key, void* val, void* heap_dict, uint8_t type) { ++ if(_heap_dict_add == NULL) { ++ return NULL; ++ } ++ return _heap_dict_add(key, val, heap_dict, type); ++ } ++ ++ static void* heap_dict_lookup(void* key, void* heap_dict, bool deletable) { ++ if(_heap_dict_lookup == NULL) { ++ return NULL; ++ } ++ return _heap_dict_lookup(key, heap_dict, deletable); ++ }; ++ ++ static void heap_dict_free(void* heap_dict, bool is_nested) { ++ if(_heap_dict_free != NULL) { ++ _heap_dict_free(heap_dict, is_nested); ++ } ++ } ++ ++ static void* heap_vector_add(void* val, void* heap_vector, bool &_inserted) { ++ if(_heap_vector_add == NULL) { ++ return NULL; ++ } ++ return _heap_vector_add(val, heap_vector, _inserted); ++ } ++ ++ static void* heap_vector_get_next(void* heap_vector, void* heap_vector_node, int &_cnt, void** &_items) { ++ if(_heap_vector_get_next == NULL) { ++ return NULL; ++ } ++ return _heap_vector_get_next(heap_vector, heap_vector_node, _cnt, _items); ++ } ++ ++ static void heap_vector_free(void* heap_vector) { ++ if(_heap_vector_free != NULL) { ++ _heap_vector_free(heap_vector); ++ } ++ } + }; + + #endif // OS_LINUX_OS_LINUX_HPP +diff --git a/src/hotspot/share/oops/annotations.hpp b/src/hotspot/share/oops/annotations.hpp +index c7919ff0f..0260de2d8 100644 +--- a/src/hotspot/share/oops/annotations.hpp ++++ b/src/hotspot/share/oops/annotations.hpp +@@ -41,6 +41,7 @@ typedef Array AnnotationArray; + // a type_annotation instance. + + class Annotations: public MetaspaceObj { ++ friend class VMStructs; + friend class JVMCIVMStructs; + + // If you add a new field that points to any metaspace object, you +diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp +index 2e2a9a0f2..dd053e458 100644 +--- a/src/hotspot/share/runtime/arguments.cpp ++++ b/src/hotspot/share/runtime/arguments.cpp +@@ -58,6 +58,7 @@ + #include "runtime/vm_version.hpp" + #include "services/management.hpp" + #include "services/nmtCommon.hpp" ++#include "services/heapRedactor.hpp" + #include "utilities/align.hpp" + #include "utilities/debug.hpp" + #include "utilities/defaultStream.hpp" +@@ -3691,6 +3692,29 @@ jint Arguments::match_special_option_and_act(const JavaVMInitArgs* args, + vm_exit(0); + } + ++ if (match_option(option, "-XX:HeapDumpRedact", &tail)) { ++ // HeapDumpRedact arguments. ++ if (!HeapRedactor::check_launcher_heapdump_redact_support(tail)) { ++ warning("Heap dump redacting did not setup properly, using wrong argument?"); ++ vm_exit_during_initialization("Syntax error, expecting -XX:HeapDumpRedact=[off|names|basic|full|diyrules|annotation]",NULL); ++ } ++ continue; ++ } ++ ++ // heapDump redact password ++ if(match_option(option, "-XX:RedactPassword=", &tail)) { ++ if(tail == NULL || strlen(tail) == 0) { ++ VerifyRedactPassword = false; ++ } else { ++ char* split_char = strstr(const_cast(tail), ","); ++ VerifyRedactPassword = !(split_char == NULL || strlen(split_char) < SALT_LEN); ++ } ++ if(!VerifyRedactPassword) { ++ jio_fprintf(defaultStream::output_stream(), "redact password is null or with bad format, disable verify heap dump authority.\n"); ++ } ++ } ++ ++ + #ifndef PRODUCT + if (match_option(option, "-XX:+PrintFlagsWithComments")) { + JVMFlag::printFlags(tty, true); +diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp +index 231c2ef16..5c028a21d 100644 +--- a/src/hotspot/share/runtime/globals.hpp ++++ b/src/hotspot/share/runtime/globals.hpp +@@ -565,6 +565,24 @@ const int ObjectAlignmentInBytes = 8; + "compression. Otherwise the level must be between 1 and 9.") \ + range(0, 9) \ + \ ++ product(ccstr, HeapDumpRedact, NULL, MANAGEABLE, \ ++ "Redact the heapdump information to remove sensitive data") \ ++ \ ++ product(ccstr, RedactMap, NULL, MANAGEABLE, \ ++ "Redact the class and field names to other strings") \ ++ \ ++ product(ccstr, RedactMapFile, NULL, MANAGEABLE, \ ++ "File path of the Redact Map") \ ++ \ ++ product(ccstr, RedactClassPath, NULL, MANAGEABLE, \ ++ "full path of the Redact Annotation") \ ++ \ ++ product(bool, VerifyRedactPassword, false, \ ++ "verify authority for operating heapDump redact feature") \ ++ \ ++ product(ccstr, RedactPassword, NULL, \ ++ "authority for operating heapDump redact feature, format {password,salt}, salt length >= 8") \ ++ \ + product(ccstr, NativeMemoryTracking, DEBUG_ONLY("summary") NOT_DEBUG("off"), \ + "Native memory tracking options") \ + \ +diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp +index 827cb0cdf..15b1102cb 100644 +--- a/src/hotspot/share/runtime/vmStructs.cpp ++++ b/src/hotspot/share/runtime/vmStructs.cpp +@@ -325,7 +325,11 @@ + nonstatic_field(Symbol, _body[0], u1) \ + nonstatic_field(TypeArrayKlass, _max_length, jint) \ + nonstatic_field(OopHandle, _obj, oop*) \ +- \ ++ nonstatic_field(Annotations, _class_annotations, Array*) \ ++ nonstatic_field(Annotations, _class_type_annotations, Array*) \ ++ nonstatic_field(Annotations, _fields_annotations, Array*>*) \ ++ nonstatic_field(Annotations, _fields_type_annotations, Array*>*) \ ++ \ + /***********************/ \ + /* Constant Pool Cache */ \ + /***********************/ \ +@@ -477,6 +481,8 @@ + nonstatic_field(Array, _data[0], Klass*) \ + nonstatic_field(Array, _length, int) \ + nonstatic_field(Array, _data[0], ResolvedIndyEntry) \ ++ nonstatic_field(Array*>, _length, int) \ ++ nonstatic_field(Array*>, _data[0], Array*) \ + \ + /*******************/ \ + /* GrowableArrays */ \ +@@ -1032,6 +1038,7 @@ + unchecked_nonstatic_field(Array, _data, sizeof(Method*)) \ + unchecked_nonstatic_field(Array, _data, sizeof(Klass*)) \ + unchecked_nonstatic_field(Array, _data, sizeof(ResolvedIndyEntry)) \ ++ unchecked_nonstatic_field(Array*>, _data, sizeof(Array*)) \ + \ + /*********************************/ \ + /* java_lang_Class fields */ \ +@@ -1238,6 +1245,7 @@ + declare_type(Method, Metadata) \ + declare_type(MethodCounters, MetaspaceObj) \ + declare_type(ConstMethod, MetaspaceObj) \ ++ declare_type(Annotations, MetaspaceObj) \ + \ + declare_toplevel_type(MethodData::CompilerCounters) \ + \ +@@ -1960,6 +1969,7 @@ + declare_type(Array, MetaspaceObj) \ + declare_type(Array, MetaspaceObj) \ + declare_type(Array, MetaspaceObj) \ ++ declare_type(Array*>, MetaspaceObj) \ + declare_type(Array, MetaspaceObj) \ + \ + declare_toplevel_type(BitMap) \ +diff --git a/src/hotspot/share/services/heapDumper.cpp b/src/hotspot/share/services/heapDumper.cpp +index b426f5d4c..82e721c17 100644 +--- a/src/hotspot/share/services/heapDumper.cpp ++++ b/src/hotspot/share/services/heapDumper.cpp +@@ -54,11 +54,13 @@ + #include "runtime/vframe.hpp" + #include "runtime/vmOperations.hpp" + #include "runtime/vmThread.hpp" ++#include "runtime/fieldDescriptor.inline.hpp" + #include "services/heapDumper.hpp" + #include "services/heapDumperCompression.hpp" + #include "services/threadService.hpp" + #include "utilities/macros.hpp" + #include "utilities/ostream.hpp" ++#include "services/heapRedactor.hpp" + + /* + * HPROF binary format - description copied from: +@@ -413,6 +415,8 @@ class AbstractDumpWriter : public StackObj { + + void write_address(address a); + ++ HeapRedactor* redactor; ++ + public: + AbstractDumpWriter() : + _buffer(nullptr), +@@ -427,6 +431,7 @@ class AbstractDumpWriter : public StackObj { + size_t position() const { return _pos; } + // writer functions + virtual void write_raw(const void* s, size_t len); ++ void write_zero_raw(void* s, size_t len); + void write_u1(u1 x); + void write_u2(u2 x); + void write_u4(u4 x); +@@ -452,6 +457,12 @@ class AbstractDumpWriter : public StackObj { + // Force flush to guarantee data from parallel dumper are written. + flush(true); + } ++ ++ // remove sensitive data from heapdump information ++ void setHeapRedactor(HeapRedactor* value); ++ HeapRedactor* heapRedactor(); ++ HeapDumpRedactLevel getHeapDumpRedactLevel(); ++ + // Called when finished to release the threads. + virtual void deactivate() = 0; + }; +@@ -489,6 +500,27 @@ void AbstractDumpWriter::write_raw(const void* s, size_t len) { + set_position(position() + len); + } + ++void AbstractDumpWriter::write_zero_raw(void* s, size_t len) { ++ assert(!_in_dump_segment || (_sub_record_left >= len), "sub-record too large"); ++ debug_only(_sub_record_left -= len); ++ ++ // flush buffer to make room. ++ while (len > buffer_size() - position()) { ++ assert(!_in_dump_segment || _is_huge_sub_record, ++ "Cannot overflow in non-huge sub-record."); ++ ++ size_t to_write = buffer_size() - position(); ++ memset(buffer() + position(), 0, to_write); ++ s = (void*) ((char*) s + to_write); ++ len -= to_write; ++ set_position(position() + to_write); ++ flush(); ++ } ++ ++ memset(buffer() + position(), 0, len); ++ set_position(position() + len); ++} ++ + // Makes sure we inline the fast write into the write_u* functions. This is a big speedup. + #define WRITE_KNOWN_TYPE(p, len) do { if (can_write_fast((len))) write_fast((p), (len)); \ + else write_raw((p), (len)); } while (0) +@@ -608,6 +640,21 @@ void AbstractDumpWriter::end_sub_record() { + debug_only(_sub_record_ended = true); + } + ++void AbstractDumpWriter::setHeapRedactor(HeapRedactor* value) { ++ redactor = value; ++} ++ ++HeapRedactor* AbstractDumpWriter::heapRedactor() { ++ return redactor; ++} ++ ++HeapDumpRedactLevel AbstractDumpWriter::getHeapDumpRedactLevel() { ++ if(redactor==NULL){ ++ return REDACT_OFF; ++ } ++ return redactor->redact_level(); ++} ++ + // Supports I/O operations for a dump + + class DumpWriter : public AbstractDumpWriter { +@@ -886,6 +933,9 @@ Monitor* ParDumpWriter::_lock = nullptr; + class DumperClassCacheTable; + class DumperClassCacheTableEntry; + ++typedef char* (*CALL_DO_LOOKUP_REPLACE_VALUE)(AbstractDumpWriter*, typeArrayOop); ++typedef void (*CALL_DUMP_INSTANCE_FIELDS_DESCRIPTORS)(AbstractDumpWriter*, Klass*); ++typedef void (*CALL_DUMP_PRIM_ARRAY)(AbstractDumpWriter*, typeArrayOop); + // Support class with a collection of functions used when dumping the heap + class DumperSupport : AllStatic { + public: +@@ -915,14 +965,26 @@ class DumperSupport : AllStatic { + static void dump_static_fields(AbstractDumpWriter* writer, Klass* k); + // dump the raw values of the instance fields of the given object + static void dump_instance_fields(AbstractDumpWriter* writer, oop o, DumperClassCacheTableEntry* class_cache_entry); ++ // dump the redact values of the instance fields of the given object ++ static void dump_instance_redact_fields(AbstractDumpWriter* writer, oop o, DumperClassCacheTableEntry* class_cache_entry, void* replace_value_table, uint container_id); + // get the count of the instance fields for a given class + static u2 get_instance_fields_count(InstanceKlass* ik); + // dumps the definition of the instance fields for a given class + static void dump_instance_field_descriptors(AbstractDumpWriter* writer, Klass* k); ++ // dumps the definition of the instance fields for a given class ++ static void dump_instance_annotation_field_descriptors(AbstractDumpWriter* writer, Klass* k); ++ // dumps the definition of the instance fields for a given class ++ static void dump_instance_diyrules_field_descriptors(AbstractDumpWriter* writer, Klass* k); + // creates HPROF_GC_INSTANCE_DUMP record for the given object + static void dump_instance(AbstractDumpWriter* writer, oop o, DumperClassCacheTable* class_cache); ++ // creates HPROF_GC_INSTANCE_REDACT_DUMP record for the given object ++ static void dump_redact_instance(AbstractDumpWriter* writer, oop o, DumperClassCacheTable* class_cache, uint container_id); ++ // lookup different value type depend on redact mode ++ static char* do_lookup_replace_value_with_symbol(AbstractDumpWriter* writer, typeArrayOop array); ++ static char* do_lookup_replace_value_with_char(AbstractDumpWriter* writer, typeArrayOop array); ++ static bool dump_replace_value(CALL_DO_LOOKUP_REPLACE_VALUE fn, AbstractDumpWriter* writer, typeArrayOop array, uint container_id); + // creates HPROF_GC_CLASS_DUMP record for the given instance class +- static void dump_instance_class(AbstractDumpWriter* writer, Klass* k); ++ static void dump_instance_class(CALL_DUMP_INSTANCE_FIELDS_DESCRIPTORS fn, AbstractDumpWriter* writer, Klass* k); + // creates HPROF_GC_CLASS_DUMP record for a given array class + static void dump_array_class(AbstractDumpWriter* writer, Klass* k); + +@@ -930,15 +992,22 @@ class DumperSupport : AllStatic { + static void dump_object_array(AbstractDumpWriter* writer, objArrayOop array); + // creates HPROF_GC_PRIM_ARRAY_DUMP record for the given type array + static void dump_prim_array(AbstractDumpWriter* writer, typeArrayOop array); ++ // creates HPROF_GC_PRIM_ARRAY_REDACT_DUMP record for the given type array ++ static void redact_basic_dump_prim_array(AbstractDumpWriter* writer, typeArrayOop array); ++ static void redact_replace_dump_prim_array(CALL_DO_LOOKUP_REPLACE_VALUE fn, AbstractDumpWriter* writer, typeArrayOop array, uint container_id); ++ static void redact_dump_prim_array(CALL_DUMP_PRIM_ARRAY fn, AbstractDumpWriter* dumpWriter, typeArrayOop o); + // create HPROF_FRAME record for the given method and bci + static void dump_stack_frame(AbstractDumpWriter* writer, int frame_serial_num, int class_serial_num, Method* m, int bci); + + // check if we need to truncate an array +- static int calculate_array_max_length(AbstractDumpWriter* writer, arrayOop array, short header_size); ++ static int calculate_array_max_length(AbstractDumpWriter* writer, arrayOop array, short header_size, int char_length = 0); + + // fixes up the current dump record and writes HPROF_HEAP_DUMP_END record + static void end_of_dump(AbstractDumpWriter* writer); + ++ // is_large function, if oop is large object, retrun true ++ static bool is_large(oop o); ++ + static oop mask_dormant_archived_object(oop o, oop ref_obj) { + if (o != nullptr && o->klass()->java_mirror_no_keepalive() == nullptr) { + // Ignore this object since the corresponding java mirror is not loaded. +@@ -974,6 +1043,7 @@ class DumperClassCacheTableEntry : public CHeapObj { + private: + GrowableArray _sigs_start; + GrowableArray _offsets; ++ GrowableArray

_name_symbol_addrs; + u4 _instance_size; + int _entries; + +@@ -983,6 +1053,7 @@ public: + int field_count() { return _entries; } + char sig_start(int field_idx) { return _sigs_start.at(field_idx); } + int offset(int field_idx) { return _offsets.at(field_idx); } ++ address name_symbol_addrs(int field_idx) { return _name_symbol_addrs.at(field_idx); } + u4 instance_size() { return _instance_size; } + }; + +@@ -1033,6 +1104,7 @@ public: + Symbol* sig = fld.signature(); + entry->_sigs_start.push(sig->char_at(0)); + entry->_offsets.push(fld.offset()); ++ entry->_name_symbol_addrs.push((address)((uintptr_t)fld.name())); + entry->_entries++; + entry->_instance_size += DumperSupport::sig2size(sig); + } +@@ -1309,6 +1381,37 @@ void DumperSupport::dump_instance_fields(AbstractDumpWriter* writer, oop o, Dump + } + } + ++// dump the diyrules values of the instance fields of the given object ++void DumperSupport::dump_instance_redact_fields(AbstractDumpWriter* writer, oop o, DumperClassCacheTableEntry* class_cache_entry, void* replace_value_table, uint container_id) { ++ InstanceKlass* ik = InstanceKlass::cast(o->klass()); ++ ++ for (int idx = 0; idx < class_cache_entry->field_count(); idx++) { ++ ++ char type = class_cache_entry->sig_start(idx); ++ int offset = class_cache_entry->offset(idx); ++ ++ ResourceMark rm; ++ address field_adr = class_cache_entry->name_symbol_addrs(idx); ++ void* replace_value = writer->heapRedactor()->lookup_value(field_adr, replace_value_table, false); ++ ++ if (replace_value != nullptr) { ++ oop field_oop = o->obj_field_access(offset); ++ if (!java_lang_String::is_instance(field_oop)) { ++ // data not completed, skip this field value; ++ writer->write_objectID(nullptr); ++ continue; ++ } ++ ++ typeArrayOop field_value_oop = java_lang_String::value(field_oop); ++ address type_array_addr = cast_from_oop
(field_value_oop); ++ writer->heapRedactor()->insert_anonymous_value(type_array_addr, replace_value, container_id); ++ writer->write_objectID(field_oop); ++ continue; ++ } ++ dump_field_value(writer, type, o, offset); ++ } ++} ++ + // dumps the definition of the instance fields for a given class + u2 DumperSupport::get_instance_fields_count(InstanceKlass* ik) { + u2 field_count = 0; +@@ -1335,6 +1438,97 @@ void DumperSupport::dump_instance_field_descriptors(AbstractDumpWriter* writer, + } + } + ++// dumps the definition of the instance fields for a given class ++void DumperSupport::dump_instance_annotation_field_descriptors(AbstractDumpWriter* writer, Klass* k) { ++ ResourceMark rm; ++ InstanceKlass* ik = InstanceKlass::cast(k); ++ Symbol *class_name_symbol = ik->name(); ++ bool in_exclude_package = false; ++ if (Symbol::is_valid(class_name_symbol)) { ++ char *class_name = class_name_symbol->as_C_string(); ++ in_exclude_package = (strncmp("java/", class_name, 5) == 0) || (strncmp("org/springframework", class_name, 19) == 0); ++ } ++ ++ if(in_exclude_package) { ++ DumperSupport::dump_instance_field_descriptors(writer, k); ++ return; ++ } ++ ++ address obj_adr = (address)((uintptr_t)class_name_symbol); ++ // dump the field descriptors ++ for (JavaFieldStream fld(ik); !fld.done(); fld.next()) { ++ if (!fld.access_flags().is_static()) { ++ Symbol* sig = fld.signature(); ++ Symbol* field_name = fld.name(); ++ ++ writer->write_symbolID(field_name); // name ++ writer->write_u1(sig2tag(sig)); // type ++ ++ if(strcmp(sig->as_C_string(), "Ljava/lang/String;") != 0) { ++ continue; ++ } ++ ++ AnnotationArray *field_annotations = fld.field_descriptor().annotations(); ++ if (field_annotations == nullptr || field_annotations->length() == 0) { ++ continue; ++ } ++ ++ // byte index into field_annotations ++ ConstantPool *cp = fld.field_descriptor().field_holder()->constants(); ++ int byte_i = 0; ++ if (writer->heapRedactor()->lookup_annotation_index_in_constant_pool(field_annotations, cp, byte_i)) { ++ address element_value_addr = (address) field_annotations->adr_at(byte_i); ++ u2 cp_str_index = Bytes::get_Java_u2(element_value_addr); ++ Symbol *element_value_symbol = cp->symbol_at(cp_str_index); ++ ++ address field_adr = (address) ((uintptr_t) field_name); ++ writer->heapRedactor()->insert_class_field_value(obj_adr, field_adr, element_value_symbol); ++ } ++ } ++ } ++} ++ ++// dumps the definition of the instance fields for a given class ++void DumperSupport::dump_instance_diyrules_field_descriptors(AbstractDumpWriter *writer, Klass *k) { ++ ResourceMark rm; ++ InstanceKlass* ik = InstanceKlass::cast(k); ++ Symbol *class_name_symbol = ik->name(); ++ void* redact_class_table = nullptr; ++ bool has_diyrules = false; ++ if (Symbol::is_valid(class_name_symbol)) { ++ char *class_name = class_name_symbol->as_C_string(); ++ redact_class_table = writer->heapRedactor()->lookup_class_rules(class_name); ++ has_diyrules = (redact_class_table != nullptr); ++ } ++ ++ if (!has_diyrules) { ++ DumperSupport::dump_instance_field_descriptors(writer, k); ++ return; ++ } ++ ++ address obj_adr = (address) ((uintptr_t) class_name_symbol); ++ // dump the field descriptors ++ for (JavaFieldStream fld(ik); !fld.done(); fld.next()) { ++ if (!fld.access_flags().is_static()) { ++ Symbol* sig = fld.signature(); ++ Symbol* field_name = fld.name(); ++ ++ writer->write_symbolID(field_name); // name ++ writer->write_u1(sig2tag(sig)); // type ++ ++ if(strcmp(sig->as_C_string(), "Ljava/lang/String;") != 0) { ++ continue; ++ } ++ char *field_name_str = field_name->as_C_string(); ++ char *replace_value = (char *) writer->heapRedactor()->lookup_value(field_name_str, redact_class_table, false); ++ if (replace_value != nullptr) { ++ address field_adr = (address) ((uintptr_t) field_name); ++ writer->heapRedactor()->insert_class_field_value(obj_adr, field_adr, replace_value); ++ } ++ } ++ } ++} ++ + // creates HPROF_GC_INSTANCE_DUMP record for the given object + void DumperSupport::dump_instance(AbstractDumpWriter* writer, oop o, DumperClassCacheTable* class_cache) { + InstanceKlass* ik = InstanceKlass::cast(o->klass()); +@@ -1360,8 +1554,104 @@ void DumperSupport::dump_instance(AbstractDumpWriter* writer, oop o, DumperClass + writer->end_sub_record(); + } + ++// creates HPROF_GC_INSTANCE_REDACT_DUMP record for the given object ++void DumperSupport::dump_redact_instance(AbstractDumpWriter* writer, oop o, DumperClassCacheTable* class_cache, uint container_id) { ++ InstanceKlass* ik = InstanceKlass::cast(o->klass()); ++ ++ DumperClassCacheTableEntry* cache_entry = class_cache->lookup_or_create(ik); ++ ++ u4 is = instance_size(ik); ++ u4 size = 1 + sizeof(address) + 4 + sizeof(address) + 4 + is; ++ ++ writer->start_sub_record(HPROF_GC_INSTANCE_DUMP, size); ++ writer->write_objectID(o); ++ writer->write_u4(STACK_TRACE_ID); ++ ++ // class ID ++ writer->write_classID(ik); ++ ++ // number of bytes that follow ++ writer->write_u4(is); ++ ++ // field values ++ void* replace_value_table = nullptr; ++ InstanceKlass* java_super = ik; ++ do { ++ Symbol * class_name_symbol = java_super->name(); ++ address obj_adr = (address)((uintptr_t)class_name_symbol); ++ replace_value_table = writer->heapRedactor()->lookup_class_value(obj_adr); ++ java_super = java_super->java_super(); ++ } while (replace_value_table == nullptr && java_super != nullptr); ++ ++ bool has_rules = replace_value_table != nullptr; ++ if(has_rules) { ++ dump_instance_redact_fields(writer, o, cache_entry, replace_value_table, container_id); ++ } else { ++ dump_instance_fields(writer, o, cache_entry); ++ } ++ ++ writer->end_sub_record(); ++} ++ ++char* DumperSupport::do_lookup_replace_value_with_symbol(AbstractDumpWriter* writer, typeArrayOop array) { ++ address obj_addr = cast_from_oop
(array); ++ Symbol* anonymous_value_symbol = writer->heapRedactor()->lookup_replace_value(obj_addr); ++ if(anonymous_value_symbol == nullptr) { ++ return nullptr; ++ } ++ return anonymous_value_symbol->as_C_string(); ++} ++ ++char* DumperSupport::do_lookup_replace_value_with_char(AbstractDumpWriter* writer, typeArrayOop array) { ++ address obj_addr = cast_from_oop
(array); ++ char* anonymous_value = writer->heapRedactor()->lookup_replace_value(obj_addr); ++ return anonymous_value; ++} ++ ++bool DumperSupport::dump_replace_value(CALL_DO_LOOKUP_REPLACE_VALUE fn, AbstractDumpWriter* writer, typeArrayOop array, uint container_id) { ++ BasicType type = TypeArrayKlass::cast(array->klass())->element_type(); ++ if(type != T_BYTE) { ++ return false; ++ } ++ ++ // 2 * sizeof(u1) + 2 * sizeof(u4) + sizeof(objectID) ++ short header_size = 2 * 1 + 2 * 4 + sizeof(address); ++ ++ int length = 0; ++ ++ char *anonymous_value = nullptr; ++ anonymous_value = fn(writer, array); ++ if(anonymous_value == nullptr) { ++ // record all array, expect who's replace value can be find now ++ return writer->heapRedactor()->record_typeArrayOop(array, container_id); ++ } ++ ++ size_t char_length = strlen(anonymous_value); ++ length = DumperSupport::calculate_array_max_length(writer, array, header_size, char_length); ++ ++ int type_size = type2aelembytes(type); ++ u4 length_in_bytes = (u4)length * type_size; ++ u4 size = header_size + length_in_bytes; ++ ++ writer->start_sub_record(HPROF_GC_PRIM_ARRAY_DUMP, size); ++ writer->write_objectID(array); ++ writer->write_u4(STACK_TRACE_ID); ++ writer->write_u4(length); ++ writer->write_u1(HPROF_BYTE); ++ ++ // nothing to copy ++ if (length == 0) { ++ writer->end_sub_record(); ++ return true; ++ } ++ ++ writer->write_raw(anonymous_value, char_length); ++ writer->end_sub_record(); ++ return true; ++} ++ + // creates HPROF_GC_CLASS_DUMP record for the given instance class +-void DumperSupport::dump_instance_class(AbstractDumpWriter* writer, Klass* k) { ++void DumperSupport::dump_instance_class(CALL_DUMP_INSTANCE_FIELDS_DESCRIPTORS fn, AbstractDumpWriter* writer, Klass* k) { + InstanceKlass* ik = InstanceKlass::cast(k); + + // We can safepoint and do a heap dump at a point where we have a Klass, +@@ -1411,7 +1701,7 @@ void DumperSupport::dump_instance_class(AbstractDumpWriter* writer, Klass* k) { + + // description of instance fields + writer->write_u2(instance_fields_count); +- dump_instance_field_descriptors(writer, ik); ++ fn(writer, ik); + + writer->end_sub_record(); + } +@@ -1454,12 +1744,11 @@ void DumperSupport::dump_array_class(AbstractDumpWriter* writer, Klass* k) { + + // Hprof uses an u4 as record length field, + // which means we need to truncate arrays that are too long. +-int DumperSupport::calculate_array_max_length(AbstractDumpWriter* writer, arrayOop array, short header_size) { ++int DumperSupport::calculate_array_max_length(AbstractDumpWriter* writer, arrayOop array, short header_size, int char_length) { + BasicType type = ArrayKlass::cast(array->klass())->element_type(); + assert(type >= T_BOOLEAN && type <= T_OBJECT, "invalid array element type"); + +- int length = array->length(); +- ++ int length = char_length == 0 ? array->length() : char_length; + int type_size; + if (type == T_OBJECT) { + type_size = sizeof(address); +@@ -1543,7 +1832,7 @@ void DumperSupport::dump_prim_array(AbstractDumpWriter* writer, typeArrayOop arr + break; + } + case T_BYTE : { +- writer->write_raw(array->byte_at_addr(0), length_in_bytes); ++ writer->write_raw((void*)(array->byte_at_addr(0)), length_in_bytes); + break; + } + case T_CHAR : { +@@ -1601,6 +1890,101 @@ void DumperSupport::dump_prim_array(AbstractDumpWriter* writer, typeArrayOop arr + writer->end_sub_record(); + } + ++// creates HPROF_GC_PRIM_ARRAY_DUMP redact record for the given type array ++void DumperSupport::redact_basic_dump_prim_array(AbstractDumpWriter* writer, typeArrayOop array) { ++ BasicType type = TypeArrayKlass::cast(array->klass())->element_type(); ++ ++ // 2 * sizeof(u1) + 2 * sizeof(u4) + sizeof(objectID) ++ short header_size = 2 * 1 + 2 * 4 + sizeof(address); ++ ++ int length = calculate_array_max_length(writer, array, header_size); ++ int type_size = type2aelembytes(type); ++ u4 length_in_bytes = (u4)length * type_size; ++ u4 size = header_size + length_in_bytes; ++ ++ writer->start_sub_record(HPROF_GC_PRIM_ARRAY_DUMP, size); ++ writer->write_objectID(array); ++ writer->write_u4(STACK_TRACE_ID); ++ writer->write_u4(length); ++ writer->write_u1(type2tag(type)); ++ ++ // nothing to copy ++ if (length == 0) { ++ writer->end_sub_record(); ++ return; ++ } ++ ++ // If the byte ordering is big endian then we can copy most types directly ++ ++ switch (type) { ++ case T_INT : { ++ writer->write_zero_raw((void*)(array->int_at_addr(0)), length_in_bytes); ++ break; ++ } ++ case T_BYTE : { ++ writer->write_zero_raw((void*)(array->byte_at_addr(0)), length_in_bytes); ++ break; ++ } ++ case T_CHAR : { ++ writer->write_zero_raw((void*)(array->char_at_addr(0)), length_in_bytes); ++ break; ++ } ++ case T_SHORT : { ++ if (Endian::is_Java_byte_ordering_different()) { ++ WRITE_ARRAY(array, short, u2, length); ++ } else { ++ writer->write_raw((void*)(array->short_at_addr(0)), length_in_bytes); ++ } ++ break; ++ } ++ case T_BOOLEAN : { ++ if (Endian::is_Java_byte_ordering_different()) { ++ WRITE_ARRAY(array, bool, u1, length); ++ } else { ++ writer->write_raw((void*)(array->bool_at_addr(0)), length_in_bytes); ++ } ++ break; ++ } ++ case T_LONG : { ++ if (Endian::is_Java_byte_ordering_different()) { ++ WRITE_ARRAY(array, long, u8, length); ++ } else { ++ writer->write_raw((void*)(array->long_at_addr(0)), length_in_bytes); ++ } ++ break; ++ } ++ ++ // handle float/doubles in a special value to ensure than NaNs are ++ // written correctly. TO DO: Check if we can avoid this on processors that ++ // use IEEE 754. ++ ++ case T_FLOAT : { ++ for (int i = 0; i < length; i++) { ++ dump_float(writer, array->float_at(i)); ++ } ++ break; ++ } ++ case T_DOUBLE : { ++ for (int i = 0; i < length; i++) { ++ dump_double(writer, array->double_at(i)); ++ } ++ break; ++ } ++ default : ShouldNotReachHere(); ++ } ++ ++ writer->end_sub_record(); ++} ++ ++// creates HPROF_GC_PRIM_ARRAY_DUMP redact record for the given type array ++void DumperSupport::redact_replace_dump_prim_array(CALL_DO_LOOKUP_REPLACE_VALUE fn, AbstractDumpWriter *writer, typeArrayOop array, uint container_id) { ++ if(dump_replace_value(fn, writer, array, container_id)) { ++ return; ++ } ++ ++ DumperSupport::dump_prim_array(writer, array); ++} ++ + // create a HPROF_FRAME record of the given Method* and bci + void DumperSupport::dump_stack_frame(AbstractDumpWriter* writer, + int frame_serial_num, +@@ -1650,6 +2034,39 @@ void SymbolTableDumper::do_symbol(Symbol** p) { + } + } + ++// Support class used to generate HPROF_UTF8 records from the entries in the ++// SymbolTable and Redact the sensitive String. ++ ++class SymbolTableRedactDumper : public SymbolClosure { ++private: ++ AbstractDumpWriter* _writer; ++ AbstractDumpWriter* writer() const { return _writer; } ++public: ++ SymbolTableRedactDumper(AbstractDumpWriter* writer) { _writer = writer; } ++ void do_symbol(Symbol** p); ++}; ++ ++void SymbolTableRedactDumper::do_symbol(Symbol** p) { ++ ResourceMark rm; ++ Symbol* sym = *p; ++ int len = sym->utf8_length(); ++ if (len > 0) { ++ char* s = sym->as_utf8(); ++ ++ char* redact_field = NULL; ++ HeapDumpRedactLevel level = writer()->getHeapDumpRedactLevel(); ++ if((level == REDACT_NAMES || level == REDACT_FULL) && ++ (redact_field = writer()->heapRedactor()->lookup_redact_name(s)) != NULL){ ++ len = (int)strlen(redact_field); ++ s = redact_field; ++ } ++ ++ DumperSupport::write_header(writer(), HPROF_UTF8, oopSize + len); ++ writer()->write_symbolID(sym); ++ writer()->write_raw(s, len); ++ } ++} ++ + // Support class used to generate HPROF_GC_ROOT_JNI_LOCAL records + + class JNILocalsDumper : public OopClosure { +@@ -1809,17 +2226,19 @@ class HeapObjectDumper : public ObjectClosure { + private: + AbstractDumpWriter* _writer; + HeapDumpLargeObjectList* _list; ++ CALL_DUMP_PRIM_ARRAY _redact_dump_prim_array; + + AbstractDumpWriter* writer() { return _writer; } +- bool is_large(oop o); ++ // bool is_large(oop o); + + DumperClassCacheTable _class_cache; + + public: +- HeapObjectDumper(AbstractDumpWriter* writer, HeapDumpLargeObjectList* list = nullptr) { +- _writer = writer; +- _list = list; +- } ++ HeapObjectDumper(AbstractDumpWriter* writer, CALL_DUMP_PRIM_ARRAY fn = DumperSupport::dump_prim_array, HeapDumpLargeObjectList* list = nullptr) { ++ _writer = writer; ++ _list = list; ++ _redact_dump_prim_array = fn; ++ } + + // called for each object in the heap + void do_object(oop o); +@@ -1839,7 +2258,7 @@ void HeapObjectDumper::do_object(oop o) { + + // If large object list exists and it is large object/array, + // add oop into the list and skip scan. VM thread will process it later. +- if (_list != nullptr && is_large(o)) { ++ if (_list != nullptr && DumperSupport::is_large(o)) { + _list->atomic_push(o); + return; + } +@@ -1852,11 +2271,72 @@ void HeapObjectDumper::do_object(oop o) { + DumperSupport::dump_object_array(writer(), objArrayOop(o)); + } else if (o->is_typeArray()) { + // create a HPROF_GC_PRIM_ARRAY_DUMP record for each type array +- DumperSupport::dump_prim_array(writer(), typeArrayOop(o)); ++ DumperSupport::redact_dump_prim_array(_redact_dump_prim_array, writer(), typeArrayOop(o)); + } + } + +-bool HeapObjectDumper::is_large(oop o) { ++void DumperSupport::redact_dump_prim_array(CALL_DUMP_PRIM_ARRAY fn, AbstractDumpWriter* dumpWriter, typeArrayOop o){ ++ fn(dumpWriter, o); ++} ++ ++class HeapObjectRedactDumper : public ObjectClosure { ++private: ++ AbstractDumpWriter* _writer; ++ HeapDumpLargeObjectList* _list; ++ ++ AbstractDumpWriter* writer() { return _writer; } ++ ++ CALL_DO_LOOKUP_REPLACE_VALUE _do_lookup_replace_value; ++ ++ DumperClassCacheTable _class_cache; ++ ++ uint container_id; ++ ++public: ++ HeapObjectRedactDumper(AbstractDumpWriter* writer, CALL_DO_LOOKUP_REPLACE_VALUE do_lookup_replace_value, HeapDumpLargeObjectList* list = nullptr, uint id = 0) { ++ _writer = writer; ++ _list = list; ++ container_id = id; ++ _do_lookup_replace_value = do_lookup_replace_value; ++ } ++ ++ // called for each object in the heap ++ void do_object(oop o); ++}; ++ ++void HeapObjectRedactDumper::do_object(oop o) { ++ // skip classes as these emitted as HPROF_GC_CLASS_DUMP records ++ if (o->klass() == vmClasses::Class_klass()) { ++ if (!java_lang_Class::is_primitive(o)) { ++ return; ++ } ++ } ++ ++ if (DumperSupport::mask_dormant_archived_object(o, nullptr) == nullptr) { ++ log_debug(cds, heap)("skipped dormant archived object " INTPTR_FORMAT " (%s)", p2i(o), o->klass()->external_name()); ++ return; ++ } ++ ++ // If large object list exists and it is large object/array, ++ // and oop into the list and skip scan. VM thread will process it later. ++ if (_list != nullptr && DumperSupport::is_large(o)) { ++ _list->atomic_push(o); ++ return; ++ } ++ ++ if (o->is_instance()) { ++ // create a HPROF_GC_INSTANCE record for each object ++ DumperSupport::dump_redact_instance(writer(), o, &_class_cache, container_id); ++ } else if (o->is_objArray()) { ++ // create a HPROF_GC_OBJ_ARRAY_DUMP record for each object array ++ DumperSupport::dump_object_array(writer(), objArrayOop(o)); ++ } else if (o->is_typeArray()) { ++ // create a HPROF_GC_PRIM_ARRAY_DUMP record for each type array ++ DumperSupport::redact_replace_dump_prim_array(_do_lookup_replace_value, writer(), typeArrayOop(o), container_id); ++ } ++} ++ ++bool DumperSupport::is_large(oop o) { + size_t size = 0; + if (o->is_instance()) { + // Use o->size() * 8 as the upper limit of instance size to avoid iterating static fields +@@ -1941,6 +2421,7 @@ class VM_HeapDumper : public VM_GC_Operation, public WorkerTask { + ThreadStackTrace** _stack_traces; + int _num_threads; + // parallel heap dump support ++ static CALL_DUMP_INSTANCE_FIELDS_DESCRIPTORS _dump_instance_fields_descriptors; + uint _num_dumper_threads; + uint _num_writer_threads; + DumperController* _dumper_controller; +@@ -2033,6 +2514,9 @@ class VM_HeapDumper : public VM_GC_Operation, public WorkerTask { + // HPROF_TRACE and HPROF_FRAME records + void dump_stack_traces(); + ++ // HeapVector Records ++ void do_heapVector(); ++ + // large objects + void dump_large_objects(ObjectClosure* writer); + +@@ -2052,6 +2536,14 @@ class VM_HeapDumper : public VM_GC_Operation, public WorkerTask { + _dumper_controller = nullptr; + _poi = nullptr; + _large_object_list = new (std::nothrow) HeapDumpLargeObjectList(); ++ if(writer->getHeapDumpRedactLevel() == REDACT_ANNOTATION) { ++ _dump_instance_fields_descriptors = DumperSupport::dump_instance_annotation_field_descriptors; ++ } else if(writer->getHeapDumpRedactLevel() == REDACT_DIYRULES) { ++ _dump_instance_fields_descriptors = DumperSupport::dump_instance_diyrules_field_descriptors; ++ } else { ++ _dump_instance_fields_descriptors = DumperSupport::dump_instance_field_descriptors; ++ } ++ + if (oome) { + assert(!Thread::current()->is_VM_thread(), "Dump from OutOfMemoryError cannot be called by the VMThread"); + // get OutOfMemoryError zero-parameter constructor +@@ -2089,6 +2581,7 @@ class VM_HeapDumper : public VM_GC_Operation, public WorkerTask { + + VM_HeapDumper* VM_HeapDumper::_global_dumper = nullptr; + DumpWriter* VM_HeapDumper::_global_writer = nullptr; ++CALL_DUMP_INSTANCE_FIELDS_DESCRIPTORS VM_HeapDumper::_dump_instance_fields_descriptors = nullptr; + + bool VM_HeapDumper::skip_operation() const { + return false; +@@ -2131,8 +2624,8 @@ void VM_HeapDumper::do_load_class(Klass* k) { + // writes a HPROF_GC_CLASS_DUMP record for the given class + void VM_HeapDumper::do_class_dump(Klass* k) { + if (k->is_instance_klass()) { +- DumperSupport::dump_instance_class(writer(), k); +- } else { ++ DumperSupport::dump_instance_class(_dump_instance_fields_descriptors, writer(), k); ++ } else { + DumperSupport::dump_array_class(writer(), k); + } + } +@@ -2317,14 +2810,15 @@ void VM_HeapDumper::doit() { + set_global_writer(); + + WorkerThreads* workers = ch->safepoint_workers(); +- + if (workers == nullptr) { + // Use serial dump, set dumper threads and writer threads number to 1. + _num_dumper_threads=1; + _num_writer_threads=1; ++ writer()->heapRedactor()->init_containers(_num_dumper_threads); + work(0); + } else { + prepare_parallel_dump(workers->active_workers()); ++ writer()->heapRedactor()->init_containers(_num_dumper_threads); + if (_num_dumper_threads > 1) { + ParallelObjectIterator poi(_num_dumper_threads); + _poi = &poi; +@@ -2361,8 +2855,14 @@ void VM_HeapDumper::work(uint worker_id) { + // timestamp is current time in ms + writer()->write_u8(os::javaTimeMillis()); + // HPROF_UTF8 records +- SymbolTableDumper sym_dumper(writer()); +- SymbolTable::symbols_do(&sym_dumper); ++ if(writer()->heapRedactor() != NULL && (writer()->heapRedactor()->redact_level() == REDACT_NAMES || ++ writer()->heapRedactor()->redact_level() == REDACT_FULL)){ ++ SymbolTableRedactDumper sym_dumper(writer()); ++ SymbolTable::symbols_do(&sym_dumper); ++ } else{ ++ SymbolTableDumper sym_dumper(writer()); ++ SymbolTable::symbols_do(&sym_dumper); ++ } + + // write HPROF_LOAD_CLASS records + { +@@ -2403,8 +2903,20 @@ void VM_HeapDumper::work(uint worker_id) { + // of the heap dump. + if (_num_dumper_threads <= 1) { + ResourceMark rm; +- HeapObjectDumper obj_dumper(writer()); +- Universe::heap()->object_iterate(&obj_dumper); ++ if(writer()->heapRedactor() != NULL && (writer()->heapRedactor()->redact_level() == REDACT_BASIC || ++ writer()->heapRedactor()->redact_level() == REDACT_FULL)) { ++ HeapObjectDumper obj_dumper(writer(), DumperSupport::redact_basic_dump_prim_array); ++ Universe::heap()->object_iterate(&obj_dumper); ++ } else if(writer()->heapRedactor() != NULL && writer()->heapRedactor()->redact_level() == REDACT_ANNOTATION) { ++ HeapObjectRedactDumper obj_dumper(writer(), DumperSupport::do_lookup_replace_value_with_symbol); ++ Universe::heap()->object_iterate(&obj_dumper); ++ } else if(writer()->heapRedactor() != NULL && writer()->heapRedactor()->redact_level() == REDACT_DIYRULES) { ++ HeapObjectRedactDumper obj_dumper(writer(), DumperSupport::do_lookup_replace_value_with_char); ++ Universe::heap()->object_iterate(&obj_dumper); ++ } else { ++ HeapObjectDumper obj_dumper(writer()); ++ Universe::heap()->object_iterate(&obj_dumper); ++ } + } else { + assert(get_worker_type(worker_id) == DumperType + || get_worker_type(worker_id) == VMDumperType, +@@ -2418,10 +2930,23 @@ void VM_HeapDumper::work(uint worker_id) { + // Heap iteration. + { + ParDumpWriter pw(writer()); ++ pw.setHeapRedactor(writer()->heapRedactor()); + { + ResourceMark rm; +- HeapObjectDumper obj_dumper(&pw, _large_object_list); +- _poi->object_iterate(&obj_dumper, worker_id); ++ if(writer()->heapRedactor() != NULL && (writer()->heapRedactor()->redact_level() == REDACT_BASIC || ++ writer()->heapRedactor()->redact_level() == REDACT_FULL)) { ++ HeapObjectDumper obj_dumper(&pw, DumperSupport::redact_basic_dump_prim_array, _large_object_list); ++ _poi->object_iterate(&obj_dumper, worker_id); ++ } else if(writer()->heapRedactor() != NULL && writer()->heapRedactor()->redact_level() == REDACT_ANNOTATION) { ++ HeapObjectRedactDumper obj_dumper(&pw, DumperSupport::do_lookup_replace_value_with_symbol, _large_object_list, worker_id); ++ _poi->object_iterate(&obj_dumper, worker_id); ++ } else if(writer()->heapRedactor() != NULL && writer()->heapRedactor()->redact_level() == REDACT_DIYRULES) { ++ HeapObjectRedactDumper obj_dumper(&pw, DumperSupport::do_lookup_replace_value_with_char, _large_object_list, worker_id); ++ _poi->object_iterate(&obj_dumper, worker_id); ++ } else { ++ HeapObjectDumper obj_dumper(&pw, DumperSupport::dump_prim_array, _large_object_list); ++ _poi->object_iterate(&obj_dumper, worker_id); ++ } + } + + if (get_worker_type(worker_id) == VMDumperType) { +@@ -2441,8 +2966,26 @@ void VM_HeapDumper::work(uint worker_id) { + assert(get_worker_type(worker_id) == VMDumperType, "Heap dumper must be VMDumper"); + // Use writer() rather than ParDumpWriter to avoid memory consumption. + ResourceMark rm; +- HeapObjectDumper obj_dumper(writer()); +- dump_large_objects(&obj_dumper); ++ if(writer()->heapRedactor() != NULL && (writer()->heapRedactor()->redact_level() == REDACT_BASIC || ++ writer()->heapRedactor()->redact_level() == REDACT_FULL)) { ++ HeapObjectDumper obj_dumper(writer(), DumperSupport::redact_basic_dump_prim_array); ++ dump_large_objects(&obj_dumper); ++ } else if(writer()->heapRedactor() != NULL && writer()->heapRedactor()->redact_level() == REDACT_ANNOTATION) { ++ HeapObjectRedactDumper obj_dumper(writer(), DumperSupport::do_lookup_replace_value_with_symbol); ++ dump_large_objects(&obj_dumper); ++ } else if(writer()->heapRedactor() != NULL && writer()->heapRedactor()->redact_level() == REDACT_DIYRULES) { ++ HeapObjectRedactDumper obj_dumper(writer(), DumperSupport::do_lookup_replace_value_with_char); ++ dump_large_objects(&obj_dumper); ++ } else { ++ HeapObjectDumper obj_dumper(writer()); ++ dump_large_objects(&obj_dumper); ++ } ++ ++ // if value in INSTANCE is sensitive ++ // and redact level is REDACT_ANNOTATION ++ // writes HeapVector here ++ do_heapVector(); ++ + // Writes the HPROF_HEAP_DUMP_END record. + DumperSupport::end_of_dump(writer()); + // We are done with writing. Release the worker threads. +@@ -2507,6 +3050,68 @@ void VM_HeapDumper::dump_stack_traces() { + } + } + ++void VM_HeapDumper::do_heapVector(){ ++ CALL_DO_LOOKUP_REPLACE_VALUE fn = nullptr; ++ if(writer()->getHeapDumpRedactLevel() == REDACT_ANNOTATION) { ++ fn = DumperSupport::do_lookup_replace_value_with_symbol; ++ } else if(writer()->getHeapDumpRedactLevel() == REDACT_DIYRULES) { ++ fn = DumperSupport::do_lookup_replace_value_with_char; ++ } else { ++ return; ++ } ++ ++ BasicType type = T_BYTE; ++ short header_size = 2 * 1 + 2 * 4 + sizeof(address); ++ int type_size = type2aelembytes(type); ++ uint max_bytes = max_juint - header_size; ++ ++ uint container_nums = writer()->heapRedactor()->get_container_nums(); ++ for (uint container_index = 0; container_index < container_nums; container_index++) { ++ int node_len = 0, i =0; ++ void** items = nullptr; ++ void *vector_node = writer()->heapRedactor()->get_vector_node_next(nullptr, node_len, items, container_index); ++ while (vector_node != NULL && items != NULL) { ++ for (i = 0; i < node_len; i++) { ++ typeArrayOop array = (typeArrayOopDesc*)items[i]; ++ ++ char *anonymous_value = fn(writer(), array); ++ int length = anonymous_value == NULL ? array->length() : strlen(anonymous_value); ++ ++ u4 length_in_bytes = (u4) length * type_size; ++ if (length_in_bytes > max_bytes) { ++ length = max_bytes / type_size; ++ length_in_bytes = (size_t)length * type_size; ++ } ++ u4 size = header_size + length_in_bytes; ++ ++ writer()->start_sub_record(HPROF_GC_PRIM_ARRAY_DUMP, size); ++ writer()->write_objectID(array); ++ writer()->write_u4(STACK_TRACE_ID); ++ writer()->write_u4(length); ++ writer()->write_u1(HPROF_BYTE); ++ ++ // nothing to copy ++ if (length == 0) { ++ writer()->end_sub_record(); ++ continue; ++ } ++ if(anonymous_value != nullptr){ ++ writer()->write_raw(anonymous_value, length); ++ } else { ++ writer()->write_raw((void *) (array->byte_at_addr(0)), length_in_bytes); ++ } ++ writer()->end_sub_record(); ++ } ++ ++ // clear current node info, maybe next node items is NULL, node_len = 0 will skip this NULL point error ++ node_len = 0; ++ items = nullptr; ++ void *temp = writer()->heapRedactor()->get_vector_node_next(vector_node, node_len, items, container_index); ++ vector_node = temp; ++ } ++ } ++} ++ + // dump the large objects. + void VM_HeapDumper::dump_large_objects(ObjectClosure* cl) { + _large_object_list->drain(cl); +@@ -2516,9 +3121,22 @@ void VM_HeapDumper::dump_large_objects(ObjectClosure* cl) { + int HeapDumper::dump(const char* path, outputStream* out, int compression, bool overwrite, uint num_dump_threads) { + assert(path != nullptr && strlen(path) > 0, "path missing"); + ++ const char* path_and_anonymous = path; ++ const char* redact_params = nullptr; ++ // parse args[0] to get real path and redact_params ++ const char* split_char = strstr(path_and_anonymous, ";"); ++ size_t path_length = (split_char == nullptr) ? strlen(path_and_anonymous) : (unsigned long)(split_char - path_and_anonymous); ++ size_t path_and_anonymous_length = strlen(path_and_anonymous); ++ char* _path = NEW_C_HEAP_ARRAY(char, path_and_anonymous_length + 1, mtInternal); ++ strncpy(_path, path_and_anonymous, path_and_anonymous_length + 1); ++ _path[path_length] = '\0'; ++ if (split_char != NULL) { ++ redact_params = split_char + 1; ++ } ++ + // print message in interactive case + if (out != nullptr) { +- out->print_cr("Dumping heap to %s ...", path); ++ out->print_cr("Dumping heap to %s ...", _path); + timer()->start(); + } + // create JFR event +@@ -2535,17 +3153,26 @@ int HeapDumper::dump(const char* path, outputStream* out, int compression, bool + } + } + +- DumpWriter writer(new (std::nothrow) FileWriter(path, overwrite), compressor); ++ HeapRedactor heapRedactor(redact_params, out); ++ DumpWriter writer(new (std::nothrow) FileWriter(_path, overwrite), compressor); ++ if(heapRedactor.redact_level() > REDACT_UNKNOWN) { ++ if(out != NULL) { ++ out->print_cr("HeapDump Redact Level = %s", heapRedactor.get_redact_level_string()); ++ } ++ } ++ writer.setHeapRedactor(&heapRedactor); + + if (writer.error() != nullptr) { + set_error(writer.error()); + if (out != nullptr) { +- out->print_cr("Unable to create %s: %s", path, +- (error() != nullptr) ? error() : "reason unknown"); +- } +- return -1; ++ out->print_cr("Unable to create %s: %s", _path, ++ (error() != NULL) ? error() : "reason unknown"); ++ } ++ FREE_C_HEAP_ARRAY(char, _path); ++ return -1; + } + ++ FREE_C_HEAP_ARRAY(char, _path); + // generate the dump + VM_HeapDumper dumper(&writer, _gc_before_heap_dump, _oome, num_dump_threads); + if (Thread::current()->is_VM_thread()) { +diff --git a/src/hotspot/share/services/heapRedactor.cpp b/src/hotspot/share/services/heapRedactor.cpp +new file mode 100644 +index 000000000..9fe4648df +--- /dev/null ++++ b/src/hotspot/share/services/heapRedactor.cpp +@@ -0,0 +1,676 @@ ++/* ++ * Copyright (c) 2020, 2023, 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. Huawei designates this ++ * particular file as subject to the "Classpath" exception as provided ++ * by Huawei 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 visit https://gitee.com/openeuler/bishengjdk-8 if you need additional ++ * information or have any questions. ++ */ ++ ++#include "runtime/globals.hpp" ++#include "runtime/os.hpp" ++#include "runtime/arguments.hpp" ++#include "utilities/ostream.hpp" ++#include "memory/allocation.hpp" ++#include "memory/allocation.inline.hpp" ++#include "jvm_md.h" ++#include "utilities/debug.hpp" ++#include "services/heapRedactor.hpp" ++#include "logging/log.hpp" ++#include "logging/logLevel.hpp" ++ ++const char* HeapRedactor::REDACT_UNKNOWN_STR = "UNKNOWN"; ++const char* HeapRedactor::REDACT_OFF_STR = "OFF"; ++const char* HeapRedactor::REDACT_NAMES_STR = "NAMES"; ++const char* HeapRedactor::REDACT_BASIC_STR = "BASIC"; ++const char* HeapRedactor::REDACT_ANNOTATION_STR = "ANNOTATION"; ++const char* HeapRedactor::REDACT_DIYRULES_STR = "DIYRULES"; ++const char* HeapRedactor::REDACT_FULL_STR = "FULL"; ++ ++HeapRedactor::HeapRedactor(outputStream* out) { ++ init_fields(); ++ _use_sys_params = true; ++ init(out); ++} ++ ++HeapRedactor::HeapRedactor(const char *redact_params_string, outputStream* out) { ++ init_fields(); ++ if (redact_params_string != NULL && strlen(redact_params_string) > 0) { ++ _use_sys_params = false; ++ parse_redact_params(redact_params_string); ++ } else { ++ _use_sys_params = true; ++ } ++ init(out); ++} ++ ++HeapRedactor::~HeapRedactor() { ++#ifdef LINUX ++ if(_redact_name_table != NULL) { ++ os::Linux::heap_dict_free(_redact_name_table,false); ++ _redact_name_table = NULL; ++ } ++ ++ if(_redact_rules_table != NULL) { ++ os::Linux::heap_dict_free(_redact_rules_table, true); ++ _redact_rules_table = NULL; ++ } ++ ++ if(_annotation_value_tables != NULL) { ++ for(uint i = 0; i < _container_nums; i++) { ++ if(_annotation_value_tables[i] != NULL) { ++ os::Linux::heap_dict_free(_annotation_value_tables[i], false); ++ _annotation_value_tables[i] = NULL; ++ } ++ } ++ } ++ ++ if(_redact_class_field_table != NULL) { ++ os::Linux::heap_dict_free(_redact_class_field_table, true); ++ _redact_class_field_table = NULL; ++ } ++ ++ if(_redact_records!= NULL) { ++ for(uint i = 0; i < _container_nums; i++) { ++ if(_redact_records[i] != NULL) { ++ os::Linux::heap_vector_free(_redact_records[i]); ++ _redact_records[i] = NULL; ++ } ++ } ++ } ++#endif ++ if(_annotation_value_tables != NULL) { ++ FREE_C_HEAP_ARRAY(void*, _annotation_value_tables); ++ } ++ ++ if(_redact_records != NULL) { ++ FREE_C_HEAP_ARRAY(void*, _redact_records); ++ } ++ ++ if(_name_map_list != NULL){ ++ FREE_C_HEAP_ARRAY(char, _name_map_list); ++ } ++ if(_file_name_map_list != NULL){ ++ FREE_C_HEAP_ARRAY(char, _file_name_map_list); ++ } ++ if(_annotation_class_path != NULL) { ++ FREE_C_HEAP_ARRAY(char, _annotation_class_path); ++ } ++ _file_name_map_list = NULL; ++ _name_map_list = NULL; ++ _annotation_class_path = NULL; ++} ++ ++void HeapRedactor::init_fields() { ++ _redact_level = REDACT_UNKNOWN; ++ _redact_name_table = NULL; ++ _redact_rules_table= NULL; ++ _annotation_value_tables = NULL; ++ _redact_records = NULL; ++ _redact_class_field_table = NULL; ++ _file_name_map_list = NULL; ++ _name_map_list = NULL; ++ _redact_class_full_name = NULL; ++ _annotation_class_path = NULL; ++ ++ _redact_params.params_string = NULL; ++ _redact_params.heap_dump_redact = NULL; ++ _redact_params.redact_map = NULL; ++ _redact_params.redact_map_file = NULL; ++ _redact_params.annotation_class_path = NULL; ++ _redact_params.redact_password = NULL; ++} ++ ++void HeapRedactor::parse_redact_params(const char *redact_params_string) { ++ size_t length = strlen(redact_params_string); ++ char* buf = NEW_C_HEAP_ARRAY(char, length + 1, mtInternal); ++ _redact_params.params_string = buf; ++ strncpy(_redact_params.params_string, redact_params_string, length + 1); ++ size_t start = strlen("-HeapDumpRedact="); ++ _redact_params.heap_dump_redact = _redact_params.params_string + start; ++ char* map_pos = strstr(_redact_params.heap_dump_redact, ",RedactMap="); ++ char* file_pos = strstr(_redact_params.heap_dump_redact, ",RedactMapFile="); ++ char* class_path_pos = strstr(_redact_params.heap_dump_redact, ",RedactClassPath="); ++ char* redact_password_pos = strstr(_redact_params.heap_dump_redact, ",RedactPassword="); ++ ++ _redact_params.redact_map = parse_redact_child_param(map_pos, ",RedactMap=", ++ file_pos); ++ _redact_params.redact_map_file = parse_redact_child_param(file_pos, ",RedactMapFile=", ++ class_path_pos); ++ _redact_params.annotation_class_path = parse_redact_child_param(class_path_pos, ",RedactClassPath=", ++ redact_password_pos); ++ _redact_params.redact_password = parse_redact_child_param(redact_password_pos, ",RedactPassword=", ++ _redact_params.params_string + length); ++} ++ ++char* HeapRedactor::parse_redact_child_param(char *redact_params_sub_string, const char* redact_param_prefix, ++ const char* next_redact_param_prefix) { ++ char* pos = NULL; ++ if (redact_params_sub_string == NULL) { ++ pos = NULL; ++ } else { ++ *redact_params_sub_string = '\0'; ++ pos = redact_params_sub_string + strlen(redact_param_prefix); ++ if (pos == next_redact_param_prefix) { ++ pos = NULL; ++ } ++ } ++ return pos; ++} ++ ++bool HeapRedactor::check_launcher_heapdump_redact_support(const char *value) { ++ if (!strcmp(value, "=basic") || !strcmp(value, "=names") || !strcmp(value, "=off") ++ || !strcmp(value, "=diyrules") || !strcmp(value, "=annotation") || !strcmp(value, "=full")) { ++ return true; ++ } ++ return false; ++} ++ ++void HeapRedactor::init(outputStream* out) { ++ /** -XX:+VerifyRedactPassword, ++ * if HeapDumpRedact is NULL , jmap operation can not open redact feature without password ++ * if HeapDumpRedact is not NULL, jmap operation can not change redact level without password ++ **/ ++ char* split_char = NULL; ++ if(RedactPassword == NULL || (split_char = strstr(const_cast(RedactPassword), ",")) == NULL || strlen(split_char) < SALT_LEN) { ++ VerifyRedactPassword = false; ++ } ++ if(VerifyRedactPassword && !_use_sys_params) { ++ size_t auth_len = strlen(RedactPassword); ++ size_t suffix_len = strlen(split_char); ++ if(_redact_params.redact_password == NULL || ++ strncmp(_redact_params.redact_password, RedactPassword, auth_len-suffix_len) ) { ++ // no password or wrong password ++ _use_sys_params = true; ++ if(out != NULL) { ++ out->print_cr("not correct password, use the default redact mode when stared"); ++ } ++ } ++ } ++ ++ if(_redact_params.redact_password != NULL) { ++ size_t password_Len = strlen(_redact_params.redact_password); ++ memset(_redact_params.redact_password, '\0', password_Len); ++ } ++ ++ if (_redact_level == REDACT_UNKNOWN) { ++ init_heapdump_redact_level(); ++ } ++ return; ++} ++ ++void HeapRedactor::init_redact_map() { ++ const char* map_param = NULL; ++ const char* map_file_param = NULL; ++ if (_use_sys_params) { ++ map_param = RedactMap; ++ map_file_param = RedactMapFile; ++ } else { ++ map_param = _redact_params.redact_map; ++ map_file_param = _redact_params.redact_map_file; ++ } ++ if (map_file_param != NULL) { ++ read_redact_map_from_file(map_file_param); ++ } ++ if (map_param != NULL) { ++ size_t length = strlen(map_param); ++ _name_map_list = NEW_C_HEAP_ARRAY(char, length + 1, mtInternal); ++ strncpy(_name_map_list, map_param, length + 1); ++ read_redact_map_dependon_mode(_name_map_list, _redact_level); ++ } ++} ++ ++void HeapRedactor::read_redact_map_dependon_mode(char* name_map_list, HeapDumpRedactLevel redact_level) { ++ if(redact_level == REDACT_DIYRULES) { ++ parse_redact_diy_rules(name_map_list); ++ } else { ++ parse_redact_map_string(name_map_list); ++ } ++} ++ ++void HeapRedactor::parse_redact_map_string(char *name_map_list) { ++#ifdef LINUX ++ size_t token_start = 0; ++ size_t step = 0; ++ size_t length = strlen(name_map_list); ++ ++ while (step < length) { ++ bool is_seperator = false; ++ if ((is_seperator = (name_map_list[step] == ',' || name_map_list[step] == ';' || name_map_list[step] == '\n' || ++ name_map_list[step] == ' ')) || ++ step == length - 1) { ++ if (is_seperator) { ++ name_map_list[step] = '\0'; ++ } else { ++ step++; ++ } ++ ++ if (token_start >= step) { ++ // to reduce the depth of the method ++ token_start = step + 1; ++ continue; ++ } ++ ++ char *token = name_map_list + token_start; ++ size_t i = 0; ++ size_t token_length = strlen(token); ++ while (i < token_length && token[i] != ':') { ++ i++; ++ } ++ if (i < token_length - 1) { ++ token[i] = '\0'; ++ _redact_name_table = os::Linux::heap_dict_add(token, token + i + 1, _redact_name_table, 0); ++ } ++ token_start = step + 1; ++ } ++ step++; ++ } ++#endif ++} ++ ++void HeapRedactor::read_redact_map_from_file(const char *path) { ++ char base_path[JVM_MAXPATHLEN] = {'\0'}; ++ char buffer[MAX_MAP_FILE_LENGTH + 1] = {'\0'}; ++ if (path == NULL || path[0] == '\0') { ++ // RedactMapFile= not specified ++ } else { ++ if (strlen(path) >= JVM_MAXPATHLEN) { ++ warning("RedactMap File path is too long "); ++ return; ++ } ++ strncpy(base_path, path, sizeof(base_path)); ++ // check if the path is a directory (must exist) ++ FILE* fm = NULL; ++ fm = fopen(base_path, "r"); ++ if (fm == NULL) { ++ return; ++ } ++ size_t num_read = fread((char *)buffer, 1, MAX_MAP_FILE_LENGTH, fm); ++ if((int)num_read != -1){ ++ _file_name_map_list = NEW_C_HEAP_ARRAY(char, (size_t)num_read + 1, mtInternal); ++ strncpy(_file_name_map_list, buffer, num_read + 1); ++ read_redact_map_dependon_mode(_file_name_map_list, _redact_level); ++ } ++ ++ fclose(fm); ++ } ++} ++ ++void HeapRedactor::parse_redact_diy_rules(char* name_map_list) { ++ size_t token_start = 0; ++ size_t step = 0; ++ size_t length = strlen(name_map_list); ++ ++ while (step < length) { ++ bool is_seperator = false; ++ if ((is_seperator = (name_map_list[step] == ',' || name_map_list[step] == ';' || name_map_list[step] == '\n' || ++ name_map_list[step] == ' ')) || ++ step == length - 1) { ++ if (is_seperator) { ++ name_map_list[step] = '\0'; ++ } else { ++ step++; ++ } ++ ++ if (token_start >= step) { ++ // to reduce the depth of the method ++ token_start = step + 1; ++ continue; ++ } ++ ++ char *token = name_map_list + token_start; ++ parse_token(token); ++ token_start = step + 1; ++ } ++ step++; ++ } ++ ++ // clear _redact_class_full_name, encase RedactMap has an unformatted value(without class name), ++ // will rewrite the last class's value_map ++ _redact_class_full_name = NULL; ++} ++ ++void HeapRedactor::parse_token(char* token) { ++#ifdef LINUX ++ size_t i = 0; ++ size_t token_length = strlen(token); ++ while (i < token_length && token[i] != ':') { ++ if(token[i] == '.' ) { ++ token[i] = '/'; ++ } ++ i++; ++ } ++ ++ void* _redact_rules_sub_table = _redact_class_full_name == NULL ? NULL : ++ os::Linux::heap_dict_lookup(_redact_class_full_name, _redact_rules_table, false); ++ if (i < token_length - 1 && _redact_rules_sub_table != NULL) { ++ token[i] = '\0'; ++ os::Linux::heap_dict_add(token, token + i + 1, _redact_rules_sub_table, 0); ++ } else if( i == token_length) { ++ _redact_class_full_name = token; ++ _redact_rules_sub_table = os::Linux::heap_dict_lookup(token, _redact_rules_table, false); ++ if (_redact_rules_sub_table == NULL) { ++ _redact_rules_sub_table = os::Linux::heap_dict_add(token, NULL, _redact_rules_sub_table, 0); ++ _redact_rules_table = os::Linux::heap_dict_add(token, _redact_rules_sub_table, _redact_rules_table, 0); ++ } ++ } ++#endif ++} ++ ++HeapDumpRedactLevel HeapRedactor::init_heapdump_redact_level() { ++ const char* redact_string = NULL; ++ if (_use_sys_params) { ++ redact_string = HeapDumpRedact; ++ } else { ++ redact_string = _redact_params.heap_dump_redact; ++ } ++ if (redact_string == NULL) { ++ _redact_level = REDACT_OFF; ++ } else { ++#ifdef LINUX ++ if (strcmp(redact_string, "basic") == 0) { ++ _redact_level = REDACT_BASIC; ++ } else if (strcmp(redact_string, "names") == 0) { ++ _redact_level = REDACT_NAMES; ++ init_redact_map(); ++ } else if (strcmp(redact_string, "full") == 0) { ++ _redact_level = REDACT_FULL; ++ init_redact_map(); ++ } else if (strcmp(redact_string, "diyrules") == 0) { ++ _redact_level = REDACT_DIYRULES; ++ init_redact_map(); ++ } else if (strcmp(redact_string, "annotation") == 0) { ++ _redact_level = REDACT_ANNOTATION; ++ init_class_path(); ++ if(_annotation_class_path == NULL) { ++ _redact_level = REDACT_OFF; ++ } ++ } else { ++ _redact_level = REDACT_OFF; ++ } ++#else ++ if (strcmp(redact_string, "basic") == 0) { ++ _redact_level = REDACT_BASIC; ++ } else if (strcmp(redact_string, "full") == 0) { ++ _redact_level = REDACT_BASIC; ++ } else { ++ _redact_level = REDACT_OFF; ++ } ++#endif ++ ++ } ++ ++ if (_redact_params.params_string != NULL) { ++ FREE_C_HEAP_ARRAY(char, _redact_params.params_string); ++ } ++ return _redact_level; ++} ++ ++void HeapRedactor::init_class_path() { ++ const char* class_path = NULL; ++ if (_use_sys_params) { ++ class_path = RedactClassPath; ++ } else { ++ class_path = _redact_params.annotation_class_path; ++ } ++ ++ if(class_path != NULL) { ++ size_t class_path_len = strlen(class_path); ++ _annotation_class_path = NEW_C_HEAP_ARRAY(char, class_path_len + 3, mtInternal); ++ _annotation_class_path[0] = 'L'; ++ strncpy(_annotation_class_path + 1, class_path, class_path_len + 1); ++ _annotation_class_path[class_path_len + 1] = ';'; ++ _annotation_class_path[class_path_len + 2] = '\0'; ++ } ++} ++ ++void HeapRedactor::insert_anonymous_value(void* key, void* value, uint index){ ++#ifdef LINUX ++ _annotation_value_tables[index] = os::Linux::heap_dict_add(key, value, _annotation_value_tables[index], 1); ++#endif ++} ++ ++bool HeapRedactor::recursion_cp_refs_in_annotation_struct( ++ AnnotationArray* annotations_typeArray, int &byte_i_ref) { ++ if ((byte_i_ref + 2 + 2) > annotations_typeArray->length()) { ++ // not enough room for smallest annotation_struct ++ if (log_is_enabled(Debug, cds, heap)) { ++ log_debug(cds, heap)("length() is too small for annotation_struct"); ++ } ++ return false; ++ } ++ ++ u2 type_index = Bytes::get_Java_u2((address)annotations_typeArray->adr_at(byte_i_ref)); ++ byte_i_ref += 2; ++ ++ u2 num_element_value_pairs = Bytes::get_Java_u2((address) annotations_typeArray->adr_at(byte_i_ref)); ++ byte_i_ref += 2; ++ ++ if (log_is_enabled(Debug, cds, heap)) { ++ log_debug(cds, heap)("type_index=%d num_element_value_pairs=%d", type_index, num_element_value_pairs); ++ } ++ ++ int calc_num_element_value_pairs = 0; ++ for (; calc_num_element_value_pairs < num_element_value_pairs; ++ calc_num_element_value_pairs++) { ++ if ((byte_i_ref + 2) > annotations_typeArray->length()) { ++ // not enough room for another element_name_index, let alone ++ // the rest of another component ++ log_debug(cds, heap)("length() is too small for element_name_index"); ++ return false; ++ } ++ ++ u2 element_name_index = Bytes::get_Java_u2((address) annotations_typeArray->adr_at(byte_i_ref)); ++ byte_i_ref += 2; ++ ++ log_debug(cds, heap)("element_name_index=%d", element_name_index); ++ ++ if (!recursion_cp_refs_in_element_value(annotations_typeArray, byte_i_ref)) { ++ log_debug(cds, heap)("bad element_value at %d", calc_num_element_value_pairs); ++ // propagate failure back to caller ++ return false; ++ } ++ } // end for each component ++ assert(num_element_value_pairs == calc_num_element_value_pairs, "sanity check"); ++ ++ return true; ++} // end recursion_cp_refs_in_annotation_struct() ++ ++bool HeapRedactor::lookup_annotation_index_in_constant_pool(AnnotationArray* field_annotations, ConstantPool *cp, int &byte_i_ref) { ++ u2 num_annotations = 0; ++ bool has_anonymous_annotation = false; ++ ++ if ((byte_i_ref + 2) > field_annotations->length()) { ++ // not enough room for num_annotations field ++ log_debug(cds, heap)("length() is too small for num_annotations field"); ++ return false; ++ } else { ++ num_annotations = Bytes::get_Java_u2((address) field_annotations->adr_at(byte_i_ref)); ++ } ++ ++ byte_i_ref += 2; ++ ++ for (int calc_num_annotations = 0; calc_num_annotations < num_annotations; calc_num_annotations++) { ++ ++ if ((byte_i_ref + 2 + 2) > field_annotations->length()) { ++ // not enough room for smallest annotation_struct ++ log_debug(cds, heap)("length() is too small for annotation_struct"); ++ return false; ++ } ++ ++ // get constants pool index ++ address cp_index_addr = (address) field_annotations->adr_at(byte_i_ref); ++ byte_i_ref += 2; ++ u2 cp_index = Bytes::get_Java_u2(cp_index_addr); ++ Symbol *annotate_class_symbol = cp->symbol_at(cp_index); ++ if (!Symbol::is_valid(annotate_class_symbol)) { ++ return false; ++ } ++ char *annotate_class_name = annotate_class_symbol->as_C_string(); ++ ++ u2 num_element_value_pairs = Bytes::get_Java_u2((address) field_annotations->adr_at(byte_i_ref)); ++ byte_i_ref += 2; ++ if ((byte_i_ref + 2 + 1) > field_annotations->length()) { ++ // not enough room for smallest annotation_struct ++ log_debug(cds, heap)("length() is too small for element_name_index"); ++ return false; ++ } ++ ++ const char *annotation_class_path = get_annotation_class_path(); ++ has_anonymous_annotation = (strcmp(annotation_class_path, annotate_class_name) == 0); ++ if (has_anonymous_annotation) { ++ address element_name_addr = (address) field_annotations->adr_at(byte_i_ref); ++ byte_i_ref += 2; ++ u2 cp_name_index = Bytes::get_Java_u2(element_name_addr); ++ Symbol *element_name_symbol = cp->symbol_at(cp_name_index); ++ char *element_name = element_name_symbol->as_C_string(); ++ if(element_name == NULL || strcmp(element_name, "value")) { ++ // expected annotation has only one field "value" ++ return false; ++ } ++ // skip element tag ++ byte_i_ref++; ++ return true; ++ } ++ ++ int calc_num_element_value_pairs = 0; ++ // skip element_name_index ++ byte_i_ref += 2; ++ for (; calc_num_element_value_pairs < num_element_value_pairs; calc_num_element_value_pairs++) { ++ if (!recursion_cp_refs_in_element_value(field_annotations, byte_i_ref)) { ++ return false; ++ } ++ } ++ } ++ return false; ++} ++ ++bool HeapRedactor::recursion_cp_refs_in_element_value(AnnotationArray* field_annotations, int &byte_i_ref) { ++ if ((byte_i_ref + 1) > field_annotations->length()) { ++ // not enough room for a tag let alone the rest of an element_value ++ if (log_is_enabled(Debug, cds, heap)) { ++ log_debug(cds, heap)("length() is too small for a tag"); ++ } ++ return false; ++ } ++ ++ u1 tag = field_annotations->at(byte_i_ref); ++ byte_i_ref++; ++ switch (tag) { ++ case JVM_SIGNATURE_BYTE: ++ case JVM_SIGNATURE_CHAR: ++ case JVM_SIGNATURE_DOUBLE: ++ case JVM_SIGNATURE_FLOAT: ++ case JVM_SIGNATURE_INT: ++ case JVM_SIGNATURE_LONG: ++ case JVM_SIGNATURE_SHORT: ++ case JVM_SIGNATURE_BOOLEAN: ++ case 's': ++ case 'c': ++ { ++ if ((byte_i_ref + 2) > field_annotations->length()) { ++ if (log_is_enabled(Debug, cds, heap)) { ++ log_debug(cds, heap)("length() is too small for a const_value_index"); ++ } ++ break; ++ } ++ byte_i_ref += 2; ++ } break; ++ case 'e': ++ { ++ if ((byte_i_ref + 4) > field_annotations->length()) { ++ if (log_is_enabled(Debug, cds, heap)) { ++ log_debug(cds, heap)("length() is too small for a enum_const_value"); ++ } ++ break; ++ } ++ byte_i_ref += 4; ++ } break; ++ ++ case '@': ++ // For the above tag value, value.attr_value is the right union ++ // field. This is a nested annotation. ++ if (!recursion_cp_refs_in_annotation_struct(field_annotations, byte_i_ref)) { ++ // propagate failure back to caller ++ return false; ++ } ++ break; ++ ++ case JVM_SIGNATURE_ARRAY: ++ { ++ if ((byte_i_ref + 2) > field_annotations->length()) { ++ // not enough room for a num_values field ++ if (log_is_enabled(Debug, cds, heap)) { ++ log_debug(cds, heap)("length() is too small for a num_values field"); ++ } ++ return false; ++ } ++ ++ // For the above tag value, value.array_value is the right union ++ // field. This is an array of nested element_value. ++ u2 num_values = Bytes::get_Java_u2((address) field_annotations->adr_at(byte_i_ref)); ++ byte_i_ref += 2; ++ if (log_is_enabled(Debug, cds, heap)) { ++ log_debug(cds, heap)("num_values=%d", num_values); ++ } ++ ++ int calc_num_values = 0; ++ for (; calc_num_values < num_values; calc_num_values++) { ++ if (!recursion_cp_refs_in_element_value(field_annotations, byte_i_ref)) { ++ if (log_is_enabled(Debug, cds, heap)) { ++ log_debug(cds, heap)("bad nested element_value at %d", calc_num_values); ++ } ++ // propagate failure back to caller ++ return false; ++ } ++ } ++ } break; ++ ++ default: ++ if (log_is_enabled(Debug, cds, heap)) { ++ log_debug(cds, heap)("bad tag=0x%x", tag); ++ } ++ return false; ++ } ++ ++ return true; ++} ++ ++bool HeapRedactor::record_typeArrayOop(typeArrayOop array, uint index) { ++ bool _inserted = false; ++#ifdef LINUX ++ _redact_records[index] = os::Linux::heap_vector_add(array, _redact_records[index], _inserted); ++#endif ++ return _inserted; ++} ++ ++void HeapRedactor::insert_class_field_value(void* class_key, void* field_key, void* value) { ++#ifdef LINUX ++ void* _redact_sub_table = os::Linux::heap_dict_lookup(class_key, _redact_class_field_table, false); ++ _redact_sub_table = os::Linux::heap_dict_add(field_key, value, _redact_sub_table, 1); ++ _redact_class_field_table = os::Linux::heap_dict_add(class_key, _redact_sub_table, _redact_class_field_table, 1); ++#endif ++} ++ ++void HeapRedactor::init_containers(uint nums) { ++ _container_nums = (nums == 0 ? 1 : nums); ++ _annotation_value_tables = NEW_C_HEAP_ARRAY(void*, _container_nums, mtInternal); ++ memset(_annotation_value_tables, 0, sizeof(void*)*_container_nums); ++ _redact_records = NEW_C_HEAP_ARRAY(void*, _container_nums, mtInternal); ++ memset(_redact_records, 0, sizeof(void*)*_container_nums); ++} +\ No newline at end of file +diff --git a/src/hotspot/share/services/heapRedactor.hpp b/src/hotspot/share/services/heapRedactor.hpp +new file mode 100644 +index 000000000..467baa39d +--- /dev/null ++++ b/src/hotspot/share/services/heapRedactor.hpp +@@ -0,0 +1,225 @@ ++/* ++ * Copyright (c) 2020, 2023, 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. Huawei designates this ++ * particular file as subject to the "Classpath" exception as provided ++ * by Huawei 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 visit https://gitee.com/openeuler/bishengjdk-8 if you need additional ++ * information or have any questions. ++ */ ++ ++#ifndef SHARE_SERVICES_HEAPREDACTOR_HPP ++#define SHARE_SERVICES_HEAPREDACTOR_HPP ++#include "memory/allocation.hpp" ++#include "oops/annotations.hpp" ++#include "oops/constantPool.hpp" ++#ifdef LINUX ++#include "os_linux.hpp" ++#endif ++ ++#define MAX_MAP_FILE_LENGTH 1024 ++#define SALT_LEN 9 ++ ++enum HeapDumpRedactLevel { ++ REDACT_UNKNOWN, ++ REDACT_OFF, ++ REDACT_NAMES, ++ REDACT_BASIC, ++ REDACT_DIYRULES, ++ REDACT_ANNOTATION, ++ REDACT_FULL ++}; ++ ++struct RedactParams { ++ char* params_string; ++ char* heap_dump_redact; ++ char* redact_map; ++ char* redact_map_file; ++ char* annotation_class_path; ++ char* redact_password; ++}; ++ ++class HeapRedactor : public StackObj { ++public: ++ static const char* REDACT_UNKNOWN_STR; ++ static const char* REDACT_OFF_STR; ++ static const char* REDACT_NAMES_STR; ++ static const char* REDACT_BASIC_STR; ++ static const char* REDACT_DIYRULES_STR; ++ static const char* REDACT_ANNOTATION_STR; ++ static const char* REDACT_FULL_STR; ++ HeapRedactor(outputStream* out); ++ HeapRedactor(const char* redact_params, outputStream* out); ++ ~HeapRedactor(); ++ static bool check_launcher_heapdump_redact_support(const char* value); ++ HeapDumpRedactLevel redact_level() { ++ if(_redact_level == REDACT_UNKNOWN) { ++ _redact_level = init_heapdump_redact_level(); ++ } ++ ++ return _redact_level; ++ } ++ ++ const char* get_redact_level_string() const { ++#ifdef LINUX ++ switch (_redact_level) { ++ case REDACT_OFF: ++ return REDACT_OFF_STR; ++ case REDACT_NAMES: ++ return REDACT_NAMES_STR; ++ case REDACT_BASIC: ++ return REDACT_BASIC_STR; ++ case REDACT_DIYRULES: ++ return REDACT_DIYRULES_STR; ++ case REDACT_ANNOTATION: ++ return REDACT_ANNOTATION_STR; ++ case REDACT_FULL: ++ return REDACT_FULL_STR; ++ case REDACT_UNKNOWN: ++ default: ++ return REDACT_UNKNOWN_STR; ++ } ++#else ++ switch (_redact_level) { ++ case REDACT_OFF: ++ return REDACT_OFF_STR; ++ case REDACT_BASIC: ++ return REDACT_BASIC_STR; ++ case REDACT_UNKNOWN: ++ default: ++ return REDACT_UNKNOWN_STR; ++ } ++#endif ++ ++ } ++ ++ char* lookup_redact_name(const void* name) { ++ void* val = NULL; ++#ifdef LINUX ++ val = os::Linux::heap_dict_lookup(const_cast(name), _redact_name_table, false); ++#endif ++ if(val != NULL) { ++ return (char*)val; ++ } ++ return NULL; ++ } ++ ++ void* lookup_class_rules(const void* name) { ++ void* val = NULL; ++#ifdef LINUX ++ val = os::Linux::heap_dict_lookup(const_cast(name), _redact_rules_table, false); ++#endif ++ return val; ++ } ++ ++ const char* get_annotation_class_path(){ ++ return _annotation_class_path; ++ } ++ ++ void insert_anonymous_value(void* key, void* value, uint index); ++ ++ template ++ T lookup_replace_value(void* key) { ++ void* val = NULL; ++#ifdef LINUX ++ for(uint i = 0; i < _container_nums; i++) { ++ if(_annotation_value_tables[i] == NULL) { ++ continue; ++ } ++ val = os::Linux::heap_dict_lookup(key, _annotation_value_tables[i], true); ++ if(val != NULL) { ++ break; ++ } ++ } ++#endif ++ if(val != NULL) { ++ return (T)val; ++ } ++ return NULL; ++ } ++ ++ bool lookup_annotation_index_in_constant_pool(AnnotationArray* field_annotations, ConstantPool *cp, int &byte_i_ref); ++ bool recursion_cp_refs_in_element_value(AnnotationArray* field_annotations, int &byte_i_ref); ++ bool recursion_cp_refs_in_annotation_struct(AnnotationArray* field_annotations, int &byte_i_ref); ++ ++ bool record_typeArrayOop(typeArrayOop array, uint index); ++ void* get_vector_node_next(void* node, int &_cnt, void** &_items, uint index) { ++ void* val = NULL; ++#ifdef LINUX ++ val = os::Linux::heap_vector_get_next(_redact_records[index], node, _cnt, _items); ++#endif ++ return val; ++ } ++ ++ void insert_class_field_value(void* class_key, void* field_key, void* value); ++ void* lookup_class_value(void* key) { ++ void* val = NULL; ++#ifdef LINUX ++ if(_redact_class_field_table == NULL) { ++ return val; ++ } ++ val = os::Linux::heap_dict_lookup(key, _redact_class_field_table, false); ++#endif ++ return val; ++ } ++ ++ void* lookup_value(void* key, void* heap_dict, bool deletable) { ++ void* val = NULL; ++#ifdef LINUX ++ val = os::Linux::heap_dict_lookup(key, heap_dict, deletable); ++#endif ++ return val; ++ } ++ ++ void init_containers(uint nums); ++ const uint get_container_nums() { ++ return _container_nums; ++ } ++ ++private: ++ HeapDumpRedactLevel _redact_level; ++ RedactParams _redact_params; ++ bool _use_sys_params; ++ void* _redact_name_table; ++ void* _redact_rules_table; ++ void** _annotation_value_tables; ++ // void* _annotation_value_table; ++ void* _redact_class_field_table; ++ char* _file_name_map_list; ++ char* _name_map_list; ++ char* _annotation_class_path; ++ char* _redact_class_full_name; ++ void** _redact_records; ++ //void* _redact_record; ++ // safety: every dump thread has one map and one vector ++ uint _container_nums; ++ ++ HeapDumpRedactLevel init_heapdump_redact_level(); ++ void read_redact_map_from_file(const char* path); ++ void read_redact_map_dependon_mode(char* name_map_list, HeapDumpRedactLevel redact_level); ++ void parse_redact_map_string(char* name_map_list); ++ void parse_redact_diy_rules(char* name_map_list); ++ void parse_token(char* token); ++ void parse_redact_params(const char *redact_params_string); ++ char* parse_redact_child_param(char *redact_params_sub_string, const char* redact_param_prefix, const char* next_redact_param_prefix); ++ void init(outputStream* out); ++ void init_fields(); ++ void init_redact_map(); ++ void init_class_path(); ++ ++}; ++#endif // SHARE_SERVICES_HEAPREDACTOR_HPP +\ No newline at end of file +diff --git a/src/hotspot/share/services/writeableFlags.cpp b/src/hotspot/share/services/writeableFlags.cpp +index d4f601ed8..6e8924c86 100644 +--- a/src/hotspot/share/services/writeableFlags.cpp ++++ b/src/hotspot/share/services/writeableFlags.cpp +@@ -235,6 +235,13 @@ JVMFlag::Error WriteableFlags::set_flag(const char* name, const void* value, JVM + + JVMFlag* f = JVMFlag::find_flag(name); + if (f) { ++ if(VerifyRedactPassword) { ++ if(strcmp(name, "HeapDumpRedact") == 0 || strcmp(name, "RedactMap") == 0 || strcmp(name, "RedactMapFile") == 0 ++ || strcmp(name, "RedactClassPath") == 0) { ++ err_msg.print("has no authority to reset redact params"); ++ return JVMFlag::NON_WRITABLE; ++ } ++ } + // only writeable flags are allowed to be set + if (f->is_writeable()) { + return setter(f, value, origin, err_msg); +diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HSDB.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HSDB.java +index 9cc51934d..80d64ea97 100644 +--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HSDB.java ++++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HSDB.java +@@ -41,15 +41,16 @@ import sun.jvm.hotspot.gc.g1.*; + import sun.jvm.hotspot.gc.x.*; + import sun.jvm.hotspot.gc.z.*; + import sun.jvm.hotspot.interpreter.*; +-import sun.jvm.hotspot.oops.*; + import sun.jvm.hotspot.runtime.*; + import sun.jvm.hotspot.ui.*; ++import sun.jvm.hotspot.ui.Annotation; + import sun.jvm.hotspot.ui.tree.*; + import sun.jvm.hotspot.ui.classbrowser.*; + import sun.jvm.hotspot.utilities.*; + import sun.jvm.hotspot.utilities.Observable; + import sun.jvm.hotspot.utilities.Observer; +- ++import sun.jvm.hotspot.oops.*; ++ + /** The top-level HotSpot Debugger. FIXME: make this an embeddable + component! (Among other things, figure out what to do with the + menu bar...) */ +@@ -1003,7 +1004,7 @@ public class HSDB implements ObjectHistogramPanel.Listener, SAListener { + } + + if (curFrame.getFP() != null) { +- annoPanel.addAnnotation(new Annotation(curFrame.getSP(), ++ annoPanel.addAnnotation(new sun.jvm.hotspot.ui.Annotation(curFrame.getSP(), + curFrame.getFP(), + anno)); + } else { +@@ -1013,14 +1014,14 @@ public class HSDB implements ObjectHistogramPanel.Listener, SAListener { + if (Assert.ASSERTS_ENABLED) { + Assert.that(cb.getFrameSize() > 0, "CodeBlob must have non-zero frame size"); + } +- annoPanel.addAnnotation(new Annotation(sp, ++ annoPanel.addAnnotation(new sun.jvm.hotspot.ui.Annotation(sp, + sp.addOffsetTo(cb.getFrameSize()), + anno)); + } + + // Add interpreter frame annotations + if (curFrame.isInterpretedFrame()) { +- annoPanel.addAnnotation(new Annotation(curFrame.addressOfInterpreterFrameExpressionStack(), ++ annoPanel.addAnnotation(new sun.jvm.hotspot.ui.Annotation(curFrame.addressOfInterpreterFrameExpressionStack(), + curFrame.addressOfInterpreterFrameTOS(), + "Interpreter expression stack")); + Address monBegin = curFrame.interpreterFrameMonitorBegin().address(); +@@ -1032,7 +1033,7 @@ public class HSDB implements ObjectHistogramPanel.Listener, SAListener { + if (interpreterFrameMethod != null) { + // The offset is just to get the right stack slots highlighted in the output + int offset = 1; +- annoPanel.addAnnotation(new Annotation(curFrame.addressOfInterpreterFrameLocal(offset), ++ annoPanel.addAnnotation(new sun.jvm.hotspot.ui.Annotation(curFrame.addressOfInterpreterFrameLocal(offset), + curFrame.addressOfInterpreterFrameLocal((int) interpreterFrameMethod.getMaxLocals() + offset), + "Interpreter locals area for frame with SP = " + curFrame.getSP())); + } +@@ -1041,9 +1042,9 @@ public class HSDB implements ObjectHistogramPanel.Listener, SAListener { + methodAnno += " (BAD OOP)"; + } + Address a = curFrame.addressOfInterpreterFrameMethod(); +- annoPanel.addAnnotation(new Annotation(a, a.addOffsetTo(addressSize), methodAnno)); ++ annoPanel.addAnnotation(new sun.jvm.hotspot.ui.Annotation(a, a.addOffsetTo(addressSize), methodAnno)); + a = curFrame.addressOfInterpreterFrameCPCache(); +- annoPanel.addAnnotation(new Annotation(a, a.addOffsetTo(addressSize), "Interpreter constant pool cache")); ++ annoPanel.addAnnotation(new sun.jvm.hotspot.ui.Annotation(a, a.addOffsetTo(addressSize), "Interpreter constant pool cache")); + } + + RegisterMap rm = (RegisterMap) vf.getRegisterMap().clone(); +@@ -1160,7 +1161,7 @@ public class HSDB implements ObjectHistogramPanel.Listener, SAListener { + } + } + +- annoPanel.addAnnotation(new Annotation(addr, addr.addOffsetTo(addressSize), anno)); ++ annoPanel.addAnnotation(new sun.jvm.hotspot.ui.Annotation(addr, addr.addOffsetTo(addressSize), anno)); + } + }, rm); + } catch (Exception e) { +diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/SALauncher.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/SALauncher.java +index b884058a3..1adabf833 100644 +--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/SALauncher.java ++++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/SALauncher.java +@@ -130,6 +130,10 @@ public class SALauncher { + System.out.println(" --histo To print histogram of java object heap."); + System.out.println(" --clstats To print class loader statistics."); + System.out.println(" --finalizerinfo To print information on objects awaiting finalization."); ++ System.out.println(" --HeapDumpRedact redact the heapdump information to remove sensitive data."); ++ System.out.println(" --RedactMap Redact the class and field names to other strings."); ++ System.out.println(" --RedactMapFile file path of the redact map."); ++ System.err.println(" --RedactClassPath full path of the redact annotation"); + return commonHelpWithConnect("jmap"); + } + +@@ -302,8 +306,8 @@ public class SALauncher { + jstack.runWithArgs(buildAttachArgs(newArgMap, false)); + } + +- private static void runJMAP(String[] oldArgs) { +- Map longOptsMap = Map.ofEntries( ++ private static Map getLongOptsMap() { ++ return Map.ofEntries( + Map.entry("exe=", "exe"), + Map.entry("core=", "core"), + Map.entry("pid=", "pid"), +@@ -314,13 +318,27 @@ public class SALauncher { + Map.entry("gz=", "gz"), + Map.entry("histo", "-histo"), + Map.entry("clstats", "-clstats"), +- Map.entry("finalizerinfo", "-finalizerinfo")); ++ Map.entry("finalizerinfo", "-finalizerinfo"), ++ Map.entry("HeapDumpRedact=", "HeapDumpRedact"), ++ Map.entry("RedactMap=", "RedactMap"), ++ Map.entry("RedactMapFile=", "RedactMapFile"), ++ Map.entry("RedactClassPath=", "RedactClassPath"), ++ Map.entry("RedactPassword", "RedactPassword")); ++ } ++ ++ private static void runJMAP(String[] oldArgs) { ++ Map longOptsMap = getLongOptsMap(); + Map newArgMap = parseOptions(oldArgs, longOptsMap); + + boolean requestHeapdump = newArgMap.containsKey("binaryheap"); + String dumpfile = newArgMap.get("dumpfile"); + String gzLevel = newArgMap.get("gz"); + String command = "-heap:format=b"; ++ String heapDumpRedact = newArgMap.get("HeapDumpRedact"); ++ String redactMap = newArgMap.get("RedactMap"); ++ String redactMapFile = newArgMap.get("RedactMapFile"); ++ String redactClassPath = newArgMap.get("RedactClassPath"); ++ boolean hasRedactPassword = newArgMap.containsKey("RedactPassword"); + if (!requestHeapdump && (dumpfile != null)) { + throw new IllegalArgumentException("Unexpected argument: dumpfile"); + } +@@ -331,15 +349,37 @@ public class SALauncher { + if (dumpfile != null) { + command += ",file=" + dumpfile; + } ++ if (heapDumpRedact != null) { ++ command += ",HeapDumpRedact=" + heapDumpRedact; ++ } ++ if (redactMap != null) { ++ command += ",RedactMap=" + redactMap; ++ } ++ if (redactMapFile != null) { ++ command += ",RedactMapFile=" + redactMapFile; ++ } ++ if (redactClassPath != null) { ++ command += ",RedactClassPath=" + redactClassPath; ++ } ++ if(hasRedactPassword) { ++ command += ",RedactPassword"; ++ } + newArgMap.put(command, null); + } + + newArgMap.remove("binaryheap"); + newArgMap.remove("dumpfile"); + newArgMap.remove("gz"); ++ newArgMap.remove("HeapDumpRedact"); ++ newArgMap.remove("RedactMap"); ++ newArgMap.remove("RedactMapFile"); ++ newArgMap.remove("RedactClassPath"); ++ newArgMap.remove("RedactPassword"); + JMap.main(buildAttachArgs(newArgMap, false)); + } + ++ ++ + private static void runJINFO(String[] oldArgs) { + Map longOptsMap = Map.of("exe=", "exe", + "core=", "core", +diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Annotation.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Annotation.java +new file mode 100644 +index 000000000..617e8952b +--- /dev/null ++++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Annotation.java +@@ -0,0 +1,70 @@ ++/* ++ * Copyright (c) 2000, 2020, 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 sun.jvm.hotspot.oops; ++ ++import sun.jvm.hotspot.debugger.Address; ++import sun.jvm.hotspot.runtime.VM; ++import sun.jvm.hotspot.runtime.VMObject; ++import sun.jvm.hotspot.types.AddressField; ++import sun.jvm.hotspot.types.Type; ++import sun.jvm.hotspot.types.TypeDataBase; ++import sun.jvm.hotspot.types.WrongTypeException; ++import sun.jvm.hotspot.utilities.AnnotationArray2D; ++import sun.jvm.hotspot.utilities.Observable; ++import sun.jvm.hotspot.utilities.Observer; ++ ++// An Annotation is an oop containing class annotations ++ ++public class Annotation extends VMObject { ++ private static AddressField class_annotations; ++ private static AddressField class_type_annotations; ++ private static AddressField fields_annotations; ++ private static AddressField fields_type_annotations; ++ ++ static { ++ VM.registerVMInitializedObserver(new Observer() { ++ public void update(Observable o, Object data) { ++ initialize(VM.getVM().getTypeDataBase()); ++ } ++ }); ++ } ++ ++ private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { ++ Type type = db.lookupType("Annotations"); ++ class_annotations = type.getAddressField("_class_annotations"); ++ class_type_annotations = type.getAddressField("_class_type_annotations"); ++ fields_annotations = type.getAddressField("_fields_annotations"); ++ fields_type_annotations = type.getAddressField("_fields_type_annotations"); ++ } ++ ++ public Annotation(Address addr) { ++ super(addr); ++ } ++ ++ public AnnotationArray2D getFieldsAnnotations() { ++ Address addr = getAddress().getAddressAt(fields_annotations.getOffset()); ++ return new AnnotationArray2D(addr); ++ } ++} +\ No newline at end of file +diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Field.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Field.java +index ebf571c7b..98cb416c9 100644 +--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Field.java ++++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Field.java +@@ -131,6 +131,8 @@ public class Field { + private Symbol genericSignature; + private AccessFlags accessFlags; + private int fieldIndex; ++ // java field redact annotation ++ private U1Array fieldAnnotations; + + /** Returns the byte offset of the field within the object or klass */ + public long getOffset() { return offset; } +@@ -182,6 +184,14 @@ public class Field { + public boolean hasInitialValue() { return holder.getFieldInitialValueIndex(fieldIndex) != 0; } + public int getInitialValueIndex() { return values.initialValueIndex; } + ++ public void setFieldAnnotations(U1Array fieldAnnotations){ ++ this.fieldAnnotations = fieldAnnotations; ++ } ++ ++ public U1Array getFieldAnnotations(){ ++ return fieldAnnotations; ++ } ++ + // + // Following accessors are for named, non-VM fields only + // +diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java +index cec603be2..719aa8435 100644 +--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java ++++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java +@@ -75,6 +75,7 @@ public class InstanceKlass extends Klass { + constants = new MetadataField(type.getAddressField("_constants"), 0); + sourceDebugExtension = type.getAddressField("_source_debug_extension"); + innerClasses = type.getAddressField("_inner_classes"); ++ annotate = type.getAddressField("_annotations"); + nonstaticFieldSize = new CIntField(type.getCIntegerField("_nonstatic_field_size"), 0); + staticFieldSize = new CIntField(type.getCIntegerField("_static_field_size"), 0); + staticOopFieldCount = new CIntField(type.getCIntegerField("_static_oop_field_count"), 0); +@@ -145,6 +146,7 @@ public class InstanceKlass extends Klass { + private static CIntField initState; + private static CIntField itableLen; + private static AddressField breakpoints; ++ private static AddressField annotate; + + // type safe enum for ClassState from instanceKlass.hpp + public static class ClassState { +@@ -854,6 +856,10 @@ public class InstanceKlass extends Klass { + return VMObjectFactory.newObject(U2Array.class, addr); + } + ++ public Annotation getAnnotation() { ++ Address addr = getAddress().getAddressAt(annotate.getOffset()); ++ return VMObjectFactory.newObject(Annotation.class, addr); ++ } + + //---------------------------------------------------------------------- + // Internals only below this point +diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/JMap.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/JMap.java +index 4c80ecd6c..fbead3ce4 100644 +--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/JMap.java ++++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/JMap.java +@@ -25,6 +25,9 @@ + package sun.jvm.hotspot.tools; + + import java.io.*; ++import java.nio.CharBuffer; ++import java.util.regex.Pattern; ++ + import sun.jvm.hotspot.debugger.JVMDebugger; + import sun.jvm.hotspot.utilities.*; + +@@ -78,6 +81,7 @@ public class JMap extends Tool { + + private static String dumpfile = "heap.bin"; + private static int gzLevel = 0; ++ private static HeapRedactor heapRedactor; + + public void run() { + Tool tool = null; +@@ -123,6 +127,7 @@ public class JMap extends Tool { + + public static void main(String[] args) { + int mode = MODE_PMAP; ++ HeapRedactor.RedactParams redactParams = new HeapRedactor.RedactParams(); + if (args.length > 1 ) { + String modeFlag = args[0]; + boolean copyArgs = true; +@@ -177,6 +182,19 @@ public class JMap extends Tool { + System.err.println("compression level out of range (1-9): " + level); + System.exit(1); + } ++ } else if (keyValue[0].equals("HeapDumpRedact")) { ++ if (!redactParams.setAndCheckHeapDumpRedact(keyValue[1])) { ++ System.exit(1); ++ } ++ } else if (keyValue[0].equals("RedactMap")) { ++ redactParams.setRedactMap(keyValue[1]); ++ } else if (keyValue[0].equals("RedactMapFile")) { ++ redactParams.setRedactMapFile(keyValue[1]); ++ } else if (keyValue[0].equals("RedactClassPath")) { ++ redactParams.setRedactClassPath(keyValue[1]); ++ } else if (keyValue[0].equals("RedactPassword")) { ++ redactParams.setRedactPassword(getRedactPassword()); ++ + } else { + System.err.println("unknown option:" + keyValue[0]); + +@@ -189,12 +207,24 @@ public class JMap extends Tool { + } + } + ++ if (redactParams.getHeapDumpRedact() == null) { ++ if (redactParams.getRedactMap() == null && redactParams.getRedactMapFile() == null ++ && redactParams.getRedactClassPath() == null) { ++ redactParams.setEnableRedact(false); ++ } else { ++ System.err.println("Error: HeapDumpRedact must be specified to enable heap-dump-redacting"); ++ copyArgs = false; ++ System.exit(1); ++ } ++ } ++ + if (copyArgs) { + String[] newArgs = new String[args.length - 1]; + for (int i = 0; i < newArgs.length; i++) { + newArgs[i] = args[i + 1]; + } + args = newArgs; ++ heapRedactor = new HeapRedactor(redactParams); + } + } + +@@ -202,9 +232,39 @@ public class JMap extends Tool { + jmap.execute(args); + } + ++ private static CharBuffer getRedactPassword() { ++ CharBuffer redactPassword = CharBuffer.wrap(""); ++ // heap dump may need a password ++ Console console = System.console(); ++ char[] passwords = null; ++ if (console == null) { ++ return redactPassword; ++ } ++ ++ try { ++ passwords = console.readPassword("redact authority password:"); ++ } catch (Exception e) { ++ } ++ if(passwords == null) { ++ return redactPassword; ++ } ++ ++ try { ++ CharBuffer cb = CharBuffer.wrap(passwords); ++ String passwordPattern = "^[0-9a-zA-Z!@#$]{1,9}$"; ++ if(!Pattern.matches(passwordPattern, cb)) { ++ return redactPassword; ++ } ++ redactPassword = cb; ++ } catch (Exception e) { ++ } ++ ++ return redactPassword; ++ } ++ + public boolean writeHeapHprofBin(String fileName, int gzLevel) { + try { +- HeapGraphWriter hgw; ++ HeapHprofBinWriter hgw; + if (gzLevel == 0) { + hgw = new HeapHprofBinWriter(); + } else if (gzLevel >=1 && gzLevel <= 9) { +@@ -213,6 +273,7 @@ public class JMap extends Tool { + System.err.println("Illegal compression level: " + gzLevel); + return false; + } ++ hgw.setHeapRedactor(heapRedactor); + hgw.write(fileName); + System.out.println("heap written to " + fileName); + return true; +diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/AnnotationArray2D.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/AnnotationArray2D.java +new file mode 100644 +index 000000000..fa2b06776 +--- /dev/null ++++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/AnnotationArray2D.java +@@ -0,0 +1,62 @@ ++/* ++ * Copyright (c) 2023, 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. Huawei designates this ++ * particular file as subject to the "Classpath" exception as provided ++ * by Huawei 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 visit https://gitee.com/openeuler/bishengjdk-8 if you need additional ++ * information or have any questions. ++ */ ++ ++package sun.jvm.hotspot.utilities; ++ ++import sun.jvm.hotspot.debugger.Address; ++import sun.jvm.hotspot.runtime.VM; ++import sun.jvm.hotspot.types.Type; ++import sun.jvm.hotspot.types.TypeDataBase; ++import sun.jvm.hotspot.types.WrongTypeException; ++ ++public class AnnotationArray2D extends GenericArray { ++ static { ++ VM.registerVMInitializedObserver(new Observer() { ++ public void update(Observable o, Object data) { ++ initialize(VM.getVM().getTypeDataBase()); ++ } ++ }); ++ } ++ ++ private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { ++ elemType = db.lookupType("Array*"); ++ ++ Type type = db.lookupType("Array*>"); ++ dataFieldOffset = type.getAddressField("_data").getOffset(); ++ } ++ ++ private static long dataFieldOffset; ++ protected static Type elemType; ++ ++ public AnnotationArray2D(Address addr) { ++ super(addr, dataFieldOffset); ++ } ++ ++ public U1Array getAt(int i) { ++ return new U1Array(getAddressAt(i)); ++ } ++ public Type getElemType() { ++ return elemType; ++ } ++} +\ No newline at end of file +diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java +index c9ffca89e..8501f4d93 100644 +--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java ++++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java +@@ -28,6 +28,10 @@ import java.io.*; + import java.nio.ByteBuffer; + import java.nio.ByteOrder; + import java.nio.channels.*; ++import java.nio.CharBuffer; ++import java.security.NoSuchAlgorithmException; ++import java.security.spec.InvalidKeySpecException; ++import java.security.spec.KeySpec; + import java.util.*; + import java.util.zip.*; + import sun.jvm.hotspot.debugger.*; +@@ -36,6 +40,10 @@ import sun.jvm.hotspot.oops.*; + import sun.jvm.hotspot.runtime.*; + import sun.jvm.hotspot.classfile.*; + ++import javax.crypto.SecretKey; ++import javax.crypto.SecretKeyFactory; ++import javax.crypto.spec.PBEKeySpec; ++ + import static java.nio.charset.StandardCharsets.UTF_8; + + /* +@@ -387,6 +395,71 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + private static final long MAX_U4_VALUE = 0xFFFFFFFFL; + int serialNum = 1; + ++ // encrypt ++ private static int SALT_MIN_LENGTH = 8; ++ private static int HASH_BIT_SIZE = 256; ++ private static int HASH_ITERATIONS_COUNT = 10000; ++ ++ // Heap Redact ++ private HeapRedactor heapRedactor; ++ ++ public HeapRedactor getHeapRedactor() { ++ return heapRedactor; ++ } ++ ++ public void setHeapRedactor(HeapRedactor heapRedactor) { ++ this.heapRedactor = heapRedactor; ++ } ++ ++ public HeapRedactor.HeapDumpRedactLevel getHeapDumpRedactLevel(){ ++ if(heapRedactor==null){ ++ return HeapRedactor.HeapDumpRedactLevel.REDACT_OFF; ++ } ++ return heapRedactor.getHeapDumpRedactLevel(); ++ } ++ ++ public Optional getHeapDumpRedactPassword() { ++ return heapRedactor == null ? Optional.empty() : Optional.ofNullable(heapRedactor.getRedactPassword()); ++ } ++ ++ private Optional lookupRedactName(String name){ ++ return heapRedactor == null ? Optional.empty() : heapRedactor.lookupRedactName(name); ++ } ++ ++ private void resetRedactParams(){ ++ Optional redactOption = getVMRedactParameter("HeapDumpRedact"); ++ String redactStr= redactOption.isPresent() ? redactOption.get() : null; ++ if(redactStr!=null && !redactStr.isEmpty()){ ++ HeapRedactor.RedactParams redactParams = new HeapRedactor.RedactParams(); ++ if(HeapRedactor.REDACT_ANNOTATION_OPTION.equals(redactStr)){ ++ Optional classPathOption = getVMRedactParameter("RedactClassPath"); ++ String classPathStr = classPathOption.isPresent() ? classPathOption.get() : null; ++ redactStr = classPathOption.isPresent() ? redactStr : HeapRedactor.REDACT_OFF_OPTION; ++ redactParams.setRedactClassPath(classPathStr); ++ } else { ++ Optional redactMapOption = getVMRedactParameter("RedactMap"); ++ String redactMapStr = redactMapOption.isPresent() ? redactMapOption.get() : null; ++ redactParams.setRedactMap(redactMapStr); ++ Optional redactMapFileOption = getVMRedactParameter("RedactMapFile"); ++ String redactMapFileStr = redactMapFileOption.isPresent() ? redactMapFileOption.get() : null; ++ redactParams.setRedactMapFile(redactMapFileStr); ++ } ++ if(!redactParams.setAndCheckHeapDumpRedact(redactStr)){ ++ redactParams.setAndCheckHeapDumpRedact(HeapRedactor.REDACT_OFF_OPTION); ++ } ++ setHeapRedactor(new HeapRedactor(redactParams)); ++ } ++ } ++ ++ private Optional getVMRedactParameter(String name) { ++ VM vm = VM.getVM(); ++ VM.Flag flag = vm.getCommandLineFlag(name); ++ if(flag == null){ ++ return Optional.empty(); ++ } ++ return Optional.ofNullable(flag.getCcstr()); ++ } ++ + public HeapHprofBinWriter() { + this.KlassMap = new ArrayList(); + this.names = new HashSet(); +@@ -399,9 +472,69 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + this.gzLevel = gzLevel; + } + ++ private boolean checkPassword() { ++ Optional redactAuthOption = getVMRedactParameter("RedactPassword"); ++ String redactAuth = redactAuthOption.isPresent() ? redactAuthOption.get() : null; ++ boolean redactAuthFlag = true; ++ if(redactAuth != null) { ++ String[] auths = redactAuth.split(","); ++ if(auths.length != 2) { ++ return redactAuthFlag; ++ } ++ ++ Optional passwordOption = getHeapDumpRedactPassword(); ++ CharBuffer password = passwordOption.isPresent() ? passwordOption.get() : CharBuffer.wrap(""); ++ char[] passwordChars = null; ++ try { ++ passwordChars = password.array(); ++ ++ byte[] saltBytes = auths[1].getBytes("UTF-8"); ++ if(saltBytes.length < SALT_MIN_LENGTH) { ++ return redactAuthFlag; ++ } ++ ++ String digestStr = getEncryptValue(passwordChars, saltBytes); ++ redactAuthFlag = auths[0].equals(digestStr); ++ } catch (Exception e) { ++ // ignore ++ redactAuthFlag = false; ++ } finally { ++ // clear all password ++ if(passwordChars != null) { ++ Arrays.fill(passwordChars, '0'); ++ } ++ } ++ } ++ ++ return redactAuthFlag; ++ } ++ ++ private String getEncryptValue(char[] passwordValue, byte[] saltBytes) throws InvalidKeySpecException, NoSuchAlgorithmException { ++ StringBuilder digestStrBuilder = new StringBuilder(); ++ ++ KeySpec spec = new PBEKeySpec(passwordValue, saltBytes, HASH_ITERATIONS_COUNT, HASH_BIT_SIZE); ++ SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); ++ SecretKey secretKey = secretKeyFactory.generateSecret(spec); ++ byte[] digestBytes = secretKey.getEncoded(); ++ for (byte b : digestBytes) { ++ String hex = Integer.toHexString(0xff & b); ++ if (hex.length() == 1) { ++ digestStrBuilder.append('0'); ++ } ++ digestStrBuilder.append(hex); ++ } ++ String digestStr = digestStrBuilder.toString(); ++ ++ return digestStr; ++ } ++ + public synchronized void write(String fileName) throws IOException { + VM vm = VM.getVM(); + ++ if(getHeapDumpRedactLevel() == HeapRedactor.HeapDumpRedactLevel.REDACT_UNKNOWN || !checkPassword()) { ++ resetRedactParams(); ++ } ++ + // Check whether we should dump the heap as segments + useSegmentedHeapDump = isCompression() || + (vm.getUniverse().heap().used() > HPROF_SEGMENTED_HEAP_DUMP_THRESHOLD); +@@ -465,6 +598,9 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + // this will write heap data into the buffer stream + super.write(); + ++ // write redacted String Field record ++ writeAnnotateFieldValue(); ++ + // flush buffer stream. + out.flush(); + if (!useSegmentedHeapDump) { +@@ -725,6 +861,68 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + } + } + ++ private void writeAnnotateFieldValue() throws IOException { ++ HeapRedactor.HeapDumpRedactLevel level = getHeapDumpRedactLevel(); ++ if(level != HeapRedactor.HeapDumpRedactLevel.REDACT_ANNOTATION ++ && level != HeapRedactor.HeapDumpRedactLevel.REDACT_DIYRULES) { ++ return; ++ } ++ ++ HeapRedactor.RedactVectorNode redactVector = heapRedactor.getHeaderNode(); ++ if(redactVector == null) { ++ return; ++ } ++ ++ while(redactVector != null) { ++ List typeArrayList = redactVector.getTypeArrayList(); ++ for(int i = 0; i < redactVector.getCurrentIndex(); i++) { ++ TypeArray array = typeArrayList.get(i); ++ TypeArrayKlass tak = (TypeArrayKlass) array.getKlass(); ++ final int type = (int) tak.getElementType(); ++ ++ if(type != TypeArrayKlass.T_BYTE) { ++ continue; ++ } ++ ++ OopHandle handle = (array != null)? array.getHandle() : null; ++ long address = getAddressValue(handle); ++ Optional annotateValueOptional = heapRedactor.lookupRedactAnnotationValue(address); ++ String annotateValue = annotateValueOptional.isPresent() ? annotateValueOptional.get() : null; ++ long expectLength = array.getLength(); ++ if(annotateValue != null) { ++ expectLength = annotateValue.length(); ++ } ++ ++ int headerSize = getArrayHeaderSize(false); ++ final String typeName = tak.getElementTypeName(); ++ final long typeSize = getSizeForType(type); ++ final int length = calculateArrayMaxLength(expectLength, ++ headerSize, ++ typeSize, ++ typeName); ++ out.writeByte((byte) HPROF_GC_PRIM_ARRAY_DUMP); ++ writeObjectID(array); ++ out.writeInt(DUMMY_STACK_TRACE_ID); ++ out.writeInt(length); ++ out.writeByte((byte) type); ++ ++ if (annotateValue != null) { ++ for(int index = 0; index < expectLength; index++) { ++ out.writeByte(annotateValue.charAt(index)); ++ } ++ } else { ++ for (int index = 0; index < length; index++) { ++ long offset = BYTE_BASE_OFFSET + index * BYTE_SIZE; ++ out.writeByte(array.getHandle().getJByteAt(offset)); ++ } ++ } ++ } ++ ++ HeapRedactor.RedactVectorNode tempVector = redactVector.getNext(); ++ redactVector = tempVector; ++ } ++ } ++ + protected void writeClass(Instance instance) throws IOException { + Klass reflectedKlass = java_lang_Class.asKlass(instance); + // dump instance record only for primitive type Class objects. +@@ -958,9 +1156,18 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + } + + protected void writePrimitiveArray(TypeArray array) throws IOException { ++ HeapRedactor.HeapDumpRedactLevel level = getHeapDumpRedactLevel(); ++ + int headerSize = getArrayHeaderSize(false); + TypeArrayKlass tak = (TypeArrayKlass) array.getKlass(); + final int type = tak.getElementType(); ++ ++ if(type == TypeArrayKlass.T_BYTE && (level == HeapRedactor.HeapDumpRedactLevel.REDACT_ANNOTATION ++ || level == HeapRedactor.HeapDumpRedactLevel.REDACT_DIYRULES)) { ++ heapRedactor.recordTypeArray(array); ++ return; ++ } ++ + final String typeName = tak.getElementTypeName(); + final long typeSize = getSizeForType(type); + final int length = calculateArrayMaxLength(array.getLength(), +@@ -972,12 +1179,16 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + out.writeInt(DUMMY_STACK_TRACE_ID); + out.writeInt(length); + out.writeByte((byte) type); ++ ++ boolean shouldRedact = ( level == HeapRedactor.HeapDumpRedactLevel.REDACT_BASIC ++ || level == HeapRedactor.HeapDumpRedactLevel.REDACT_FULL); ++ + switch (type) { + case TypeArrayKlass.T_BOOLEAN: + writeBooleanArray(array, length); + break; + case TypeArrayKlass.T_CHAR: +- writeCharArray(array, length); ++ writeCharArray(array, length, shouldRedact); + break; + case TypeArrayKlass.T_FLOAT: + writeFloatArray(array, length); +@@ -986,13 +1197,13 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + writeDoubleArray(array, length); + break; + case TypeArrayKlass.T_BYTE: +- writeByteArray(array, length); ++ writeByteArray(array, length, shouldRedact); + break; + case TypeArrayKlass.T_SHORT: + writeShortArray(array, length); + break; + case TypeArrayKlass.T_INT: +- writeIntArray(array, length); ++ writeIntArray(array, length, shouldRedact); + break; + case TypeArrayKlass.T_LONG: + writeLongArray(array, length); +@@ -1010,10 +1221,16 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + } + } + +- private void writeByteArray(TypeArray array, int length) throws IOException { +- for (int index = 0; index < length; index++) { +- long offset = BYTE_BASE_OFFSET + index * BYTE_SIZE; +- out.writeByte(array.getHandle().getJByteAt(offset)); ++ private void writeByteArray(TypeArray array, int length, boolean shouldRedact) throws IOException { ++ if (shouldRedact) { ++ for(int index = 0; index < length; index++) { ++ out.writeByte(0); ++ } ++ } else { ++ for (int index = 0; index < length; index++) { ++ long offset = BYTE_BASE_OFFSET + index * BYTE_SIZE; ++ out.writeByte(array.getHandle().getJByteAt(offset)); ++ } + } + } + +@@ -1024,10 +1241,16 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + } + } + +- private void writeIntArray(TypeArray array, int length) throws IOException { +- for (int index = 0; index < length; index++) { +- long offset = INT_BASE_OFFSET + index * INT_SIZE; +- out.writeInt(array.getHandle().getJIntAt(offset)); ++ private void writeIntArray(TypeArray array, int length, boolean shouldRedact) throws IOException { ++ if (shouldRedact) { ++ for(int index = 0; index < length; index++) { ++ out.writeInt(0); ++ } ++ } else { ++ for (int index = 0; index < length; index++) { ++ long offset = INT_BASE_OFFSET + index * INT_SIZE; ++ out.writeInt(array.getHandle().getJIntAt(offset)); ++ } + } + } + +@@ -1038,10 +1261,16 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + } + } + +- private void writeCharArray(TypeArray array, int length) throws IOException { +- for (int index = 0; index < length; index++) { +- long offset = CHAR_BASE_OFFSET + index * CHAR_SIZE; +- out.writeChar(array.getHandle().getJCharAt(offset)); ++ private void writeCharArray(TypeArray array, int length, boolean shouldRedact) throws IOException { ++ if (shouldRedact) { ++ for(int index = 0; index < length; index++) { ++ out.writeChar(0); ++ } ++ } else { ++ for (int index = 0; index < length; index++) { ++ long offset = CHAR_BASE_OFFSET + index * CHAR_SIZE; ++ out.writeChar(array.getHandle().getJCharAt(offset)); ++ } + } + } + +@@ -1083,6 +1312,16 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + for (Iterator itr = fields.iterator(); itr.hasNext();) { + writeField(itr.next(), instance); + } ++ ++ // record the anonymous value for every field ++ if(klass instanceof InstanceKlass && heapRedactor != null) { ++ if(heapRedactor.getHeapDumpRedactLevel() == HeapRedactor.HeapDumpRedactLevel.REDACT_ANNOTATION ++ && heapRedactor.getRedactAnnotationClassPath() != null && !heapRedactor.getRedactAnnotationClassPath().isEmpty()) { ++ recordAnnotationValueMap(fields, instance); ++ } else if( heapRedactor.getHeapDumpRedactLevel() == HeapRedactor.HeapDumpRedactLevel.REDACT_DIYRULES) { ++ recordDiyRulesValueMap(fields, instance); ++ } ++ } + } + + //-- Internals only below this point +@@ -1105,6 +1344,131 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + } + } + ++ private void recordAnnotationValueMap(List fields, Instance instance) { ++ Klass klass = instance.getKlass(); ++ boolean inJavaPackage = false; ++ Symbol classNameSymbol = klass.getName(); ++ if(classNameSymbol != null) { ++ String className = classNameSymbol.asString(); ++ inJavaPackage = (className != null && className.startsWith("java/")); ++ } ++ if(inJavaPackage){ ++ return; ++ } ++ for (Field field : fields) { ++ Symbol fieldSignature = field.getSignature(); ++ if(fieldSignature == null || fieldSignature.asString() == null ++ || !"Ljava/lang/String;".equals(fieldSignature.asString())) { ++ continue; ++ } ++ try { ++ InstanceKlass fieldHolder = field.getFieldHolder(); ++ U1Array fieldAnnotations = field.getFieldAnnotations(); ++ Optional anonymousValueOption = getAnonymousValue(fieldAnnotations, fieldHolder.getConstants()); ++ if(!anonymousValueOption.isPresent()) { ++ continue; ++ } ++ long address = getStringFieldAddress(field, instance); ++ if(address > 0L) { ++ heapRedactor.recordRedactAnnotationValue(address, anonymousValueOption.get()); ++ } ++ } catch (Exception e) { ++ } ++ } ++ } ++ ++ private Optional getAnonymousValue(U1Array fieldAnnotations, ConstantPool cp) { ++ Optional anonymousValueOption = Optional.empty(); ++ if (fieldAnnotations.getAddress() == null) { ++ return anonymousValueOption; ++ } ++ ++ int fieldAnnotationsTagsLen = fieldAnnotations.length(); ++ boolean isAnonymousAnnotation = false; ++ int annotationStart = 0; ++ int annotationEnd = 0; ++ for (int j = 0; j < fieldAnnotationsTagsLen; j++) { ++ int cpIndex = fieldAnnotations.at(j); ++ if (cpIndex >= cp.getLength() || cpIndex < 0) { ++ continue; ++ } ++ byte cpConstType = cp.getTags().at(cpIndex); ++ if (cpConstType == ConstantPool.JVM_CONSTANT_Utf8) { ++ annotationStart += (isAnonymousAnnotation ? 0 : 1); ++ annotationEnd++; ++ Symbol symbol = cp.getSymbolAt(cpIndex); ++ if (symbol.asString() == null || symbol.asString().isEmpty()) { ++ continue; ++ } ++ if (symbol.asString().equals("L" + heapRedactor.getRedactAnnotationClassPath() + ";")) { ++ isAnonymousAnnotation = true; ++ } ++ if(annotationEnd - annotationStart == 1 && !"value".equals(symbol.asString())) { ++ break; ++ } ++ if(annotationEnd - annotationStart == 2) { ++ anonymousValueOption = Optional.ofNullable(cp.getSymbolAt(cpIndex).asString()); ++ break; ++ } ++ } ++ } ++ return anonymousValueOption; ++ } ++ ++ private void recordDiyRulesValueMap(List fields, Instance instance) { ++ Klass klass = instance.getKlass(); ++ boolean diyRulesFlag = false; ++ Symbol classNameSymbol = klass.getName(); ++ Map redactRulesMap = null; ++ if(classNameSymbol != null) { ++ String className = classNameSymbol.asString(); ++ Optional> redactRulesMapOptional = className == null ? Optional.empty() : heapRedactor.getRedactRulesTable(className); ++ redactRulesMap = redactRulesMapOptional.isPresent() ? redactRulesMapOptional.get() : null; ++ diyRulesFlag = (redactRulesMap != null); ++ } ++ if(!diyRulesFlag){ ++ return; ++ } ++ for (Field field : fields) { ++ Symbol fieldSignature = field.getSignature(); ++ if(fieldSignature == null || fieldSignature.asString() == null || !"Ljava/lang/String;".equals(fieldSignature.asString())) { ++ continue; ++ } ++ ++ try { ++ Symbol filedNameSymbol = field.getName(); ++ if(filedNameSymbol == null) { ++ continue; ++ } ++ ++ String filedName = filedNameSymbol.asString(); ++ String replaceValue = filedName == null ? null : redactRulesMap.get(filedName); ++ long address = getStringFieldAddress(field, instance); ++ if(address > 0L && replaceValue != null) { ++ heapRedactor.recordRedactAnnotationValue(address, replaceValue); ++ } ++ } catch (Exception e) { ++ } ++ } ++ } ++ ++ private long getStringFieldAddress(Field field, Instance instance) { ++ long address = 0L; ++ if(field instanceof OopField) { ++ Oop fieldOop = ((OopField) field).getValue(instance); ++ Field stringField = null; ++ if (fieldOop != null && fieldOop.getKlass() instanceof InstanceKlass) { ++ List oopFiledSubs = ((InstanceKlass) fieldOop.getKlass()).getAllFields(); ++ stringField = oopFiledSubs.iterator().next(); ++ } ++ if (stringField != null && stringField instanceof OopField) { ++ OopHandle handle = ((OopField) stringField).getValueAsOopHandle(fieldOop); ++ address = getAddressValue(handle); ++ } ++ } ++ return address; ++ } ++ + public static int signatureToHprofKind(char ch) { + switch (ch) { + case JVM_SIGNATURE_CLASS: +@@ -1221,7 +1585,18 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + // If name is already written don't write it again. + if (names.add(sym)) { + if(sym != null) { +- byte[] buf = sym.asString().getBytes(UTF_8); ++ String symbolStr = sym.asString(); ++ HeapRedactor.HeapDumpRedactLevel level = getHeapDumpRedactLevel(); ++ boolean shouldRedact = (level == HeapRedactor.HeapDumpRedactLevel.REDACT_NAMES || ++ level == HeapRedactor.HeapDumpRedactLevel.REDACT_FULL); ++ byte[] buf = null; ++ if(shouldRedact) { ++ Optional redactField = lookupRedactName(symbolStr); ++ buf = redactField.isPresent() ? redactField.get().getBytes("UTF-8") : null; ++ } ++ if(buf == null) { ++ buf = symbolStr.getBytes(UTF_8); ++ } + writeHeader(HPROF_UTF8, buf.length + OBJ_ID_SIZE); + writeSymbolID(sym); + out.write(buf); +@@ -1301,11 +1676,23 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + List res = new ArrayList<>(); + while (klass != null) { + List curFields = klass.getImmediateFields(); ++ Annotation annotation = klass.getAnnotation(); ++ AnnotationArray2D fieldsAnnotations = (annotation == null) ? null : annotation.getFieldsAnnotations(); ++ boolean hasAnnotations = false; ++ if(fieldsAnnotations != null && fieldsAnnotations.getAddress() != null) { ++ hasAnnotations = true; ++ } ++ int fieldIndex = 0; + for (Iterator itr = curFields.iterator(); itr.hasNext();) { + Field f = itr.next(); + if (! f.isStatic()) { + res.add(f); ++ // record annotation for class Field ++ if(hasAnnotations) { ++ f.setFieldAnnotations(fieldsAnnotations.getAt(fieldIndex)); ++ } + } ++ fieldIndex++; + } + klass = (InstanceKlass) klass.getSuper(); + } +diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapRedactor.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapRedactor.java +new file mode 100644 +index 000000000..5c442b2bb +--- /dev/null ++++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapRedactor.java +@@ -0,0 +1,454 @@ ++/* ++ * Copyright (c) 2023, 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. Huawei designates this ++ * particular file as subject to the "Classpath" exception as provided ++ * by Huawei 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 visit https://gitee.com/openeuler/bishengjdk-8 if you need additional ++ * information or have any questions. ++ */ ++ ++package sun.jvm.hotspot.utilities; ++ ++import sun.jvm.hotspot.oops.TypeArray; ++ ++import java.io.BufferedReader; ++import java.io.File; ++import java.io.FileReader; ++import java.io.IOException; ++import java.nio.CharBuffer; ++import java.util.ArrayList; ++import java.util.HashMap; ++import java.util.List; ++import java.util.Map; ++import java.util.Locale; ++import java.util.Optional; ++ ++public class HeapRedactor { ++ public enum HeapDumpRedactLevel { ++ REDACT_UNKNOWN, ++ REDACT_OFF, ++ REDACT_NAMES, ++ REDACT_BASIC, ++ REDACT_DIYRULES, ++ REDACT_ANNOTATION, ++ REDACT_FULL ++ } ++ ++ private HeapDumpRedactLevel redactLevel; ++ private Map redactNameTable; ++ private Map> redactClassTable; ++ private String redactClassFullName = null; ++ private Map redactValueTable; ++ private RedactVectorNode headerNode; ++ private RedactVectorNode currentNode; ++ ++ private RedactParams redactParams; ++ ++ public static final String HEAP_DUMP_REDACT_PREFIX = "HeapDumpRedact="; ++ public static final String REDACT_MAP_PREFIX = "RedactMap="; ++ public static final String REDACT_MAP_FILE_PREFIX = "RedactMapFile="; ++ public static final String REDACT_CLASS_PATH_PREFIX = "RedactClassPath="; ++ public static final String REDACT_PASSWORD_PREFIX = "RedactPassword="; ++ ++ public static final String REDACT_UNKNOWN_STR = "UNKNOWN"; ++ public static final String REDACT_OFF_STR = "OFF"; ++ public static final String REDACT_NAME_STR = "NAMES"; ++ public static final String REDACT_BASIC_STR = "BASIC"; ++ public static final String REDACT_DIYRULES_STR = "DIYRULES"; ++ public static final String REDACT_ANNOTATION_STR = "ANNOTATION"; ++ public static final String REDACT_FULL_STR = "FULL"; ++ ++ public static final String REDACT_UNKNOWN_OPTION = REDACT_UNKNOWN_STR.toLowerCase(Locale.ROOT); ++ public static final String REDACT_OFF_OPTION = REDACT_OFF_STR.toLowerCase(Locale.ROOT); ++ public static final String REDACT_NAME_OPTION = REDACT_NAME_STR.toLowerCase(Locale.ROOT); ++ public static final String REDACT_BASIC_OPTION = REDACT_BASIC_STR.toLowerCase(Locale.ROOT); ++ public static final String REDACT_DIYRULES_OPTION = REDACT_DIYRULES_STR.toLowerCase(Locale.ROOT); ++ public static final String REDACT_ANNOTATION_OPTION = REDACT_ANNOTATION_STR.toLowerCase(Locale.ROOT); ++ public static final String REDACT_FULL_OPTION = REDACT_FULL_STR.toLowerCase(Locale.ROOT); ++ ++ public static final int PATH_MAX = 4096; ++ public static final int REDACT_VECTOR_SIZE = 1024; ++ ++ public HeapRedactor(RedactParams redactParams) { ++ this.redactParams = redactParams; ++ redactLevel = HeapDumpRedactLevel.REDACT_UNKNOWN; ++ redactNameTable = null; ++ redactClassTable = null; ++ redactValueTable = null; ++ init(null); ++ } ++ ++ private void init(String options) { ++ if (redactLevel == HeapDumpRedactLevel.REDACT_UNKNOWN) { ++ initHeapdumpRedactLevel(options); ++ } ++ } ++ ++ public HeapDumpRedactLevel getHeapDumpRedactLevel() { ++ return redactLevel; ++ } ++ ++ public String getRedactLevelString() { ++ switch (redactLevel) { ++ case REDACT_BASIC: ++ return REDACT_BASIC_STR; ++ case REDACT_NAMES: ++ return REDACT_NAME_STR; ++ case REDACT_FULL: ++ return REDACT_FULL_STR; ++ case REDACT_DIYRULES: ++ return REDACT_DIYRULES_STR; ++ case REDACT_ANNOTATION: ++ return REDACT_ANNOTATION_STR; ++ case REDACT_OFF: ++ return REDACT_OFF_STR; ++ case REDACT_UNKNOWN: ++ default: ++ return REDACT_UNKNOWN_STR; ++ } ++ } ++ ++ public Optional lookupRedactName(String name){ ++ return Optional.ofNullable(redactNameTable == null ? null : redactNameTable.get(name)); ++ } ++ ++ public void recordTypeArray(TypeArray oop) { ++ int tmp_index = currentNode.getCurrentIndex(); ++ if(tmp_index == REDACT_VECTOR_SIZE){ ++ RedactVectorNode newNode = new RedactVectorNode(); ++ List list = new ArrayList<>(REDACT_VECTOR_SIZE); ++ newNode.setTypeArrayList(list); ++ newNode.setNext(null); ++ newNode.setCurrentIndex(0); ++ tmp_index = 0; ++ currentNode.setNext(newNode); ++ currentNode = newNode; ++ } ++ currentNode.getTypeArrayList().add(tmp_index, oop); ++ tmp_index++; ++ currentNode.setCurrentIndex(tmp_index); ++ ++ } ++ ++ public RedactVectorNode getHeaderNode(){ ++ return headerNode; ++ } ++ ++ public void recordRedactAnnotationValue(Long addr, String value) { ++ redactValueTable.put(addr, value); ++ } ++ ++ public Optional lookupRedactAnnotationValue(Long addr){ ++ return Optional.ofNullable(redactValueTable == null ? null : redactValueTable.get(addr)); ++ } ++ ++ public String getRedactAnnotationClassPath(){ ++ return redactParams.getRedactClassPath(); ++ } ++ ++ public CharBuffer getRedactPassword(){ ++ return redactParams.getRedactPassword(); ++ } ++ ++ public Optional> getRedactRulesTable(String key) { ++ return Optional.ofNullable(redactClassTable == null ? null: redactClassTable.get(key)); ++ } ++ ++ private HeapDumpRedactLevel initHeapdumpRedactLevel(String options) { ++ RedactParams customizedParams = parseRedactOptions(options); ++ ++ if (customizedParams.isEnableRedact() || this.redactParams == null) { ++ this.redactParams = customizedParams; ++ } ++ ++ if (redactParams.heapDumpRedact == null) { ++ redactLevel = HeapDumpRedactLevel.REDACT_UNKNOWN; ++ } else { ++ if (REDACT_BASIC_OPTION.equals(redactParams.heapDumpRedact)) { ++ redactLevel = HeapDumpRedactLevel.REDACT_BASIC; ++ } else if (REDACT_NAME_OPTION.equals(redactParams.heapDumpRedact)) { ++ redactLevel = HeapDumpRedactLevel.REDACT_NAMES; ++ initRedactMap(); ++ } else if (REDACT_FULL_OPTION.equals(redactParams.heapDumpRedact)) { ++ redactLevel = HeapDumpRedactLevel.REDACT_FULL; ++ initRedactMap(); ++ } else if (REDACT_DIYRULES_OPTION.equals(redactParams.heapDumpRedact)) { ++ redactLevel = HeapDumpRedactLevel.REDACT_DIYRULES; ++ initRedactMap(); ++ initRedactVector(); ++ } else if (REDACT_ANNOTATION_OPTION.equals(redactParams.heapDumpRedact)) { ++ redactLevel = HeapDumpRedactLevel.REDACT_ANNOTATION; ++ initRedactVector(); ++ } else { ++ redactLevel = HeapDumpRedactLevel.REDACT_OFF; ++ } ++ } ++ return redactLevel; ++ } ++ ++ private void initRedactVector(){ ++ if(redactValueTable == null) { ++ redactValueTable = new HashMap<>(); ++ } ++ if(headerNode == null) { ++ headerNode = new RedactVectorNode(); ++ List list = new ArrayList<>(REDACT_VECTOR_SIZE); ++ headerNode.setTypeArrayList(list); ++ headerNode.setNext(null); ++ headerNode.setCurrentIndex(0); ++ currentNode = headerNode; ++ } ++ } ++ ++ private RedactParams parseRedactOptions(String optionStr) { ++ RedactParams params = new RedactParams(REDACT_OFF_OPTION, null, null, null, null); ++ if (optionStr != null) { ++ String[] options = optionStr.split(","); ++ for (String option : options) { ++ if (option.startsWith(HEAP_DUMP_REDACT_PREFIX)) { ++ params.setAndCheckHeapDumpRedact(option.substring(HEAP_DUMP_REDACT_PREFIX.length())); ++ } else if (option.startsWith(REDACT_MAP_PREFIX)) { ++ params.setRedactMap(option.substring(REDACT_MAP_PREFIX.length())); ++ } else if (option.startsWith(REDACT_MAP_FILE_PREFIX)) { ++ params.setRedactMapFile(option.substring(REDACT_MAP_FILE_PREFIX.length())); ++ } else if (option.startsWith(REDACT_CLASS_PATH_PREFIX)) { ++ params.setRedactClassPath(option.substring(REDACT_CLASS_PATH_PREFIX.length())); ++ } else { ++ continue; ++ } ++ } ++ } ++ return params; ++ } ++ ++ private void initRedactMap() { ++ if (redactParams.redactMapFile != null) { ++ readRedactMapFromFile(redactParams.redactMapFile); ++ } ++ if (redactParams.redactMap != null) { ++ parseRedactMapStringDependOnMode(redactParams.redactMap, redactLevel); ++ } ++ } ++ ++ private void readRedactMapFromFile(String path) { ++ if (path == null || path.isEmpty()) { ++ // RedactMapFile= not specified ++ return; ++ } else { ++ if (path.length() >= PATH_MAX) { ++ System.err.println("RedactMap File path is too long"); ++ return; ++ } ++ File file = new File(path); ++ if (!file.exists() || !file.isFile()) { ++ System.err.println("RedactMap File does not exist"); ++ } ++ try (BufferedReader reader = new BufferedReader(new FileReader(path))) { ++ String line; ++ while ((line = reader.readLine()) != null) { ++ parseRedactMapStringDependOnMode(line, redactLevel); ++ } ++ } catch (IOException e) { ++ System.err.println("Encounter an error when reading " + path + " , skip processing RedactMap File."); ++ e.printStackTrace(); ++ return; ++ } ++ } ++ } ++ ++ private void parseRedactMapStringDependOnMode(String nameMapList, HeapDumpRedactLevel redactLevel) { ++ if(redactLevel == HeapDumpRedactLevel.REDACT_DIYRULES) { ++ parseRedactDiyRulesString(nameMapList); ++ } else { ++ parseRedactMapString(nameMapList); ++ } ++ } ++ ++ private void parseRedactMapString(String nameMapList) { ++ if (redactNameTable == null) { ++ redactNameTable = new HashMap<>(); ++ } ++ String[] tokens = nameMapList.split("[,;\\s+]"); ++ for (String token : tokens) { ++ String[] pair = token.split(":"); ++ if (pair.length == 2) { ++ redactNameTable.put(pair[0], pair[1]); ++ } ++ } ++ } ++ ++ private void parseRedactDiyRulesString(String nameMapList) { ++ if (redactClassTable == null) { ++ redactClassTable = new HashMap<>(); ++ } ++ Map redactRulesTable = redactClassFullName == null ? null : redactClassTable.get(redactClassFullName); ++ String[] tokens = nameMapList.split("[,;\\s+]"); ++ for (String token : tokens) { ++ String[] pair = token.split(":"); ++ if (pair.length == 1) { ++ redactClassFullName = pair[0].replace(".", "/"); ++ redactRulesTable = redactClassTable.get(redactClassFullName); ++ if(redactRulesTable == null) { ++ redactRulesTable = new HashMap<>(); ++ redactClassTable.put(redactClassFullName, redactRulesTable); ++ } ++ } ++ if (pair.length == 2 && redactRulesTable != null) { ++ redactRulesTable.put(pair[0], pair[1]); ++ } ++ } ++ } ++ ++ public static class RedactParams { ++ private String heapDumpRedact; ++ private String redactMap; ++ private String redactMapFile; ++ private String redactClassPath; ++ private CharBuffer redactPassword; ++ private boolean enableRedact = false; ++ ++ public RedactParams() { ++ } ++ ++ public RedactParams(String heapDumpRedact, String redactMap, String redactMapFile, String redactClassPath, CharBuffer redactPassword) { ++ this.heapDumpRedact = heapDumpRedact; ++ this.redactMap = redactMap; ++ this.redactMapFile = redactMapFile; ++ this.redactClassPath = redactClassPath; ++ this.redactPassword = redactPassword; ++ } ++ ++ @Override ++ public String toString() { ++ StringBuilder builder = new StringBuilder(); ++ if (heapDumpRedact != null) { ++ builder.append(HEAP_DUMP_REDACT_PREFIX); ++ builder.append(heapDumpRedact); ++ builder.append(","); ++ } ++ if (redactMap != null) { ++ builder.append(REDACT_MAP_PREFIX); ++ builder.append(redactMap); ++ builder.append(","); ++ } ++ if (redactMapFile != null) { ++ builder.append(REDACT_MAP_FILE_PREFIX); ++ builder.append(redactMapFile); ++ builder.append(","); ++ } ++ if (redactClassPath != null) { ++ builder.append(REDACT_CLASS_PATH_PREFIX); ++ builder.append(redactClassPath); ++ } ++ return builder.toString(); ++ } ++ ++ public String getHeapDumpRedact() { ++ return heapDumpRedact; ++ } ++ ++ public boolean setAndCheckHeapDumpRedact(String heapDumpRedact) { ++ if (!checkLauncherHeapdumpRedactSupport(heapDumpRedact)) { ++ return false; ++ } ++ this.heapDumpRedact = heapDumpRedact; ++ this.enableRedact = true; ++ return true; ++ } ++ ++ public String getRedactMap() { ++ return redactMap; ++ } ++ ++ public void setRedactMap(String redactMap) { ++ this.redactMap = redactMap; ++ } ++ ++ public String getRedactMapFile() { ++ return redactMapFile; ++ } ++ ++ public void setRedactMapFile(String redactMapFile) { ++ this.redactMapFile = redactMapFile; ++ } ++ ++ public String getRedactClassPath() { ++ return redactClassPath; ++ } ++ ++ public void setRedactClassPath(String redactClassPath) { ++ this.redactClassPath = redactClassPath; ++ } ++ ++ public CharBuffer getRedactPassword() { ++ return redactPassword; ++ } ++ ++ public void setRedactPassword(CharBuffer redactPassword) { ++ this.redactPassword = redactPassword; ++ } ++ ++ public static boolean checkLauncherHeapdumpRedactSupport(String value) { ++ String[] validValues = {REDACT_BASIC_OPTION, REDACT_NAME_OPTION, REDACT_FULL_OPTION, REDACT_DIYRULES_OPTION, REDACT_ANNOTATION_OPTION, REDACT_OFF_OPTION}; ++ for (String validValue : validValues) { ++ if (validValue.equals(value)) { ++ return true; ++ } ++ } ++ return false; ++ } ++ ++ public boolean isEnableRedact() { ++ return enableRedact; ++ } ++ ++ public void setEnableRedact(boolean enableRedact) { ++ this.enableRedact = enableRedact; ++ } ++ } ++ ++ public class RedactVectorNode{ ++ private List typeArrayList; ++ private RedactVectorNode next; ++ private int currentIndex; ++ ++ public List getTypeArrayList() { ++ return typeArrayList; ++ } ++ ++ public void setTypeArrayList(List list) { ++ this.typeArrayList = list; ++ } ++ ++ public RedactVectorNode getNext() { ++ return next; ++ } ++ ++ public void setNext(RedactVectorNode next) { ++ this.next = next; ++ } ++ ++ public int getCurrentIndex() { ++ return currentIndex; ++ } ++ ++ public void setCurrentIndex(int index) { ++ this.currentIndex = index; ++ } ++ } ++} +\ No newline at end of file +diff --git a/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java b/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java +index 8939d6093..50757d2d7 100644 +--- a/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java ++++ b/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java +@@ -25,16 +25,29 @@ + + package sun.tools.jmap; + ++import java.io.BufferedInputStream; ++import java.io.Console; + import java.io.File; + import java.io.IOException; + import java.io.InputStream; ++import java.io.InputStreamReader; ++import java.nio.CharBuffer; ++import java.security.NoSuchAlgorithmException; ++import java.security.spec.InvalidKeySpecException; ++import java.security.spec.KeySpec; ++import java.util.Arrays; + import java.util.Collection; ++import java.util.regex.Pattern; + + import com.sun.tools.attach.VirtualMachine; + import com.sun.tools.attach.AttachNotSupportedException; + import sun.tools.attach.HotSpotVirtualMachine; + import sun.tools.common.ProcessArgumentMatcher; + ++import javax.crypto.SecretKey; ++import javax.crypto.SecretKeyFactory; ++import javax.crypto.spec.PBEKeySpec; ++ + import static java.nio.charset.StandardCharsets.UTF_8; + + /* +@@ -45,6 +58,10 @@ import static java.nio.charset.StandardCharsets.UTF_8; + * options are mapped to SA tools. + */ + public class JMap { ++ // encrypt ++ private static int SALT_MIN_LENGTH = 8; ++ private static int HASH_BIT_SIZE = 256; ++ private static int HASH_ITERATIONS_COUNT = 10000; + + public static void main(String[] args) throws Exception { + if (args.length == 0) { +@@ -206,6 +223,8 @@ public class JMap { + String filename = null; + String liveopt = "-all"; + String compress_level = null; ++ RedactParams redactParams = new RedactParams(); ++ String redactPassword = ",RedactPassword="; + + for (String subopt : subopts) { + if (subopt.equals("") || subopt.equals("all")) { +@@ -226,6 +245,18 @@ public class JMap { + System.err.println("Fail: no number provided in option: '" + subopt + "'"); + usage(1); + } ++ } else if (subopt.startsWith("HeapDumpRedact=")) { ++ if (!redactParams.setAndCheckHeapDumpRedact(subopt.substring("HeapDumpRedact=".length()))) { ++ usage(1); ++ } ++ } else if (subopt.startsWith("RedactMap=")) { ++ redactParams.setRedactMap(subopt.substring("RedactMap=".length())); ++ } else if (subopt.startsWith("RedactMapFile=")) { ++ redactParams.setRedactMapFile(subopt.substring("RedactMapFile=".length())); ++ } else if (subopt.startsWith("RedactClassPath")) { ++ redactParams.setRedactClassPath(subopt.substring("RedactClassPath=".length())); ++ } else if (subopt.startsWith("RedactPassword")) { ++ redactPassword = getRedactPassword(pid); + } else { + System.err.println("Fail: invalid option: '" + subopt + "'"); + usage(1); +@@ -237,10 +268,121 @@ public class JMap { + usage(1); + } + ++ checkRedactParams(redactParams); ++ + System.out.flush(); + + // dumpHeap is not the same as jcmd GC.heap_dump +- executeCommandForPid(pid, "dumpheap", filename, liveopt, compress_level); ++ String heapDumpRedactParams = redactParams.isEnableRedact() ? ";" + redactParams.toDumpArgString() + redactPassword : ""; ++ executeCommandForPid(pid, "dumpheap", filename + heapDumpRedactParams, liveopt, compress_level); ++ } ++ ++ private static void checkRedactParams(RedactParams redactParams) { ++ if (redactParams.getHeapDumpRedact() == null) { ++ if (redactParams.getRedactMap() == null && redactParams.getRedactMapFile() == null) { ++ redactParams.setEnableRedact(false); ++ } else { ++ System.err.println("Error: HeapDumpRedact must be specified to enable heap-dump-redacting"); ++ usage(1); ++ } ++ } ++ } ++ ++ private static String getRedactPassword(String pid) { ++ String redactPassword = ",RedactPassword="; ++ // heap dump may need a password ++ Console console = System.console(); ++ char[] passwords = null; ++ if (console == null) { ++ return redactPassword; ++ } ++ ++ try { ++ passwords = console.readPassword("redact authority password:"); ++ } catch (Exception e) { ++ } ++ if(passwords == null) { ++ return redactPassword; ++ } ++ ++ String digestStr = null; ++ try { ++ CharBuffer cb = CharBuffer.wrap(passwords); ++ String passwordPattern = "^[0-9a-zA-Z!@#$]{1,9}$"; ++ if(!Pattern.matches(passwordPattern, cb)) { ++ return redactPassword; ++ } ++ ++ String salt = getSalt(pid); ++ if(salt == null) { ++ return redactPassword; ++ } ++ byte[] saltBytes = salt.getBytes("UTF-8"); ++ if(saltBytes.length < SALT_MIN_LENGTH) { ++ return redactPassword; ++ } ++ digestStr = getEncryptValue(passwords, saltBytes); ++ } catch (Exception e) { ++ }finally { ++ // clear all password ++ if(passwords != null) { ++ Arrays.fill(passwords, '0'); ++ } ++ } ++ ++ redactPassword += (digestStr == null ? "" : digestStr); ++ return redactPassword; ++ } ++ ++ private static String getSalt(String pid) throws Exception { ++ String salt = null; ++ StringBuilder redactAuth = new StringBuilder(); ++ ++ VirtualMachine vm = VirtualMachine.attach(pid); ++ HotSpotVirtualMachine hvm = (HotSpotVirtualMachine) vm; ++ String flag = "RedactPassword"; ++ try (InputStream in = hvm.printFlag(flag); BufferedInputStream bis = new BufferedInputStream(in); ++ InputStreamReader isr = new InputStreamReader(bis, "UTF-8")) { ++ char c[] = new char[256]; ++ int n; ++ do { ++ n = isr.read(c); ++ ++ if (n > 0) { ++ redactAuth.append(n == c.length ? c : Arrays.copyOf(c, n)); ++ } ++ } while (n > 0); ++ } ++ vm.detach(); ++ ++ if(redactAuth.length() > 0) { ++ String[] auths = redactAuth.toString().split(","); ++ if(auths.length != 2) { ++ return salt; ++ } ++ return auths[1].trim(); ++ } ++ ++ return salt; ++ } ++ ++ private static String getEncryptValue(char[] passwordValue, byte[] saltBytes) throws InvalidKeySpecException, NoSuchAlgorithmException { ++ StringBuilder digestStrBuilder = new StringBuilder(); ++ ++ KeySpec spec = new PBEKeySpec(passwordValue, saltBytes, HASH_ITERATIONS_COUNT, HASH_BIT_SIZE); ++ SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); ++ SecretKey secretKey = secretKeyFactory.generateSecret(spec); ++ byte[] digestBytes = secretKey.getEncoded(); ++ for (byte b : digestBytes) { ++ String hex = Integer.toHexString(0xff & b); ++ if (hex.length() == 1) { ++ digestStrBuilder.append('0'); ++ } ++ digestStrBuilder.append(hex); ++ } ++ String digestStr = digestStrBuilder.toString(); ++ ++ return digestStr; + } + + private static void checkForUnsupportedOptions(String[] args) { +@@ -307,6 +449,12 @@ public class JMap { + System.err.println(" file= dump heap to "); + System.err.println(" gz= If specified, the heap dump is written in gzipped format using the given compression level."); + System.err.println(" 1 (recommended) is the fastest, 9 the strongest compression."); ++ System.err.println(" HeapDumpRedact= redact the heapdump information to remove sensitive data,"); ++ System.err.println(" RedactMap= Redact the class and field names to other strings"); ++ System.err.println(" RedactMapFile= file path of the redact map"); ++ System.err.println(" RedactClassPath= full path of the redact annotation"); ++ System.err.println(" RedactPassword maybe redact feature has an authority, will wait for a password, "); ++ System.err.println(" without a correct password, heap dump with default redact level"); + System.err.println(""); + System.err.println(" Example: jmap -dump:live,format=b,file=heap.bin "); + System.err.println(""); +@@ -322,4 +470,112 @@ public class JMap { + System.err.println(" Example: jmap -histo:live,file=/tmp/histo.data "); + System.exit(exit); + } ++ ++ public static class RedactParams { ++ private boolean enableRedact = false; ++ private String heapDumpRedact; ++ private String redactMap; ++ private String redactMapFile; ++ private String redactClassPath; ++ ++ public RedactParams() { ++ } ++ ++ public RedactParams(String heapDumpRedact, String redactMap, String redactMapFile, String redactClassPath) { ++ if (heapDumpRedact != null && checkLauncherHeapdumpRedactSupport(heapDumpRedact)) { ++ enableRedact = true; ++ } ++ this.heapDumpRedact = heapDumpRedact; ++ this.redactMap = redactMap; ++ this.redactMapFile = redactMapFile; ++ this.redactClassPath = redactClassPath; ++ } ++ ++ @Override ++ public String toString() { ++ StringBuilder builder = new StringBuilder(); ++ if (heapDumpRedact != null) { ++ builder.append("HeapDumpRedact="); ++ builder.append(heapDumpRedact); ++ builder.append(","); ++ } ++ if (redactMap != null) { ++ builder.append("RedactMap="); ++ builder.append(redactMap); ++ builder.append(","); ++ } ++ if (redactMapFile != null) { ++ builder.append("RedactMapFile="); ++ builder.append(redactMapFile); ++ builder.append(","); ++ } ++ if (redactClassPath != null) { ++ builder.append("RedactClassPath="); ++ builder.append(redactClassPath); ++ } ++ return builder.toString(); ++ } ++ ++ public String toDumpArgString() { ++ return "-HeapDumpRedact=" + (heapDumpRedact == null ? "off" : heapDumpRedact) + ++ ",RedactMap=" + (redactMap == null ? "" : redactMap) + ++ ",RedactMapFile=" + (redactMapFile == null ? "" : redactMapFile) + ++ ",RedactClassPath=" + (redactClassPath == null ? "" : redactClassPath); ++ } ++ ++ public static boolean checkLauncherHeapdumpRedactSupport(String value) { ++ String[] validValues = {"basic", "names", "full", "diyrules", "annotation", "off"}; ++ for (String validValue : validValues) { ++ if (validValue.equals(value)) { ++ return true; ++ } ++ } ++ return false; ++ } ++ ++ public boolean isEnableRedact() { ++ return enableRedact; ++ } ++ ++ public void setEnableRedact(boolean enableRedact) { ++ this.enableRedact = enableRedact; ++ } ++ ++ public String getHeapDumpRedact() { ++ return heapDumpRedact; ++ } ++ ++ public boolean setAndCheckHeapDumpRedact(String heapDumpRedact) { ++ if (!checkLauncherHeapdumpRedactSupport(heapDumpRedact)) { ++ return false; ++ } ++ this.heapDumpRedact = heapDumpRedact; ++ this.enableRedact = true; ++ return true; ++ } ++ ++ public String getRedactMap() { ++ return redactMap; ++ } ++ ++ public void setRedactMap(String redactMap) { ++ this.redactMap = redactMap; ++ } ++ ++ public String getRedactMapFile() { ++ return redactMapFile; ++ } ++ ++ public void setRedactMapFile(String redactMapFile) { ++ this.redactMapFile = redactMapFile; ++ } ++ ++ public String getRedactClassPath() { ++ return redactClassPath; ++ } ++ ++ public void setRedactClassPath(String redactClassPath) { ++ this.redactClassPath = redactClassPath; ++ } ++ } + } +-- +2.47.0.windows.2 + diff --git a/jdk-updates-jdk21u-jdk-21.0.7+6.tar.gz b/jdk-updates-jdk21u-jdk-21.0.7+6.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..48562cf217c8af99e67ec04ee8c31979053b6091 Binary files /dev/null and b/jdk-updates-jdk21u-jdk-21.0.7+6.tar.gz differ diff --git a/openjdk-21.spec b/openjdk-21.spec index 7bfd6c9c5c0f3a5438114f5c64fa947e623bc48b..98ce7a842ed6c066f8b52bdd9e7204be6d5916f4 100644 --- a/openjdk-21.spec +++ b/openjdk-21.spec @@ -160,7 +160,7 @@ # Used via new version scheme. JDK 19 was # GA'ed in March 2022 => 22.3 %global vendor_version_string BiSheng -%global securityver 6 +%global securityver 7 # buildjdkver is usually same as %%{majorver}, # but in time of bootstrap of next jdk, it is majorver-1, # and this it is better to change it here, on single place @@ -180,7 +180,7 @@ %global origin_nice OpenJDK %global top_level_dir_name %{origin} %global minorver 0 -%global buildver 7 +%global buildver 6 %global rpmrelease 1 # priority must be 8 digits in total; up to openjdk 1.8, we were using 18..... so when we moved to 11, we had to add another digit %if %is_system_jdk @@ -560,6 +560,9 @@ exit 0 %ifarch %{svml_arches} %{_jvmdir}/%{sdkdir -- %{?1}}/lib/libjsvml.so %endif +%ifarch %{aarch64} +%{_jvmdir}/%{sdkdir -- %{?1}}/lib/libj2kae.so +%endif %{_jvmdir}/%{sdkdir -- %{?1}}/lib/libsyslookup.so %{_jvmdir}/%{sdkdir -- %{?1}}/lib/libverify.so %{_jvmdir}/%{sdkdir -- %{?1}}/lib/libzip.so @@ -596,6 +599,9 @@ exit 0 %{etcjavadir -- %{?1}}/conf/security/policy/README.txt %config(noreplace) %{etcjavadir -- %{?1}}/conf/security/java.policy %config(noreplace) %{etcjavadir -- %{?1}}/conf/security/java.security +%ifarch %{aarch64} +%config(noreplace) %{etcjavadir -- %{?1}}/conf/kaeprovider.conf +%endif %config(noreplace) %{etcjavadir -- %{?1}}/conf/logging.properties %config(noreplace) %{etcjavadir -- %{?1}}/conf/security/nss.cfg %config(noreplace) %{etcjavadir -- %{?1}}/conf/management/jmxremote.access @@ -899,7 +905,7 @@ Name: java-21-%{origin} Version: %{newjavaver}.%{buildver} # This package needs `.rolling` as part of Release so as to not conflict on install with # java-X-openjdk. I.e. when latest rolling release is also an LTS release packaged as -Release: 1 +Release: 5 # 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 @@ -996,7 +1002,6 @@ Patch39: Backport-of-8330191-Fix-typo-in-precompiled.hpp.patch Patch40: Backport-of-8337787-Fix-Wzero-as-null-pointer-consta.patch Patch41: Backport-of-8337712-Wrong-javadoc-in-java.util.Date-.patch Patch49: Backport-JDK-8328107-Shenandoah-C2-TestVerifyLoopOpt.patch -Patch50: Backport-JDK-8328553-Get-rid-of-JApplet-in-test-jdk-.patch Patch51: Backport-JDK-8329754-The-ThreadSafe-attribute-is-ign.patch Patch53: Backport-JDK-8335638-Calling-VarHandle.-access-mode-.patch Patch54: Backport-JDK-8338938-The-result-of-the-combine-metho.patch @@ -1005,7 +1010,6 @@ Patch56: Backport-JDK-8304484-CDS-dynamic-dumping-incorrectly.patch Patch57: Backport-JDK-8322812-Manpage-for-jcmd-is-missing-JFR.patch Patch58: Backport-JDK-8327538-The-SSLExtension-class-specifie.patch Patch60: Backport-JDK-8333599-Improve-description-of-b-matche.patch -Patch62: Backport-JDK-8336012-Fix-usages-of-jtreg-reserved-pr.patch Patch63: Backport-JDK-8336879-Always-true-condition-img-null-.patch Patch64: Backport-JDK-8337334-Test-tools-javac-7142086-T71420.patch Patch65: Backport-JDK-8340623-Remove-outdated-PROCESSOR_ARCHI.patch @@ -1020,6 +1024,19 @@ Patch73: Backport-JDK-8325730-StringBuilder.toString-allocati.patch Patch74: Backport-JDK-8337679-Memset-warning-in-src-hotspot-s.patch Patch75: Backport-JDK-8326957-Implement-JEP-474-ZGC-Generatio.patch Patch76: Backport-JDK-8320308-C2-compilation-crashes-in-Libra.patch +Patch78: huawei-Add-KAE-Provider.patch +Patch79: huawei-Add-KAE-zip-feature.patch +Patch80: huawei-Fix-arm32-build-failed-undefined-reference.patch +Patch81: huawei-Add-Fast-Serialzer.patch +Patch82: huawei-Add-Hashmap-frontCache-opt.patch +Patch83: huawei-1-add-port-jbolt-feature.patch +Patch84: huawei-heap-dump-redact-support.patch +Patch85: huawei-Adapt-to-clang-build-toolchain.patch + +#21.0.7 +Patch87: huawei-fix-build-fail-realpath.patch +Patch88: 8352716-tz-Update-Timezone-Data-to-2025b.patch +Patch89: heapdump-bug-fix.patch ############################################ # # LoongArch64 specific patches @@ -1027,6 +1044,19 @@ Patch76: Backport-JDK-8320308-C2-compilation-crashes-in-Libra.patch ############################################ Patch2000: LoongArch64-support.patch +############################################ +# +# RISC-V specific patches +# +############################################ +Patch3001: Backport-JDK-8315338-RISC-V-Change-flags-for-stable-.patch +Patch3002: Backport-JDK-8329083-RISC-V-Update-profiles-supporte.patch +Patch3003: Backport-JDK-8343555-RISC-V-make-some-verified-on-ha.patch +Patch3004: Backport-JDK-8348554-Enhance-Linux-kernel-version-ch.patch +Patch3005: Backport-JDK-8348384-RISC-V-Disable-auto-enable-Vect.patch +Patch3006: Backport-JDK-8352673-RISC-V-Vector-can-t-be-turned-o.patch +Patch3007: Backport-JDK-8355878-RISC-V-jdk-incubator-vector-Dou.patch + BuildRequires: autoconf BuildRequires: automake BuildRequires: alsa-lib-devel @@ -1058,6 +1088,7 @@ BuildRequires: nss-devel BuildRequires: pkgconfig BuildRequires: xorg-x11-proto-devel BuildRequires: zip +BuildRequires: openssl-devel BuildRequires: javapackages-filesystem BuildRequires: java-21-openjdk-devel # Zero-assembler build requirement @@ -1276,7 +1307,6 @@ pushd %{top_level_dir_name} %patch40 -p1 %patch41 -p1 %patch49 -p1 -%patch50 -p1 %patch51 -p1 %patch53 -p1 %patch54 -p1 @@ -1285,7 +1315,6 @@ pushd %{top_level_dir_name} %patch57 -p1 %patch58 -p1 %patch60 -p1 -%patch62 -p1 %patch63 -p1 %patch64 -p1 %patch65 -p1 @@ -1300,6 +1329,17 @@ pushd %{top_level_dir_name} %patch74 -p1 %patch75 -p1 %patch76 -p1 +%patch78 -p1 +%patch79 -p1 +%patch80 -p1 +%patch81 -p1 +%patch82 -p1 +%patch83 -p1 +%patch84 -p1 +%patch85 -p1 +%patch87 -p1 +%patch88 -p1 +%patch89 -p1 popd # openjdk %endif @@ -1309,6 +1349,18 @@ pushd %{top_level_dir_name} popd %endif +%ifarch riscv64 +pushd %{top_level_dir_name} +%patch3001 -p1 +%patch3002 -p1 +%patch3003 -p1 +%patch3004 -p1 +%patch3005 -p1 +%patch3006 -p1 +%patch3007 -p1 +popd +%endif + # Extract systemtap tapsets %if %{with_systemtap} tar --strip-components=1 -x -I xz -f %{SOURCE8} @@ -1426,6 +1478,9 @@ bash ../configure \ %ifarch %{ppc64le} --with-jobs=1 \ %endif +%ifarch %{aarch64} + --enable-kae=yes \ +%endif %if "%toolchain" == "clang" --with-toolchain-type=clang \ %endif @@ -1860,6 +1915,52 @@ cjc.mainProgram(args) -- the returns from copy_jdk_configs.lua should not affect %changelog +* Sat May 17 2025 Benshuai5D - 1:21.0.7.6-5 +- add heapdump-bug-fix.patch + +* Wed May 14 2025 Dingli Zhang - 1:21.0.7.6-4 +- add Backport-JDK-8352673-RISC-V-Vector-can-t-be-turned-o.patch +- add Backport-JDK-8355878-RISC-V-jdk-incubator-vector-Dou.patch + +* Sat May 10 2025 wulongyao - 1:21.0.7.6-3 +- delete huawei-Add-KAE-SM2.patch + +* Wed Apr 30 2025 songliyang - 1:21.0.7.6-2 +- update LoongArch64 port to 21.0.7 +- fix changelog date error + +* Tue Apr 29 2025 wulongyao - 1:21.0.7.6-1 +- add 8352716-tz-Update-Timezone-Data-to-2025b.patch + +* Thu Apr 17 2025 DXwangg - 1:21.0.7.6-0 +- update to jdk21.0.7-ga +- delete Backport-JDK-8328553-Get-rid-of-JApplet-in-test-jdk-.patch +- delete Backport-JDK-8336012-Fix-usages-of-jtreg-reserved-pr.patch +- delete huawei-8347965-tz-Update-Timezone-Data-to-2025a.patch +- modify huawei-Adapt-to-clang-build-toolchain.patch +- add huawei-fix-build-fail-realpath.patch +- modify huawei-heap-dump-redact-support.patch + +* Mon Mar 24 2025 Dingli Zhang - 1:21.0.6.7-3 +- add RVA23 profile and disable auto-enable Vector on buggy kernels for riscv64 +- add Backport-JDK-8315338-RISC-V-Change-flags-for-stable-.patch +- add Backport-JDK-8329083-RISC-V-Update-profiles-supporte.patch +- add Backport-JDK-8343555-RISC-V-make-some-verified-on-ha.patch +- add Backport-JDK-8348554-Enhance-Linux-kernel-version-ch.patch +- add Backport-JDK-8348384-RISC-V-Disable-auto-enable-Vect.patch + +* Sat Mar 22 2025 wulongyao - 1:21.0.6.7-2 +- add huawei-8347965-tz-Update-Timezone-Data-to-2025a.patch +- add huawei-Add-KAE-Provider.patch +- add huawei-Add-KAE-zip-feature.patch +- add huawei-Fix-arm32-build-failed-undefined-reference.patch +- add huawei-Add-Fast-Serialzer.patch +- add huawei-Add-Hashmap-frontCache-opt.patch +- add huawei-1-add-port-jbolt-feature.patch +- add huawei-heap-dump-redact-support.patch +- add huawei-Adapt-to-clang-build-toolchain.patch +- add huawei-Add-KAE-SM2.patch + * Tue Feb 11 2025 Pan Xuefeng - 1:21.0.6.7-1 - update LoongArch64 port to 21.0.6 @@ -1982,7 +2083,7 @@ cjc.mainProgram(args) -- the returns from copy_jdk_configs.lua should not affect * Wed Aug 7 2024 Autistic_boyya - 1:21.0.4.7-3 - delete rh1648249-add_commented_out_nss_cfg_provider_to_java_security.patch -* Mon Jul 29 2024 zhaosaisai - 1:21.0.4.7-2 +* Fri Aug 2 2024 zhaosaisai - 1:21.0.4.7-2 - change boot jdk to itself * Thu Aug 1 2024 aoqi - 1:21.0.4.7-1