diff --git a/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java b/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java
index 19a193a924a..46eeb67de54 100644
--- a/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java
+++ b/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java
@@ -28,7 +28,6 @@ package java.lang.invoke;
 import jdk.internal.org.objectweb.asm.ClassWriter;
 import jdk.internal.org.objectweb.asm.Opcodes;
 import sun.invoke.util.Wrapper;
-import sun.util.logging.PlatformLogger;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -73,6 +72,7 @@ class GenerateJLIClassesHelper {
 
         private final TreeSet<String> speciesTypes = new TreeSet<>();
         private final TreeSet<String> invokerTypes = new TreeSet<>();
+        private final TreeSet<String> linkerTypes = new TreeSet<>();
         private final TreeSet<String> callSiteTypes = new TreeSet<>();
         private final Map<String, Set<String>> dmhMethods = new TreeMap<>();
 
@@ -87,6 +87,12 @@ class GenerateJLIClassesHelper {
             return this;
         }
 
+        HolderClassBuilder addLinkerType(String methodType) {
+            validateMethodType(methodType);
+            linkerTypes.add(methodType);
+            return this;
+        }
+
         HolderClassBuilder addCallSiteType(String csType) {
             validateMethodType(csType);
             callSiteTypes.add(csType);
@@ -130,19 +136,33 @@ class GenerateJLIClassesHelper {
                 }
             }
 
-            // The invoker type to ask for is retrieved by removing the first
+            // The linker type to ask for is retrieved by removing the first
             // and the last argument, which needs to be of Object.class
+            MethodType[] linkerMethodTypes = new MethodType[linkerTypes.size()];
+            index = 0;
+            for (String linkerType : linkerTypes) {
+                MethodType mt = asMethodType(linkerType);
+                final int lastParam = mt.parameterCount() - 1;
+                if (!checkLinkerTypeParams(mt)) {
+                    throw new RuntimeException(
+                            "Linker type parameter must start and end with Object: " + linkerType);
+                }
+                mt = mt.dropParameterTypes(lastParam, lastParam + 1);
+                linkerMethodTypes[index] = mt.dropParameterTypes(0, 1);
+                index++;
+            }
+
+            // The invoker type to ask for is retrieved by removing the first
+            // argument, which needs to be of Object.class
             MethodType[] invokerMethodTypes = new MethodType[invokerTypes.size()];
             index = 0;
             for (String invokerType : invokerTypes) {
                 MethodType mt = asMethodType(invokerType);
-                final int lastParam = mt.parameterCount() - 1;
                 if (!checkInvokerTypeParams(mt)) {
                     throw new RuntimeException(
-                            "Invoker type parameter must start and end with Object: " + invokerType);
+                            "Invoker type parameter must start with 2 Objects: " + invokerType);
                 }
-                mt = mt.dropParameterTypes(lastParam, lastParam + 1);
-                invokerMethodTypes[index] = mt.dropParameterTypes(0, 1);
+                invokerMethodTypes[index] = mt.dropParameterTypes(0, 2);
                 index++;
             }
 
@@ -171,7 +191,7 @@ class GenerateJLIClassesHelper {
                             DELEGATING_HOLDER, directMethodTypes));
             result.put(INVOKERS_HOLDER,
                        generateInvokersHolderClassBytes(INVOKERS_HOLDER,
-                            invokerMethodTypes, callSiteMethodTypes));
+                            linkerMethodTypes, invokerMethodTypes, callSiteMethodTypes));
             result.put(BASIC_FORMS_HOLDER,
                        generateBasicFormsClassBytes(BASIC_FORMS_HOLDER));
 
