Pass -object_path_lto <path> linker flag for LTO builds (#420) (#428)

The following commit (in 1.23.0) was reverted recently (in 1.23.1):
* commit 61b44d12df0834ae26d6e089271e14c6c9a239c8
* revert 4c51f10063687af77eabbbdc035124d798ca3a3f

This patch is a new attempt at getting that feature merged, but without
the LTO related issues that came up in the first patch. The difference
is that we're adding that feature unconditionally now, as it won't hurt
linking actions when LTO is disabled anyway. And enabling the bazel lto
features changes the linking workflow in a way that isn't necessary on
Apple platforms.

Initial commit log below.
---

This is needed for LTO builds, this is the path to which linker writes
one big object file after performing link time optimisations, and then
this object file would be used during final linking to create the App
binary, without this file binary would contain invalid debug symbols,
more info - https://github.com/bazelbuild/rules_swift/issues/1529 .

Closes - https://github.com/bazelbuild/rules_swift/issues/1529

Co-authored-by: Sanju Naik <66404008+sanju-naik@users.noreply.github.com>
diff --git a/crosstool/cc_toolchain_config.bzl b/crosstool/cc_toolchain_config.bzl
index ef21444..cf8df80 100644
--- a/crosstool/cc_toolchain_config.bzl
+++ b/crosstool/cc_toolchain_config.bzl
@@ -1023,6 +1023,38 @@
         ],
     )
 
+    # We need to pass -object_path_lto in order to get debug symbols when building with LTO.
+    # In a perfect world, we would only pass it when the thin_lto or full_lto features are enabled.
+    # However, when these features are enabled, bazel will follow the linking steps expected by
+    # the traditional llvm tools, which doesn't work on Apple platforms.
+    # On MacOS, passing -flto (or -flto=thin) to the compiler is mostly enough for the linker to
+    # do the right thing. The only thing left is to tell ld64 to keep the intermediate .o file,
+    # so dsymutil can find it.
+    # We're doing that here by passing -object_path_lto to every linking action. If LTO is disabled,
+    # it will be a no-op for the linker. But if it is enabled, it will allow dsymutil to find the
+    # symbols and put it in the .dSYM folder. Luckily for us, both ld64 and dsymutil run in the same
+    # action, so the fact that it's not declared as an output is not a problem.
+    lto_object_path_feature = feature(
+        name = "lto_object_path",
+        enabled = True,
+        flag_sets = [
+            flag_set(
+                actions = _DYNAMIC_LINK_ACTIONS,
+                flag_groups = [
+                    flag_group(
+                        flags = [
+                            "-Xlinker",
+                            "-object_path_lto",
+                            "-Xlinker",
+                            "%{output_execpath}.lto.o",
+                        ],
+                        expand_if_available = "output_execpath",
+                    ),
+                ],
+            ),
+        ],
+    )
+
     no_deduplicate_feature = feature(
         name = "no_deduplicate",
         enabled = True,
@@ -2662,6 +2694,7 @@
         fdo_optimize_feature,
         autofdo_feature,
         lipo_feature,
+        lto_object_path_feature,
         llvm_coverage_map_format_feature,
         gcc_coverage_map_format_feature,
         coverage_prefix_map_feature,