Skip to content

[Clang][Driver] Expose relocation model as multilib flags #149132

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 18, 2025

Conversation

statham-arm
Copy link
Collaborator

If a multilib collection contains libraries built for different methods of accessing global data (via absolute address, or via a GOT in -fPIC style, or as an offset from a fixed register in Arm -frwpi style), then multilib.yaml will need to know which relocation model an application is using in order to select the right library.

Even if a multilib collection only supports one relocation model, it's still useful for multilib.yaml to be able to tell if the user has selected the right one, so as to give a useful error message if they haven't, instead of silently selecting a library that won't work.

In this commit we determine the PIC / ROPI / RWPI status using the existing logic in ParsePICArgs, and translate it back into a canonical set of multilib selection flags.

If a multilib collection contains libraries built for different
methods of accessing global data (via absolute address, or via a GOT
in -fPIC style, or as an offset from a fixed register in Arm -frwpi
style), then `multilib.yaml` will need to know which relocation model
an application is using in order to select the right library.

Even if a multilib collection only supports one relocation model, it's
still useful for `multilib.yaml` to be able to tell if the user has
selected the right one, so as to give a useful error message if they
haven't, instead of silently selecting a library that won't work.

In this commit we determine the PIC / ROPI / RWPI status using the
existing logic in `ParsePICArgs`, and translate it back into a
canonical set of multilib selection flags.
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' labels Jul 16, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 16, 2025

@llvm/pr-subscribers-clang

Author: Simon Tatham (statham-arm)

Changes

If a multilib collection contains libraries built for different methods of accessing global data (via absolute address, or via a GOT in -fPIC style, or as an offset from a fixed register in Arm -frwpi style), then multilib.yaml will need to know which relocation model an application is using in order to select the right library.

Even if a multilib collection only supports one relocation model, it's still useful for multilib.yaml to be able to tell if the user has selected the right one, so as to give a useful error message if they haven't, instead of silently selecting a library that won't work.

In this commit we determine the PIC / ROPI / RWPI status using the existing logic in ParsePICArgs, and translate it back into a canonical set of multilib selection flags.


Full diff: https://github.com/llvm/llvm-project/pull/149132.diff

