Fix aggressive params file assumption

Most programs that accept params files use the `@file` syntax. For Apple
platform builds `@` can be the start of non-params file arguments as
well, such as `-rpath @executable_path/Frameworks`. There is a small
list of options where this is the case, so this new behavior no longer
assumes params files if args start with `@`, they also have to not start
with one of the 3 keywords used with this (from `man dyld` on macOS).
This should always hold since params files generated by bazel should
always start with `bazel-out`, if someone renames the symlinks to one of
the keywords, they're on their own.

Previously the workaround was to always make sure to pass the
`-Wl,-rpath,@executable_path` form of these arguments, but this makes
users not have to worry about this.

In a few other places we check this by checking if the file exists,
which is likely more accurate, but feels excessive and potentially
dangerous in this context.

Related: https://github.com/bazelbuild/bazel/pull/13148
Fixes: https://github.com/bazelbuild/bazel/issues/14316

Closes #14650.

PiperOrigin-RevId: 430195929
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLine.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLine.java
index 0403d2d..8ff77d4 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLine.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLine.java
@@ -270,13 +270,13 @@
         paramFile, linkTargetType, forcedToolPath, featureConfiguration, actionName, variables);
   }
 
-  public static void extractArgumentsForStaticLinkParamFile(
+  private static void extractArgumentsForStaticLinkParamFile(
       List<String> args, List<String> commandlineArgs, List<String> paramFileArgs) {
     commandlineArgs.add(args.get(0)); // ar command, must not be moved!
     int argsSize = args.size();
     for (int i = 1; i < argsSize; i++) {
       String arg = args.get(i);
-      if (arg.startsWith("@")) {
+      if (isLikelyParamFile(arg)) {
         commandlineArgs.add(arg); // params file, keep it in the command line
       } else {
         paramFileArgs.add(arg); // the rest goes to the params file
@@ -284,7 +284,7 @@
     }
   }
 
-  public static void extractArgumentsForDynamicLinkParamFile(
+  private static void extractArgumentsForDynamicLinkParamFile(
       List<String> args, List<String> commandlineArgs, List<String> paramFileArgs) {
     // Note, that it is not important that all linker arguments are extracted so that
     // they can be moved into a parameter file, but the vast majority should.
@@ -292,7 +292,7 @@
     int argsSize = args.size();
     for (int i = 1; i < argsSize; i++) {
       String arg = args.get(i);
-      if (arg.startsWith("@")) {
+      if (isLikelyParamFile(arg)) {
         commandlineArgs.add(arg); // params file, keep it in the command line
       } else {
         paramFileArgs.add(arg); // the rest goes to the params file
@@ -300,6 +300,13 @@
     }
   }
 
+  private static boolean isLikelyParamFile(String arg) {
+    return arg.startsWith("@")
+        && !arg.startsWith("@rpath")
+        && !arg.startsWith("@loader_path")
+        && !arg.startsWith("@executable_path");
+  }
+
   /**
    * Returns a raw link command for the given link invocation, including both command and arguments
    * (argv). The version that uses the expander is preferred, but that one can't be used during