Skip to content

Commit a1dbb6d

Browse files
committed
8352689: Allow for hash sum overrides when linking from the run-time image
1 parent e62becc commit a1dbb6d

File tree

7 files changed

+243
-17
lines changed

7 files changed

+243
-17
lines changed

src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JRTArchive.java

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,12 @@
4141
import java.util.ArrayList;
4242
import java.util.Arrays;
4343
import java.util.Collections;
44+
import java.util.HashSet;
4445
import java.util.HexFormat;
4546
import java.util.List;
4647
import java.util.Map;
4748
import java.util.Objects;
49+
import java.util.Set;
4850
import java.util.function.Function;
4951
import java.util.function.Predicate;
5052
import java.util.stream.Collectors;
@@ -74,6 +76,7 @@ public class JRTArchive implements Archive {
7476
// Maps a module resource path to the corresponding diff to packaged
7577
// modules for that resource (if any)
7678
private final Map<String, ResourceDiff> resDiff;
79+
private final Map<String, Set<String>> altHashSums;
7780
private final boolean errorOnModifiedFile;
7881
private final TaskHelper taskHelper;
7982

@@ -86,11 +89,15 @@ public class JRTArchive implements Archive {
8689
* install aborts the link.
8790
* @param perModDiff The lib/modules (a.k.a jimage) diff for this module,
8891
* possibly an empty list if there are no differences.
92+
* @param altHashSums A map of alternative hash sums for files in
93+
* a module, possibly empty.
94+
* @param taskHelper The task helper reference.
8995
*/
9096
JRTArchive(String module,
9197
Path path,
9298
boolean errorOnModifiedFile,
9399
List<ResourceDiff> perModDiff,
100+
Map<String, Set<String>> altHashSums,
94101
TaskHelper taskHelper) {
95102
this.module = module;
96103
this.path = path;
@@ -105,6 +112,7 @@ public class JRTArchive implements Archive {
105112
this.resDiff = Objects.requireNonNull(perModDiff).stream()
106113
.collect(Collectors.toMap(ResourceDiff::getName, Function.identity()));
107114
this.taskHelper = taskHelper;
115+
this.altHashSums = altHashSums;
108116
}
109117

110118
@Override
@@ -217,7 +225,21 @@ private void addNonClassResources() {
217225

218226
// Read from the base JDK image.
219227
Path path = BASE.resolve(m.resPath);
220-
if (shaSumMismatch(path, m.hashOrTarget, m.symlink)) {
228+
// Allow for additional hash sums so as to support
229+
// file modifications done after jlink has run at build
230+
// time. For example for Windows builds done with
231+
// --with-external-symbols-in-bundles=public or
232+
// distribution builds, where some post-processing happens
233+
// on produced binaries and libraries invalidating the
234+
// hash sum included in the jdk.jlink module for those
235+
// files at jlink-time
236+
Set<String> shaSums = new HashSet<>();
237+
shaSums.add(m.hashOrTarget);
238+
Set<String> extra = altHashSums.get(m.resPath);
239+
if (extra != null) {
240+
shaSums.addAll(extra);
241+
}
242+
if (shaSumMismatch(path, shaSums, m.symlink)) {
221243
if (errorOnModifiedFile) {
222244
String msg = taskHelper.getMessage("err.runtime.link.modified.file", path.toString());
223245
IOException cause = new IOException(msg);
@@ -239,14 +261,12 @@ private void addNonClassResources() {
239261
}
240262
}
241263

242-
static boolean shaSumMismatch(Path res, String expectedSha, boolean isSymlink) {
264+
static boolean shaSumMismatch(Path res, Set<String> expectedShas, boolean isSymlink) {
243265
if (isSymlink) {
244266
return false;
245267
}
246268
// handle non-symlink resources
247269
try {
248-
HexFormat format = HexFormat.of();
249-
byte[] expected = format.parseHex(expectedSha);
250270
MessageDigest digest = MessageDigest.getInstance("SHA-512");
251271
try (InputStream is = Files.newInputStream(res)) {
252272
byte[] buf = new byte[1024];
@@ -256,7 +276,9 @@ static boolean shaSumMismatch(Path res, String expectedSha, boolean isSymlink) {
256276
}
257277
}
258278
byte[] actual = digest.digest();
259-
return !MessageDigest.isEqual(expected, actual);
279+
// Convert actual to string
280+
String strActual = HexFormat.of().formatHex(actual);
281+
return !expectedShas.contains(strActual);
260282
} catch (Exception e) {
261283
throw new AssertionError("SHA-512 sum check failed!", e);
262284
}

src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Jlink.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ public static final class JlinkConfiguration {
148148
private final Set<String> modules;
149149
private final ModuleFinder finder;
150150
private final boolean linkFromRuntimeImage;
151-
private final boolean ignoreModifiedRuntime;
151+
private final LinkableRuntimeImage.Config runtimeImageConfig;
152152
private final boolean generateRuntimeImage;
153153

154154
/**
@@ -162,13 +162,13 @@ public JlinkConfiguration(Path output,
162162
Set<String> modules,
163163
ModuleFinder finder,
164164
boolean linkFromRuntimeImage,
165-
boolean ignoreModifiedRuntime,
165+
LinkableRuntimeImage.Config runtimeImageConfig,
166166
boolean generateRuntimeImage) {
167167
this.output = output;
168168
this.modules = Objects.requireNonNull(modules);
169169
this.finder = finder;
170170
this.linkFromRuntimeImage = linkFromRuntimeImage;
171-
this.ignoreModifiedRuntime = ignoreModifiedRuntime;
171+
this.runtimeImageConfig = runtimeImageConfig;
172172
this.generateRuntimeImage = generateRuntimeImage;
173173
}
174174

@@ -198,8 +198,8 @@ public boolean linkFromRuntimeImage() {
198198
return linkFromRuntimeImage;
199199
}
200200

201-
public boolean ignoreModifiedRuntime() {
202-
return ignoreModifiedRuntime;
201+
public LinkableRuntimeImage.Config runtimeImageConfig() {
202+
return runtimeImageConfig;
203203
}
204204

205205
public boolean isGenerateRuntimeImage() {

src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,35 @@ public class JlinkTask {
191191
// be used for linking from the run-time image.
192192
new Option<JlinkTask>(false, (task, opt, arg) -> {
193193
task.options.generateLinkableRuntime = true;
194-
}, true, "--generate-linkable-runtime")
194+
}, true, "--generate-linkable-runtime"),
195+
new Option<JlinkTask>(true, (task, opt, arg) -> {
196+
if (arg.startsWith("@")) {
197+
// Read overrides from file
198+
if (!task.options.shaOverrides.isEmpty()) {
199+
// Only allow a single @file value
200+
throw taskHelper.newBadArgs("err.sha.overrides.multiple");
201+
}
202+
Path file = Paths.get(arg.substring(1));
203+
// Ignore non-existing sha overrides file.
204+
//
205+
// This is to allow for custom builds needing to optionally
206+
// support run-time image links on (possibly) modified
207+
// binaries/debuginfo files done after the JDK build
208+
if (Files.exists(file)) {
209+
try {
210+
Files.readAllLines(file).stream()
211+
.forEach(task.options.shaOverrides::add);
212+
} catch (IOException e) {
213+
throw taskHelper.newBadArgs("err.sha.overrides.freaderr", file.toString());
214+
}
215+
}
216+
} else {
217+
// Allow multiple values, separated by comma in addition to
218+
// multiple times the same option.
219+
Arrays.asList(arg.split(",")).stream()
220+
.forEach(task.options.shaOverrides::add);
221+
}
222+
}, true, "--sha-overrides"),
195223
};
196224

197225

@@ -235,6 +263,7 @@ static class OptionsValues {
235263
boolean suggestProviders = false;
236264
boolean ignoreModifiedRuntime = false;
237265
boolean generateLinkableRuntime = false;
266+
final Set<String> shaOverrides = new HashSet<>();
238267
}
239268

240269
public static final String OPTIONS_RESOURCE = "jdk/tools/jlink/internal/options";
@@ -459,14 +488,41 @@ private JlinkConfiguration initJlinkConfig() throws BadArgs {
459488
throw taskHelper.newBadArgs("err.runtime.link.packaged.mods");
460489
}
461490

491+
LinkableRuntimeImage.Config linkableRuntimeConfig = new LinkableRuntimeImage.Config(
492+
options.ignoreModifiedRuntime,
493+
isLinkFromRuntime ? buildShaSumMap(taskHelper, options.shaOverrides) : null);
462494
return new JlinkConfiguration(options.output,
463495
roots,
464496
finder,
465497
isLinkFromRuntime,
466-
options.ignoreModifiedRuntime,
498+
linkableRuntimeConfig,
467499
options.generateLinkableRuntime);
468500
}
469501

502+
private Map<String, Map<String, Set<String>>> buildShaSumMap(TaskHelper taskHelper,
503+
Set<String> shaOverrides) throws BadArgs {
504+
Map<String, Map<String, Set<String>>> moduleToFiles = new HashMap<>();
505+
for (String t: shaOverrides) {
506+
String trimmed = t.trim();
507+
if (trimmed.startsWith("#")) {
508+
// skip comment lines
509+
continue;
510+
}
511+
String[] tokens = trimmed.split("\\|");
512+
if (tokens.length != 3) {
513+
throw taskHelper.newBadArgs("err.sha.overrides.bad.format", t);
514+
}
515+
// t is a '|'-separated item of (in that order):
516+
// <module-name>
517+
// <file-path>
518+
// <SHA-512-sum>
519+
Map<String, Set<String>> perModuleMap = moduleToFiles.computeIfAbsent(tokens[0], k -> new HashMap<>());
520+
Set<String> shaSumsPerFile = perModuleMap.computeIfAbsent(tokens[1], k -> new HashSet<>());
521+
shaSumsPerFile.add(tokens[2]);
522+
}
523+
return moduleToFiles;
524+
}
525+
470526
/*
471527
* Creates a ModuleFinder for the given module paths.
472528
*/
@@ -788,7 +844,7 @@ private static Archive newArchive(String module,
788844
taskHelper.getMessage("err.not.a.module.directory", path));
789845
}
790846
} else if (config.linkFromRuntimeImage()) {
791-
return LinkableRuntimeImage.newArchive(module, path, config.ignoreModifiedRuntime(), taskHelper);
847+
return LinkableRuntimeImage.newArchive(module, path, config.runtimeImageConfig(), taskHelper);
792848
} else {
793849
throw new IllegalArgumentException(
794850
taskHelper.getMessage("err.not.modular.format", module, path));

src/jdk.jlink/share/classes/jdk/tools/jlink/internal/LinkableRuntimeImage.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import java.io.InputStream;
3030
import java.nio.file.Path;
3131
import java.util.List;
32+
import java.util.Map;
33+
import java.util.Set;
3234

3335
import jdk.tools.jlink.internal.runtimelink.ResourceDiff;
3436

@@ -67,7 +69,7 @@ private static InputStream getDiffInputStream(String module) throws IOException
6769

6870
public static Archive newArchive(String module,
6971
Path path,
70-
boolean ignoreModifiedRuntime,
72+
Config config,
7173
TaskHelper taskHelper) {
7274
assert isLinkableRuntime();
7375
// Here we retrieve the per module difference file, which is
@@ -81,8 +83,15 @@ public static Archive newArchive(String module,
8183
throw new AssertionError("Failure to retrieve resource diff for " +
8284
"module " + module, e);
8385
}
84-
return new JRTArchive(module, path, !ignoreModifiedRuntime, perModuleDiff, taskHelper);
86+
return new JRTArchive(module,
87+
path,
88+
!config.ignoreModifiedRuntime,
89+
perModuleDiff,
90+
// Empty map if no alternative sha sums
91+
config.altHashSums.computeIfAbsent(module, k -> Map.of()),
92+
taskHelper);
8593
}
8694

87-
95+
static record Config(boolean ignoreModifiedRuntime,
96+
Map<String, Map<String, Set<String>>> altHashSums) {}
8897
}

src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ err.runtime.link.packaged.mods=This JDK has no packaged modules.\
127127
err.runtime.link.modified.file={0} has been modified
128128
err.runtime.link.patched.module=jlink does not support linking from the run-time image\
129129
\ when running on a patched runtime with --patch-module
130+
131+
# Linking from the run-time image, SHA sum handling
132+
err.sha.overrides.multiple=option --sha-overrides does not allow @file and non-file combinations
133+
err.sha.overrides.freaderr=Error reading file ''{0}'' passed with option --sha-overrides
134+
err.sha.overrides.bad.format=Bad format in --sha-overrides. Token was {0}. Expected <module-name>|<file-path>|<sha-sum>
135+
130136
err.no.module.path=--module-path option must be specified with --add-modules ALL-MODULE-PATH
131137
err.empty.module.path=No module found in module path ''{0}'' with --add-modules ALL-MODULE-PATH
132138
err.limit.modules=--limit-modules not allowed with --add-modules ALL-MODULE-PATH

test/jdk/tools/jlink/IntegrationTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ private static void test() throws Exception {
159159
mods,
160160
JlinkTask.limitFinder(JlinkTask.newModuleFinder(modulePaths), limits, mods),
161161
linkFromRuntime,
162-
false /* ignore modified runtime */,
162+
null /* run-time image link config */,
163163
false /* generate run-time image */);
164164

165165
List<Plugin> lst = new ArrayList<>();

0 commit comments

Comments
 (0)