From c83272a928a59c5d592722cd126325d6ac61cbf7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Galder=20Zamarren=CC=83o?= <galder@zamarreno.com>
Date: Fri, 9 Jun 2023 10:54:32 +0200
Subject: [PATCH] Add option to produce PIE native binaries #33524

---
 .../quarkus/deployment/pkg/NativeConfig.java  |  9 +++++++
 .../pkg/steps/NativeImageBuildStep.java       | 24 ++++++++++++-------
 .../deployment/pkg/TestNativeConfig.java      |  5 ++++
 3 files changed, 30 insertions(+), 8 deletions(-)

diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/NativeConfig.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/NativeConfig.java
index e966f9a1aca3b..977cb104e0461 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/NativeConfig.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/NativeConfig.java
@@ -198,6 +198,15 @@ public interface NativeConfig {
      */
     Optional<Boolean> containerBuild();
 
+    /**
+     * Explicit configuration option to generate a native Position Independent Executable (PIE) for Linux.
+     * If the system supports PIE generation, the default behaviour is to disable it for
+     * <a href="https://www.redhat.com/en/blog/position-independent-executable-pie-performance">performance reasons</a>.
+     * However, some systems can only run position-independent executables,
+     * so this option enables the generation of such native executables.
+     */
+    Optional<Boolean> pie();
+
     /**
      * If this build is done using a remote docker daemon.
      */
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 25f2ee179e2da..9734fa89d4de3 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
@@ -194,11 +194,15 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, LocalesBuildTimeCon
         Path outputDir = nativeImageSourceJarBuildItem.getPath().getParent();
         final String runnerJarName = runnerJar.getFileName().toString();
 
-        String noPIE = "";
+        String pie = "";
 
         boolean isContainerBuild = nativeImageRunner.isContainerBuild();
         if (!isContainerBuild && SystemUtils.IS_OS_LINUX) {
-            noPIE = detectNoPIE();
+            if (nativeConfig.pie().isPresent() && nativeConfig.pie().get()) {
+                pie = detectPIE();
+            } else {
+                pie = detectNoPIE();
+            }
         }
 
         String nativeImageName = getNativeImageName(outputTargetBuildItem, packageConfig);
@@ -242,7 +246,7 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, LocalesBuildTimeCon
                     .setOutputDir(outputDir)
                     .setRunnerJarName(runnerJarName)
                     .setNativeImageName(nativeImageName)
-                    .setNoPIE(noPIE)
+                    .setPIE(pie)
                     .setGraalVMVersion(graalVMVersion)
                     .setNativeImageFeatures(nativeImageFeatures)
                     .setContainerBuild(isContainerBuild)
@@ -522,6 +526,10 @@ private static String detectNoPIE() {
         return argument.length() == 0 ? testGCCArgument("-nopie") : argument;
     }
 
+    private static String detectPIE() {
+        return testGCCArgument("-pie");
+    }
+
     private static String testGCCArgument(String argument) {
         try {
             Process gcc = new ProcessBuilder("cc", "-v", "-E", argument, "-").start();
@@ -562,7 +570,7 @@ static class Builder {
             private List<NativeImageFeatureBuildItem> nativeImageFeatures;
             private Path outputDir;
             private String runnerJarName;
-            private String noPIE = "";
+            private String pie = "";
             private GraalVM.Version graalVMVersion = null;
             private String nativeImageName;
             private boolean classpathIsBroken;
@@ -646,8 +654,8 @@ public Builder setRunnerJarName(String runnerJarName) {
                 return this;
             }
 
-            public Builder setNoPIE(String noPIE) {
-                this.noPIE = noPIE;
+            public Builder setPIE(String pie) {
+                this.pie = pie;
                 return this;
             }
 
@@ -857,8 +865,8 @@ public NativeImageInvokerInfo build() {
                 if (!inlineBeforeAnalysis) {
                     nativeImageArgs.add("-H:-InlineBeforeAnalysis");
                 }
-                if (!noPIE.isEmpty()) {
-                    nativeImageArgs.add("-H:NativeLinkerOption=" + noPIE);
+                if (!pie.isEmpty()) {
+                    nativeImageArgs.add("-H:NativeLinkerOption=" + pie);
                 }
 
                 if (!nativeConfig.enableIsolates()) {
diff --git a/core/deployment/src/test/java/io/quarkus/deployment/pkg/TestNativeConfig.java b/core/deployment/src/test/java/io/quarkus/deployment/pkg/TestNativeConfig.java
index e7890ec83c3f6..900acdcef123c 100644
--- a/core/deployment/src/test/java/io/quarkus/deployment/pkg/TestNativeConfig.java
+++ b/core/deployment/src/test/java/io/quarkus/deployment/pkg/TestNativeConfig.java
@@ -137,6 +137,11 @@ public Optional<Boolean> containerBuild() {
         return Optional.empty();
     }
 
+    @Override
+    public Optional<Boolean> pie() {
+        return Optional.empty();
+    }
+
     @Override
     public boolean remoteContainerBuild() {
         return false;