Repository: quarkusio/quarkus
Issue: Synchronize GraalVM upstream's and Quarkus'-only debug info stripping

### Describe the bug

GraalVM 23.0 will gain functionality to [strip debuginfo](https://github.com/oracle/graal/pull/5691). This is turned on by default when debuginfo generation is being turned on. As a result, quarkus applications trying to turn on debuginfo and then trying to use it in a debugger results in a CRC mismatch when loading them with gdb (for example):

```
warning: the debug information found in "/disk/graal/upstream-sources/mandrel-integration-tests/apps/quarkus-full-microprofile/target/quarkus-native-image-source-jar/quarkus-runner.debug" does not match "/disk/graal/upstream-sources/mandrel-integration-tests/apps/quarkus-full-microprofile/target/quarkus-native-image-source-jar/quarkus-runner" (CRC mismatch).
```

This is caused by two `.gnu_debuglink` sections trying to get created. The first one by GraalVM, the second by quarkus's native image build steps. The second attempt will fail and then the debuginfo won't match the CRC.

### Expected behavior

Either use upstream's feature or turn it off using `-H:-StripDebugInfo` and keep using quarkus's approach.

### Actual behavior

Debuginfo isn't working. See:
https://github.com/Karm/mandrel-integration-tests/issues/138

### How to Reproduce?

Build a quarkus up in native with a GraalVM 23.0.0 snapshot build with debuginfo. Try to debug the binary in gdb. Observe the CRC warning and debuginfo not usable for source-file-based break points.

### Output of `uname -a` or `ver`

_No response_

### Output of `java -version`

17.0.7+1

### GraalVM version (if different from Java)

23.0.0

### Quarkus version or git rev

main

### Build tool (ie. output of `mvnw --version` or `gradlew --version`)

_No response_

### Additional information

_No response_

Changed files:
core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java
core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRunner.java

diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRunner.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRunner.java
index c5acdbab3a9..a079197e631 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRunner.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRunner.java
@@ -47,7 +47,7 @@ public void addShutdownHook(Process buildNativeProcess) {
     }
 
     public Result build(List<String> args, String nativeImageName, String resultingExecutableName, Path outputDir,
-            boolean debugSymbolsEnabled, boolean processInheritIODisabled)
+            GraalVM.Version graalVMVersion, boolean debugSymbolsEnabled, boolean processInheritIODisabled)
             throws InterruptedException, IOException {
         preBuild(args);
         try {
@@ -66,21 +66,27 @@ public Result build(List<String> args, String nativeImageName, String resultingE
             int exitCode = process.waitFor();
             boolean objcopyExists = objcopyExists();
             if (exitCode != 0) {
-                return new Result(exitCode, objcopyExists);
+                return new Result(exitCode);
             }
 
-            if (objcopyExists) {
-                if (debugSymbolsEnabled) {
-                    splitDebugSymbols(nativeImageName, resultingExecutableName);
+            if (debugSymbolsEnabled && graalVMVersion.compareTo(GraalVM.Version.VERSION_23_0_0) < 0 && objcopyExists) {
+                // Need to explicitly split debug symbols prior to GraalVM/Mandrel 23.0
+                splitDebugSymbols(nativeImageName, resultingExecutableName);
+            }
+            if (!(debugSymbolsEnabled && graalVMVersion.compareTo(GraalVM.Version.VERSION_23_0_0) >= 0)) {
+                // Strip debug symbols even if not generated by GraalVM/Mandrel, because the underlying JDK might
+                // contain them. Note, however, that starting with GraalVM/Mandrel 23.0 this is done by default when
+                // generating debug info, so we don't want to do it twice and print twice a warning if objcopy is not
+                // available.
+                if (objcopyExists) {
+                    objcopy("--strip-debug", resultingExecutableName);
+                } else if (SystemUtils.IS_OS_LINUX) {
+                    log.warn(
+                            "objcopy executable not found in PATH. Debug symbols will therefore not be separated from the executable.");
+                    log.warn("That also means that resulting native executable is larger as it embeds the debug symbols.");
                 }
-                // Strip debug symbols regardless, because the underlying JDK might contain them
-                objcopy("--strip-debug", resultingExecutableName);
-            } else if (SystemUtils.IS_OS_LINUX) {
-                log.warn(
-                        "objcopy executable not found in PATH. Debug symbols will therefore not be separated from the executable.");
-                log.warn("That also means that resulting native executable is larger as it embeds the debug symbols.");
             }
-            return new Result(0, objcopyExists);
+            return new Result(0);
         } finally {
             postBuild();
         }
@@ -148,19 +154,14 @@ static void runCommand(String[] command, String errorMsg, File workingDirectory)
 
     static class Result {
         private final int exitCode;
-        private final boolean objcopyExists;
 
-        public Result(int exitCode, boolean objcopyExists) {
+        public Result(int exitCode) {
             this.exitCode = exitCode;
-            this.objcopyExists = objcopyExists;
         }
 
         public int getExitCode() {
             return exitCode;
         }
 
-        public boolean isObjcopyExists() {
-            return objcopyExists;
-        }
     }
 }
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java
index 8da849d81a4..f1f391783e1 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java
@@ -257,7 +257,7 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, LocalesBuildTimeCon
 
             NativeImageBuildRunner.Result buildNativeResult = buildRunner.build(nativeImageArgs, nativeImageName,
                     resultingExecutableName, outputDir,
-                    nativeConfig.debug.enabled,
+                    graalVMVersion, nativeConfig.debug.enabled,
                     processInheritIODisabled.isPresent() || processInheritIODisabledBuildItem.isPresent());
             if (buildNativeResult.getExitCode() != 0) {
                 throw imageGenerationFailed(buildNativeResult.getExitCode(), nativeConfig.isContainerBuild());
@@ -265,9 +265,9 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, LocalesBuildTimeCon
             IoUtils.copy(generatedExecutablePath, finalExecutablePath);
             Files.delete(generatedExecutablePath);
             if (nativeConfig.debug.enabled) {
-                if (buildNativeResult.isObjcopyExists()) {
-                    final String symbolsName = String.format("%s.debug", nativeImageName);
-                    Path generatedSymbols = outputDir.resolve(symbolsName);
+                final String symbolsName = String.format("%s.debug", nativeImageName);
+                Path generatedSymbols = outputDir.resolve(symbolsName);
+                if (generatedSymbols.toFile().exists()) {
                     Path finalSymbolsPath = outputTargetBuildItem.getOutputDirectory().resolve(symbolsName);
                     IoUtils.copy(generatedSymbols, finalSymbolsPath);
                     Files.delete(generatedSymbols);