@@ -207,6 +227,12 @@ class GenerateJLIClassesHelper {
         }
 
         public static boolean checkInvokerTypeParams(MethodType mt) {
+            return (mt.parameterCount() >= 2 &&
+                    mt.parameterType(0) == Object.class &&
+                    mt.parameterType(1) == Object.class);
+        }
+
+        public static boolean checkLinkerTypeParams(MethodType mt) {
             final int lastParam = mt.parameterCount() - 1;
             return (mt.parameterCount() >= 2 &&
                     mt.parameterType(0) == Object.class &&
@@ -320,15 +346,11 @@ class GenerateJLIClassesHelper {
                                 if ("linkToTargetMethod".equals(parts[2]) ||
                                         "linkToCallSite".equals(parts[2])) {
                                     builder.addCallSiteType(methodType);
+                                } else if (parts[2].endsWith("nvoker")) {
+                                    // MH.exactInvoker exactInvoker MH.invoker invoker
+                                    builder.addInvokerType(methodType);
                                 } else {
-                                    MethodType mt = HolderClassBuilder.asMethodType(methodType);
-                                    // Work around JDK-8327499
-                                    if (HolderClassBuilder.checkInvokerTypeParams(mt)) {
-                                        builder.addInvokerType(methodType);
-                                    } else {
-                                        PlatformLogger.getLogger("java.lang.invoke")
-                                                .warning("Invalid LF_RESOLVE " + parts[1] + " " + parts[2] + " " + parts[3]);
-                                    }
+                                    builder.addLinkerType(methodType);
                                 }
                             } else if (parts[1].contains("DirectMethodHandle")) {
                                 String dmh = parts[2];
@@ -465,27 +487,27 @@ class GenerateJLIClassesHelper {
 
     /**
      * Returns a {@code byte[]} representation of a class implementing
-     * the invoker forms for the set of supplied {@code invokerMethodTypes}
-     * and {@code callSiteMethodTypes}.
+     * the invoker forms for the set of supplied {@code linkerMethodTypes}
+     * {@code invokerMethodTypes}, and {@code callSiteMethodTypes}.
      */
     static byte[] generateInvokersHolderClassBytes(String className,
-            MethodType[] invokerMethodTypes, MethodType[] callSiteMethodTypes) {
+            MethodType[] linkerMethodTypes, MethodType[] invokerMethodTypes,
+            MethodType[] callSiteMethodTypes) {
 
         HashSet<MethodType> dedupSet = new HashSet<>();
         ArrayList<LambdaForm> forms = new ArrayList<>();
         ArrayList<String> names = new ArrayList<>();
-        int[] types = {
-            MethodTypeForm.LF_EX_LINKER,
+
+        int[] invokerTypes = {
             MethodTypeForm.LF_EX_INVOKER,
-            MethodTypeForm.LF_GEN_LINKER,
-            MethodTypeForm.LF_GEN_INVOKER
+            MethodTypeForm.LF_GEN_INVOKER,
         };
 
-        for (int i = 0; i < invokerMethodTypes.length; i++) {
+        for (MethodType methodType : invokerMethodTypes) {
             // generate methods representing invokers of the specified type
-            if (dedupSet.add(invokerMethodTypes[i])) {
-                for (int type : types) {
-                    LambdaForm invokerForm = Invokers.invokeHandleForm(invokerMethodTypes[i],
+            if (dedupSet.add(methodType)) {
+                for (int type : invokerTypes) {
+                    LambdaForm invokerForm = Invokers.invokeHandleForm(methodType,
                             /*customized*/false, type);
                     forms.add(invokerForm);
                     names.add(invokerForm.kind.defaultLambdaName);
@@ -493,6 +515,24 @@ class GenerateJLIClassesHelper {
             }
         }
 
+        int[] linkerTypes = {
+                MethodTypeForm.LF_EX_LINKER,
+                MethodTypeForm.LF_GEN_LINKER,
+        };
+
+        dedupSet = new HashSet<>();
+        for (MethodType methodType : linkerMethodTypes) {
+            // generate methods representing linkers of the specified type
+            if (dedupSet.add(methodType)) {
+                for (int type : linkerTypes) {
+                    LambdaForm linkerForm = Invokers.invokeHandleForm(methodType,
+                            /*customized*/false, type);
+                    forms.add(linkerForm);
+                    names.add(linkerForm.kind.defaultLambdaName);
+                }
+            }
+        }
+
         dedupSet = new HashSet<>();
         for (int i = 0; i < callSiteMethodTypes.length; i++) {
             // generate methods representing invokers of the specified type
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/CDSLambdaInvoker.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/CDSLambdaInvoker.java
index f3485ee26a6..5d78d2bdd78 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/CDSLambdaInvoker.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/CDSLambdaInvoker.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 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
@@ -26,6 +26,9 @@ import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 
+/**
+ * This is launched from TestLambdaInvokers.
+ */
 public class CDSLambdaInvoker {
     public static void main(String args[]) throws Throwable {
         // The following calls trigger the generation of new Species classes
@@ -44,6 +47,10 @@ public class CDSLambdaInvoker {
                                               boolean.class, Object.class, long.class, double.class);
         MethodHandle mh = lookup.findStatic(CDSLambdaInvoker.class, "callme", mt);
         mh.invokeExact(4.0f, 5.0, 6, true, (Object)args, 7L, 8.0);
+
+        mh = MethodHandles.dropArguments(MethodHandles.zero(Object.class), 0, Object.class, int.class);
+        MethodHandle inv = MethodHandles.invoker(mh.type());
+        invoke(inv, mh, args, 3);
     }
 
     private static Object invoke(MethodHandle mh, Object ... args) throws Throwable {
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/TestLambdaInvokers.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/TestLambdaInvokers.java
index 9a16f55e149..3769ab97b93 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/TestLambdaInvokers.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/TestLambdaInvokers.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 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
@@ -26,7 +26,7 @@
  * @test
  * @key randomness
  * @summary test archive lambda invoker species type in dynamic dump
- * @bug 8280767
+ * @bug 8280767 8327499
  * @requires vm.cds
  * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive
  * @compile CDSLambdaInvoker.java
@@ -59,6 +59,7 @@ public class TestLambdaInvokers extends DynamicArchiveTestBase {
              "-Xlog:cds",
              "-Xlog:cds+dynamic=debug",
              "-Xlog:class+load",
+             "-Djava.lang.invoke.MethodHandle.TRACE_RESOLVE=true",
              "-cp",
              jarFile,
              mainClass)
@@ -69,6 +70,10 @@ public class TestLambdaInvokers extends DynamicArchiveTestBase {
                  // java.lang.invoke.BoundMethodHandle$Species_JL is generated from CDSLambdaInvoker and
                  // stored in the dynamic archive
                  output.shouldContain("java.lang.invoke.BoundMethodHandle$Species_JL source: shared objects file (top)");
+
+                 // java.lang.invoke.Invokers$Holder has invoker(Object,Object,Object,int)Object available
+                 // from the archives
+                 output.shouldContain("[LF_RESOLVE] java.lang.invoke.Invokers$Holder invoker L3I_L (success)");
              });
     }
 
diff --git a/test/jdk/tools/jlink/plugins/GenerateJLIClassesPluginTest.java b/test/jdk/tools/jlink/plugins/GenerateJLIClassesPluginTest.java
index 275043ed8d8..5d06288fb59 100644
--- a/test/jdk/tools/jlink/plugins/GenerateJLIClassesPluginTest.java
+++ b/test/jdk/tools/jlink/plugins/GenerateJLIClassesPluginTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 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
@@ -22,6 +22,8 @@
  */
 
 import java.io.IOException;
+import java.lang.classfile.ClassFile;
+import java.lang.constant.MethodTypeDesc;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -29,6 +31,7 @@ import java.util.Collection;
 import java.util.List;
 import java.util.stream.Collectors;
 
+import org.testng.Assert;
 import tests.Helper;
 import tests.JImageGenerator;
 import tests.JImageValidator;
@@ -37,9 +40,12 @@ import tests.Result;
 import org.testng.annotations.BeforeTest;
 import org.testng.annotations.Test;
 
- /*
+import static java.lang.constant.ConstantDescs.CD_Object;
+import static java.lang.constant.ConstantDescs.CD_int;
+
+/*
  * @test
- * @bug 8252919
+ * @bug 8252919 8327499
  * @library ../../lib
  * @summary Test --generate-jli-classes plugin
  * @enablePreview
@@ -122,6 +128,47 @@ public class GenerateJLIClassesPluginTest {
         validateHolderClasses(image);
     }
 
+    @Test
+    public static void testInvokers() throws IOException {
+        var fileString = "[LF_RESOLVE] java.lang.invoke.Invokers$Holder invoker L3I_L (fail)";
+        Path invokersTrace = Files.createTempFile("invokers", "trace");
+        Files.writeString(invokersTrace, fileString, Charset.defaultCharset());
+        Result result = JImageGenerator.getJLinkTask()
+                .modulePath(helper.defaultModulePath())
+                .output(helper.createNewImageDir("jli-invokers"))
+                .option("--generate-jli-classes=@" + invokersTrace.toString())
+                .addMods("java.base")
+                .call();
+
+        var image = result.assertSuccess();
+        var targetMtd = MethodTypeDesc.of(CD_Object, CD_Object, CD_Object, CD_Object, CD_int);
+
+        validateHolderClasses(image);
+        JImageValidator.validate(image.resolve("lib").resolve("modules"),
+                List.of(), List.of(), bytes -> {
+                    var cf = ClassFile.of().parse(bytes);
+                    if (!cf.thisClass().name().equalsString("java/lang/invoke/Invokers$Holder")) {
+                        return;
+                    }
+
+                    boolean found = false;
+                    for (var m : cf.methods()) {
+                        // LambdaForm.Kind
+                        if (m.methodName().equalsString("invoker") && m.methodTypeSymbol().equals(targetMtd)) {
+                            found = true;
+                            break;
+                        }
+                    }
+                    if (!found) {
+                        var methodsInfo = cf.methods().stream()
+                                .map(m -> m.methodName() + m.methodTypeSymbol().displayDescriptor())
+                                .collect(Collectors.joining("\n"));
+
+                        Assert.fail("Missing invoker L3I_L in java.lang.invoke.Invokers$Holder, found:\n" + methodsInfo);
+                    }
+                });
+    }
+
     private static void validateHolderClasses(Path image) throws IOException {
         JImageValidator.validate(image.resolve("lib").resolve("modules"),
                 List.of("/java.base/java/lang/invoke/DirectMethodHandle$Holder.class",
diff --git a/test/jdk/tools/lib/tests/JImageValidator.java b/test/jdk/tools/lib/tests/JImageValidator.java
index 125afb9d24c..3d22a558dbd 100644
--- a/test/jdk/tools/lib/tests/JImageValidator.java
+++ b/test/jdk/tools/lib/tests/JImageValidator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -32,6 +32,7 @@ import java.util.List;
 import java.util.Properties;
 import java.lang.classfile.ClassFile;
 import java.lang.classfile.ClassHierarchyResolver;
+import java.util.function.Consumer;
 
 import jdk.internal.jimage.BasicImageReader;
 import jdk.internal.jimage.ImageLocation;
@@ -172,6 +173,11 @@ public class JImageValidator {
 
     public static void validate(Path jimage, List<String> expectedLocations,
             List<String> unexpectedPaths) throws IOException {
+        validate(jimage, expectedLocations, unexpectedPaths, _ -> {});
+    }
+
+    public static void validate(Path jimage, List<String> expectedLocations,
+            List<String> unexpectedPaths, Consumer<byte[]> classChecker) throws IOException {
         BasicImageReader reader = BasicImageReader.open(jimage);
         // Validate expected locations
         List<String> seenLocations = new ArrayList<>();
@@ -195,6 +201,7 @@ public class JImageValidator {
                         throw new IOException("NULL RESOURCE " + s);
                     }
                     readClass(r);
+                    classChecker.accept(r);
                 } catch (IOException ex) {
                     System.err.println(s + " ERROR " + ex);
                     throw ex;
@@ -222,7 +229,7 @@ public class JImageValidator {
         return moduleExecutionTime;
     }
 
-    public static void readClass(byte[] clazz) throws IOException{
+    public static void readClass(byte[] clazz) throws IOException {
         var errors = ClassFile.of(
                 //resolution of all classes as interfaces cancels assignability verification
                 ClassFile.ClassHierarchyResolverOption.of(cls -> ClassHierarchyResolver.ClassHierarchyInfo.ofInterface()))