2 Files Affected:

  • (modified) clang/lib/Driver/ToolChain.cpp (+37)
  • (modified) clang/test/Driver/print-multi-selection-flags.c (+19)
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 3f9b808b2722e..5738c0aa9b1d2 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -343,6 +343,7 @@ ToolChain::getMultilibFlags(const llvm::opt::ArgList &Args) const {
   std::vector<std::string> Result;
   const llvm::Triple Triple(ComputeEffectiveClangTriple(Args));
   Result.push_back("--target=" + Triple.str());
+  bool IsARM = false;
 
   switch (Triple.getArch()) {
   case llvm::Triple::aarch64:
@@ -355,6 +356,7 @@ ToolChain::getMultilibFlags(const llvm::opt::ArgList &Args) const {
   case llvm::Triple::thumb:
   case llvm::Triple::thumbeb:
     getARMMultilibFlags(D, Triple, Args, Result);
+    IsARM = true; // for ROPI/RWPI below
     break;
   case llvm::Triple::riscv32:
   case llvm::Triple::riscv64:
@@ -376,6 +378,41 @@ ToolChain::getMultilibFlags(const llvm::opt::ArgList &Args) const {
   else
     Result.push_back("-fexceptions");
 
+  // A difference of relocation model (absolutely addressed data, PIC, Arm
+  // ROPI/RWPI) is likely to change whether a particular multilib variant is
+  // compatible with a given link. Determine the relocation model of the
+  // current link, and add appropriate 
+  {
+    RegisterEffectiveTriple TripleRAII(
+        *this, llvm::Triple(ComputeEffectiveClangTriple(Args)));
+
+    auto [RelocationModel, PICLevel, IsPIE] = tools::ParsePICArgs(*this, Args);
+
+    // ROPI and RWPI are only meaningful on Arm, so for other architectures, we
+    // expect never to find out they're enabled. But it seems confusing to add
+    // -fno-ropi and -fno-rwpi unconditionally to every other architecture's
+    // multilib flags, so instead we leave them out completely.
+    if (IsARM) {
+      if (RelocationModel == llvm::Reloc::ROPI ||
+          RelocationModel == llvm::Reloc::ROPI_RWPI)
+        Result.push_back("-fropi");
+      else
+        Result.push_back("-fno-ropi");
+
+      if (RelocationModel == llvm::Reloc::RWPI ||
+          RelocationModel == llvm::Reloc::ROPI_RWPI)
+        Result.push_back("-frwpi");
+      else
+        Result.push_back("-fno-rwpi");
+    }
+
+    if (RelocationModel == llvm::Reloc::PIC_)
+      Result.push_back(IsPIE ? (PICLevel > 1 ? "-fPIE" : "-fpie")
+                             : (PICLevel > 1 ? "-fPIC" : "-fpic"));
+    else
+      Result.push_back("-fno-pic");
+  }
+
   // Sort and remove duplicates.
   std::sort(Result.begin(), Result.end());
   Result.erase(llvm::unique(Result), Result.end());
diff --git a/clang/test/Driver/print-multi-selection-flags.c b/clang/test/Driver/print-multi-selection-flags.c
index 5f9383fbed8f4..1d794091970d3 100644
--- a/clang/test/Driver/print-multi-selection-flags.c
+++ b/clang/test/Driver/print-multi-selection-flags.c
@@ -107,3 +107,22 @@
 // CHECK-AARCH64-MULTILIB-CUSTOM-FLAG: --target=aarch64-unknown-none-eabi
 // CHECK-MULTILIB-CUSTOM-FLAG-DAG:     -fmultilib-flag=foo
 // CHECK-MULTILIB-CUSTOM-FLAG-DAG:     -fmultilib-flag=bar
+
+// RUN: %clang -multi-lib-config=%S/Inputs/multilib/empty.yaml -print-multi-flags-experimental --target=arm-none-eabi -march=armv7a -fropi              | FileCheck --check-prefixes=CHECK-ROPI,CHECK-NO-RWPI,CHECK-NO-PIC %s
+// RUN: %clang -multi-lib-config=%S/Inputs/multilib/empty.yaml -print-multi-flags-experimental --target=arm-none-eabi -march=armv7a -frwpi              | FileCheck --check-prefixes=CHECK-RWPI,CHECK-NO-ROPI,CHECK-NO-PIC %s
+// RUN: %clang -multi-lib-config=%S/Inputs/multilib/empty.yaml -print-multi-flags-experimental --target=arm-none-eabi -march=armv7a -fropi -frwpi       | FileCheck --check-prefixes=CHECK-ROPI,CHECK-RWPI,CHECK-NO-PIC %s
+// RUN: %clang -multi-lib-config=%S/Inputs/multilib/empty.yaml -print-multi-flags-experimental --target=arm-none-eabi -march=armv7a -fno-ropi -fno-rwpi | FileCheck --check-prefixes=CHECK-NO-ROPI,CHECK-NO-RWPI,CHECK-NO-PIC %s
+// RUN: %clang -multi-lib-config=%S/Inputs/multilib/empty.yaml -print-multi-flags-experimental --target=arm-none-eabi -march=armv7a                     | FileCheck --check-prefixes=CHECK-NO-ROPI,CHECK-NO-RWPI,CHECK-NO-PIC %s
+// RUN: %clang -multi-lib-config=%S/Inputs/multilib/empty.yaml -print-multi-flags-experimental --target=arm-none-eabi -march=armv7a -fpic               | FileCheck --check-prefixes=CHECK-NO-ROPI,CHECK-NO-RWPI,CHECK-PIC1 %s
+// RUN: %clang -multi-lib-config=%S/Inputs/multilib/empty.yaml -print-multi-flags-experimental --target=arm-none-eabi -march=armv7a -fPIC               | FileCheck --check-prefixes=CHECK-NO-ROPI,CHECK-NO-RWPI,CHECK-PIC2 %s
+// RUN: %clang -multi-lib-config=%S/Inputs/multilib/empty.yaml -print-multi-flags-experimental --target=arm-none-eabi -march=armv7a -fpie               | FileCheck --check-prefixes=CHECK-NO-ROPI,CHECK-NO-RWPI,CHECK-PIE1 %s
+// RUN: %clang -multi-lib-config=%S/Inputs/multilib/empty.yaml -print-multi-flags-experimental --target=arm-none-eabi -march=armv7a -fPIE               | FileCheck --check-prefixes=CHECK-NO-ROPI,CHECK-NO-RWPI,CHECK-PIE2 %s
+// CHECK-ROPI: -fropi
+// CHECK-NO-ROPI: -fno-ropi
+// CHECK-RWPI: -frwpi
+// CHECK-NO-RWPI: -fno-rwpi
+// CHECK-PIC1: -fpic
+// CHECK-PIC2: -fPIC
+// CHECK-PIE1: -fpie
+// CHECK-PIE2: -fPIE
+// CHECK-NO-PIC: -fno-pic

@llvmbot
Copy link
Member

llvmbot commented Jul 16, 2025

@llvm/pr-subscribers-clang-driver

Author: Simon Tatham (statham-arm)

Changes

If a multilib collection contains libraries built for different methods of accessing global data (via absolute address, or via a GOT in -fPIC style, or as an offset from a fixed register in Arm -frwpi style), then multilib.yaml will need to know which relocation model an application is using in order to select the right library.

Even if a multilib collection only supports one relocation model, it's still useful for multilib.yaml to be able to tell if the user has selected the right one, so as to give a useful error message if they haven't, instead of silently selecting a library that won't work.

In this commit we determine the PIC / ROPI / RWPI status using the existing logic in ParsePICArgs, and translate it back into a canonical set of multilib selection flags.


Full diff: https://github.com/llvm/llvm-project/pull/149132.diff

2 Files Affected:

  • (modified) clang/lib/Driver/ToolChain.cpp (+37)
  • (modified) clang/test/Driver/print-multi-selection-flags.c (+19)
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 3f9b808b2722e..5738c0aa9b1d2 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -343,6 +343,7 @@ ToolChain::getMultilibFlags(const llvm::opt::ArgList &Args) const {
   std::vector<std::string> Result;
   const llvm::Triple Triple(ComputeEffectiveClangTriple(Args));
   Result.push_back("--target=" + Triple.str());
+  bool IsARM = false;
 
   switch (Triple.getArch()) {
   case llvm::Triple::aarch64:
@@ -355,6 +356,7 @@ ToolChain::getMultilibFlags(const llvm::opt::ArgList &Args) const {
   case llvm::Triple::thumb:
   case llvm::Triple::thumbeb:
     getARMMultilibFlags(D, Triple, Args, Result);
+    IsARM = true; // for ROPI/RWPI below
     break;
   case llvm::Triple::riscv32:
   case llvm::Triple::riscv64:
@@ -376,6 +378,41 @@ ToolChain::getMultilibFlags(const llvm::opt::ArgList &Args) const {
   else
     Result.push_back("-fexceptions");
 
+  // A difference of relocation model (absolutely addressed data, PIC, Arm
+  // ROPI/RWPI) is likely to change whether a particular multilib variant is
+  // compatible with a given link. Determine the relocation model of the
+  // current link, and add appropriate 
+  {
+    RegisterEffectiveTriple TripleRAII(
+        *this, llvm::Triple(ComputeEffectiveClangTriple(Args)));
+
+    auto [RelocationModel, PICLevel, IsPIE] = tools::ParsePICArgs(*this, Args);
+
+    // ROPI and RWPI are only meaningful on Arm, so for other architectures, we
+    // expect never to find out they're enabled. But it seems confusing to add
+    // -fno-ropi and -fno-rwpi unconditionally to every other architecture's
+    // multilib flags, so instead we leave them out completely.
+    if (IsARM) {
+      if (RelocationModel == llvm::Reloc::ROPI ||
+          RelocationModel == llvm::Reloc::ROPI_RWPI)
+        Result.push_back("-fropi");
+      else
+        Result.push_back("-fno-ropi");
+
+      if (RelocationModel == llvm::Reloc::RWPI ||
+          RelocationModel == llvm::Reloc::ROPI_RWPI)
+        Result.push_back("-frwpi");
+      else
+        Result.push_back("-fno-rwpi");
+    }
+
+    if (RelocationModel == llvm::Reloc::PIC_)
+      Result.push_back(IsPIE ? (PICLevel > 1 ? "-fPIE" : "-fpie")
+                             : (PICLevel > 1 ? "-fPIC" : "-fpic"));
+    else
+      Result.push_back("-fno-pic");
+  }
+
   // Sort and remove duplicates.
   std::sort(Result.begin(), Result.end());
   Result.erase(llvm::unique(Result), Result.end());
diff --git a/clang/test/Driver/print-multi-selection-flags.c b/clang/test/Driver/print-multi-selection-flags.c
index 5f9383fbed8f4..1d794091970d3 100644
--- a/clang/test/Driver/print-multi-selection-flags.c
+++ b/clang/test/Driver/print-multi-selection-flags.c
@@ -107,3 +107,22 @@
 // CHECK-AARCH64-MULTILIB-CUSTOM-FLAG: --target=aarch64-unknown-none-eabi
 // CHECK-MULTILIB-CUSTOM-FLAG-DAG:     -fmultilib-flag=foo
 // CHECK-MULTILIB-CUSTOM-FLAG-DAG:     -fmultilib-flag=bar
+
+// RUN: %clang -multi-lib-config=%S/Inputs/multilib/empty.yaml -print-multi-flags-experimental --target=arm-none-eabi -march=armv7a -fropi              | FileCheck --check-prefixes=CHECK-ROPI,CHECK-NO-RWPI,CHECK-NO-PIC %s
+// RUN: %clang -multi-lib-config=%S/Inputs/multilib/empty.yaml -print-multi-flags-experimental --target=arm-none-eabi -march=armv7a -frwpi              | FileCheck --check-prefixes=CHECK-RWPI,CHECK-NO-ROPI,CHECK-NO-PIC %s
+// RUN: %clang -multi-lib-config=%S/Inputs/multilib/empty.yaml -print-multi-flags-experimental --target=arm-none-eabi -march=armv7a -fropi -frwpi       | FileCheck --check-prefixes=CHECK-ROPI,CHECK-RWPI,CHECK-NO-PIC %s
+// RUN: %clang -multi-lib-config=%S/Inputs/multilib/empty.yaml -print-multi-flags-experimental --target=arm-none-eabi -march=armv7a -fno-ropi -fno-rwpi | FileCheck --check-prefixes=CHECK-NO-ROPI,CHECK-NO-RWPI,CHECK-NO-PIC %s
+// RUN: %clang -multi-lib-config=%S/Inputs/multilib/empty.yaml -print-multi-flags-experimental --target=arm-none-eabi -march=armv7a                     | FileCheck --check-prefixes=CHECK-NO-ROPI,CHECK-NO-RWPI,CHECK-NO-PIC %s
+// RUN: %clang -multi-lib-config=%S/Inputs/multilib/empty.yaml -print-multi-flags-experimental --target=arm-none-eabi -march=armv7a -fpic               | FileCheck --check-prefixes=CHECK-NO-ROPI,CHECK-NO-RWPI,CHECK-PIC1 %s
+// RUN: %clang -multi-lib-config=%S/Inputs/multilib/empty.yaml -print-multi-flags-experimental --target=arm-none-eabi -march=armv7a -fPIC               | FileCheck --check-prefixes=CHECK-NO-ROPI,CHECK-NO-RWPI,CHECK-PIC2 %s
+// RUN: %clang -multi-lib-config=%S/Inputs/multilib/empty.yaml -print-multi-flags-experimental --target=arm-none-eabi -march=armv7a -fpie               | FileCheck --check-prefixes=CHECK-NO-ROPI,CHECK-NO-RWPI,CHECK-PIE1 %s
+// RUN: %clang -multi-lib-config=%S/Inputs/multilib/empty.yaml -print-multi-flags-experimental --target=arm-none-eabi -march=armv7a -fPIE               | FileCheck --check-prefixes=CHECK-NO-ROPI,CHECK-NO-RWPI,CHECK-PIE2 %s
+// CHECK-ROPI: -fropi
+// CHECK-NO-ROPI: -fno-ropi
+// CHECK-RWPI: -frwpi
+// CHECK-NO-RWPI: -fno-rwpi
+// CHECK-PIC1: -fpic
+// CHECK-PIC2: -fPIC
+// CHECK-PIE1: -fpie
+// CHECK-PIE2: -fPIE
+// CHECK-NO-PIC: -fno-pic

Copy link

github-actions bot commented Jul 16, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@statham-arm
Copy link
Collaborator Author

Note to reviewers: the thing I'm most uncertain of in this patch is whether I'm using the right strategy for finding out the information about relocation model.

ParsePICArgs looked sensible, but then failed an assertion if there was no "effective triple" set, so I dug out what looked like the obvious way to figure one out, and did that.

But I'm not very confident of what an "effective triple" is at all, and I don't know for sure that it's right to call ComputeEffectiveClangTriple(Args) to make one. So if there's any reason why that will give the wrong answer in a particular situation (e.g. because some information it depends on isn't finalized yet) then please don't assume I have a clever reason for knowing why it's safe after all 🙂

(Thanks to clang-format for pointing out the trailing space on that
line, which was there because I meant to type a word after it!)
// expect never to find out they're enabled. But it seems confusing to add
// -fno-ropi and -fno-rwpi unconditionally to every other architecture's
// multilib flags, so instead we leave them out completely.
if (IsARM) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any way to move this Arm-only segment into getARMMultilibFlags? In theory, that function handles everything that is only relevant to Arm. So it's a more natural place to host this segment here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. In my early drafts I had the whole thing in getARMMultilibFlags, but moved it out once I realised that PIC needed to be handled as well as ROPI/RWPI. I've kept the calculation of the relocation model in the top-level function, and just passed the answer down into getARMMultilibFlags where it can add the Arm-specific options.

@statham-arm
Copy link
Collaborator Author

But I'm not very confident of what an "effective triple" is at all, and I don't know for sure that it's right to call ComputeEffectiveClangTriple(Args) to make one.

Answering my own question: I had failed to spot that ToolChain::getMultilibFlags was already calling ComputeEffectiveClangTriple at the top of the function! So I'm now more confident that it's sensible, but also, I could have just reused the existing value 🙂

All the CHECK lines are checked in a single run of FileCheck, so the
options they mention must appear in the same order that the flags are
shown in the -print-multi-flags output, which is ASCII sorting order.
@statham-arm statham-arm merged commit 3ce06b8 into llvm:main Jul 18, 2025
9 checks passed
statham-arm added a commit to arm/arm-toolchain that referenced this pull request Jul 18, 2025
ATfE doesn't currently provide any library variants built for RWPI. And
if you link RWPI with non-RWPI code, nothing good will happen – they
have incompatible ABIs. So we ought to give an error message rather than
silently choose an incompatible library.

Using llvm/llvm-project#149132 which allowed
multilib selection to be aware of the relocation model at all, this
commit does the simplest possible thing: adds a catch-all error message
that will trigger on _any_ use of `-frwpi`, and report that there's no
suitable library variant.

(Another possibility would have been to add `-fno-rwpi` to the validity
criteria for each existing library, but that seemed less likely to give
a good error message. It would allow RWPI variants to be present for
some cases but not all, but at the moment, we don't need that.)

Since the error message works the same everywhere, I've just added one
test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants