diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/CommandLine.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/CommandLine.java
new file mode 100644
index 00000000000..f504d7a5739
--- /dev/null
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/CommandLine.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  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 jdk.tools.jlink.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.List;
+
+/**
+ * This file was originally a copy of CommandLine.java in
+ * com.sun.tools.javac.main.
+ * It should track changes made to that file.
+ */
+
+/**
+ * Various utility methods for processing Java tool command line arguments.
+ */
+public class CommandLine {
+
+    static void loadCmdFile(InputStream in, List<String> args)
+            throws IOException {
+        try (Reader r = new InputStreamReader(in)) {
+            Tokenizer t = new Tokenizer(r);
+            String s;
+            while ((s = t.nextToken()) != null) {
+                args.add(s);
+            }
+        }
+    }
+    public static class Tokenizer {
+        private final Reader in;
+        private int ch;
+
+        public Tokenizer(Reader in) throws IOException {
+            this.in = in;
+            ch = in.read();
+        }
+
+        public String nextToken() throws IOException {
+            skipWhite();
+            if (ch == -1) {
+                return null;
+            }
+
+            StringBuilder sb = new StringBuilder();
+            char quoteChar = 0;
+
+            while (ch != -1) {
+                switch (ch) {
+                    case ' ':
+                    case '\t':
+                    case '\f':
+                        if (quoteChar == 0) {
+                            return sb.toString();
+                        }
+                        sb.append((char) ch);
+                        break;
+
+                    case '\n':
+                    case '\r':
+                        return sb.toString();
+
+                    case '\'':
+                    case '"':
+                        if (quoteChar == 0) {
+                            quoteChar = (char) ch;
+                        } else if (quoteChar == ch) {
+                            quoteChar = 0;
+                        } else {
+                            sb.append((char) ch);
+                        }
+                        break;
+
+                    case '\\':
+                        if (quoteChar != 0) {
+                            ch = in.read();
+                            switch (ch) {
+                                case '\n':
+                                case '\r':
+                                    while (ch == ' ' || ch == '\n'
+                                            || ch == '\r' || ch == '\t'
+                                            || ch == '\f') {
+                                        ch = in.read();
+                                    }
+                                    continue;
+
+                                case 'n':
+                                    ch = '\n';
+                                    break;
+                                case 'r':
+                                    ch = '\r';
+                                    break;
+                                case 't':
+                                    ch = '\t';
+                                    break;
+                                case 'f':
+                                    ch = '\f';
+                                    break;
+                                default:
+                                    break;
+                            }
+                        }
+                        sb.append((char) ch);
+                        break;
+
+                    default:
+                        sb.append((char) ch);
+                }
+
+                ch = in.read();
+            }
+
+            return sb.toString();
+        }
+
+        void skipWhite() throws IOException {
+            while (ch != -1) {
+                switch (ch) {
+                    case ' ':
+                    case '\t':
+                    case '\n':
+                    case '\r':
+                    case '\f':
+                        break;
+
+                    case '#':
+                        ch = in.read();
+                        while (ch != '\n' && ch != '\r' && ch != -1) {
+                            ch = in.read();
+                        }
+                        break;
+
+                    default:
+                        return;
+                }
+
+                ch = in.read();
+            }
+        }
+    }
+}
diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java
index 0a79420f0e2..3484332cdd3 100644
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java
@@ -27,6 +27,7 @@ package jdk.tools.jlink.internal;
 import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.PrintWriter;
 import java.io.UncheckedIOException;
 import java.lang.module.Configuration;
@@ -223,6 +224,8 @@ public class JlinkTask {
         boolean suggestProviders = false;
     }
 
+    public static final String OPTIONS_RESOURCE = "jdk/tools/jlink/internal/options";
+
     int run(String[] args) {
         if (log == null) {
             setLog(new PrintWriter(System.out, true),
@@ -230,6 +233,18 @@ public class JlinkTask {
         }
         Path outputPath = null;
         try {
+            Module m = JlinkTask.class.getModule();
+            try (InputStream savedOptions = m.getResourceAsStream(OPTIONS_RESOURCE)) {
+                if (savedOptions != null) {
+                    List<String> prependArgs = new ArrayList<>();
+                    CommandLine.loadCmdFile(savedOptions, prependArgs);
+                    if (!prependArgs.isEmpty()) {
+                        prependArgs.addAll(Arrays.asList(args));
+                        args = prependArgs.toArray(new String[prependArgs.size()]);
+                    }
+                }
+            }
+
             List<String> remaining = optionsHelper.handleOptions(this, args);
             if (remaining.size() > 0 && !options.suggestProviders) {
                 throw taskHelper.newBadArgs("err.orphan.arguments",
diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SaveJlinkArgfilesPlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SaveJlinkArgfilesPlugin.java
new file mode 100644
index 00000000000..3cfc9578965
--- /dev/null
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SaveJlinkArgfilesPlugin.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  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 jdk.tools.jlink.internal.plugins;
+
+import static jdk.tools.jlink.internal.JlinkTask.OPTIONS_RESOURCE;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import jdk.tools.jlink.plugin.PluginException;
+import jdk.tools.jlink.plugin.ResourcePool;
+import jdk.tools.jlink.plugin.ResourcePoolBuilder;
+import jdk.tools.jlink.plugin.ResourcePoolEntry;
+
+/**
+ * Saves the arguments in the specified argument files to a resource that's read
+ * by jlink in the output image. The saved arguments are prepended to the arguments
+ * specified on the jlink command line.
+ */
+public final class SaveJlinkArgfilesPlugin extends AbstractPlugin {
+
+    public SaveJlinkArgfilesPlugin() {
+        super("save-jlink-argfiles");
+    }
+
+    private List<String> argfiles = new ArrayList<>();
+
+    @Override
+    public Category getType() {
+        return Category.ADDER;
+    }
+
+    @Override
+    public boolean hasArguments() {
+        return true;
+    }
+
+    @Override
+    public boolean hasRawArgument() {
+        return true;
+    }
+
+    @Override
+    public void configure(Map<String, String> config) {
+        var v = config.get(getName());
+
+        if (v == null)
+            throw new AssertionError();
+
+        for (String argfile : v.split(File.pathSeparator)) {
+            argfiles.add(readArgfile(argfile));
+        }
+    }
+
+    private static String readArgfile(String argfile) {
+        try {
+            return Files.readString(Path.of(argfile));
+        } catch (IOException e) {
+            throw new PluginException("Argfile " + argfile + " is not readable");
+        }
+    }
+
+    @Override
+    public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
+        in.transformAndCopy(Function.identity(), out);
+        if (!in.moduleView().findModule("jdk.jlink").isPresent()) {
+            throw new PluginException("--save-jlink-argfiles requires jdk.jlink to be in the output image");
+        }
+        byte[] savedOptions = argfiles.stream()
+                                      .collect(Collectors.joining("\n"))
+                                      .getBytes(StandardCharsets.UTF_8);
+        out.add(ResourcePoolEntry.create("/jdk.jlink/" + OPTIONS_RESOURCE,
+                                         savedOptions));
+        return out.build();
+    }
+}
diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties
index 81a88365e36..a64db65ce17 100644
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties
@@ -187,6 +187,20 @@ order-resources.usage=\
 \                            e.g.: **/module-info.class,@classlist,\n\
 \                            /java.base/java/lang/**
 
+save-jlink-argfiles.argument=<filenames>
+
+save-jlink-argfiles.description=\
+Save the specified argument files that contain the arguments \n\
+to be prepended to the execution of jlink in the output image. \n\
+<filenames> is a ':' (';' on Windows) separated list of command-line argument files.
+
+save-jlink-argfiles.usage=\
+\  --save-jlink-argfiles <filenames>\n\
+\                            Save the specified argument files that contain \n\
+\                            the arguments to be prepended to the execution of \n\
+\                            jlink in the output image. <filenames> is a \n\
+\                            ':' (';' on Windows) separated list of command-line argument files.
+
 strip-debug.description=\
 Strip debug information from the output image
 
diff --git a/src/jdk.jlink/share/classes/module-info.java b/src/jdk.jlink/share/classes/module-info.java
index 8b7c0850966..3f3f10e2b06 100644
--- a/src/jdk.jlink/share/classes/module-info.java
+++ b/src/jdk.jlink/share/classes/module-info.java
@@ -80,6 +80,7 @@ module jdk.jlink {
         jdk.tools.jlink.internal.plugins.VendorBugURLPlugin,
         jdk.tools.jlink.internal.plugins.VendorVMBugURLPlugin,
         jdk.tools.jlink.internal.plugins.VendorVersionPlugin,
-        jdk.tools.jlink.internal.plugins.CDSPlugin;
+        jdk.tools.jlink.internal.plugins.CDSPlugin,
+        jdk.tools.jlink.internal.plugins.SaveJlinkArgfilesPlugin;
 
 }
diff --git a/test/jdk/tools/jlink/plugins/SaveJlinkArgfilesPluginTest.java b/test/jdk/tools/jlink/plugins/SaveJlinkArgfilesPluginTest.java
new file mode 100644
index 00000000000..16ad889db0d
--- /dev/null
+++ b/test/jdk/tools/jlink/plugins/SaveJlinkArgfilesPluginTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary Test --save-jlink-argfiles plugin
+ * @library ../../lib
+ * @library /test/lib
+ * @modules java.base/jdk.internal.jimage
+ *          jdk.jdeps/com.sun.tools.classfile
+ *          jdk.jlink/jdk.tools.jlink.internal
+ *          jdk.jlink/jdk.tools.jmod
+ *          jdk.jlink/jdk.tools.jimage
+ *          jdk.compiler
+ * @build tests.*
+ * @run main SaveJlinkArgfilesPluginTest
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import jdk.test.lib.process.ProcessTools;
+
+import tests.Helper;
+
+public class SaveJlinkArgfilesPluginTest {
+
+    public static void main(String[] args) throws Throwable {
+
+        Helper helper = Helper.newHelper();
+        if (helper == null) {
+            System.err.println("Test not run");
+            return;
+        }
+
+        String exe = System.getProperty("os.name").startsWith("Windows") ? ".exe" : "";
+        Path argfile1 = Path.of("argfile1");
+        Path argfile2 = Path.of("argfile2");
+
+        Files.writeString(argfile1, "--add-modules jdk.internal.vm.ci --add-options=-Dfoo=xyzzy");
+        Files.writeString(argfile2, "--vendor-version=\"XyzzyVM 3.14.15\" --vendor-bug-url=https://bugs.xyzzy.com/");
+
+        var module = "base";
+        helper.generateDefaultJModule(module);
+        var image = helper.generateDefaultImage(new String[] {
+                "--add-modules", "jdk.jlink,jdk.jdeps,jdk.internal.opt,jdk.compiler,java.compiler,jdk.zipfs,jdk.internal.vm.ci",
+                "--keep-packaged-modules", "images/base.image/jmods",
+                "--save-jlink-argfiles", argfile1 + File.pathSeparator + argfile2
+            }, module)
+            .assertSuccess();
+        helper.checkImage(image, module, null, null);
+
+        String launcher = image.resolve("bin/java" + exe).toString();
+        var oa = ProcessTools.executeProcess(launcher, "-XshowSettings:properties", "--version");
+
+        // Check that the primary image creation ignored the saved args
+        oa.shouldHaveExitValue(0);
+        oa.shouldNotMatch("java.vendor.url.bug = https://bugs.xyzzy.com/");
+        oa.shouldNotMatch("java.vendor.version = XyzzyVM 3.14.15");
+        oa.shouldNotMatch("foo = xyzzy");
+
+        // Check that --save-jlink-argfiles fails if jdk.jlink not in the output image
+        launcher = image.resolve("bin/jlink" + exe).toString();
+        oa = ProcessTools.executeProcess(launcher, "--output=ignore", "--save-jlink-argfiles=" + argfile1);
+        oa.shouldHaveExitValue(1);
+        oa.stdoutShouldMatch("--save-jlink-argfiles requires jdk.jlink to be in the output image");
+
+        // Create a secondary image
+        Path image2 = Path.of("image2").toAbsolutePath();
+        launcher = image.resolve("bin/jlink" + exe).toString();
+        oa = ProcessTools.executeProcess(launcher, "--output", image2.toString(), "--add-modules=java.base");
+        oa.shouldHaveExitValue(0);
+
+        // Ensure the saved `--add-options` and `--vendor-*` options
+        // were applied when creating the secondary image.
+        launcher = image2.resolve(Path.of("bin", "java" + exe)).toString();
+        oa = ProcessTools.executeProcess(launcher, "-XshowSettings:properties", "--version");
+        oa.shouldHaveExitValue(0);
+        oa.stdoutShouldMatch(" XyzzyVM 3.14.15 ");
+        oa.stderrShouldMatch("java.vendor.url.bug = https://bugs.xyzzy.com/");
+        oa.stderrShouldMatch("java.vendor.version = XyzzyVM 3.14.15");
+        oa.stderrShouldMatch("foo = xyzzy");
+
+        // Ensure the saved `--add-modules` option
+        // was applied when creating the secondary image.
+        oa = ProcessTools.executeProcess(launcher.toString(), "-d", "jdk.internal.vm.ci");
+        oa.shouldHaveExitValue(0);
+    }
+}