[lld] [lld/ELF] Warn when a PC32 relocation references a large section (PR #71248)

Arthur Eubanks via llvm-commits llvm-commits at lists.llvm.org
Fri Nov 3 15:55:30 PDT 2023


https://github.com/aeubanks created https://github.com/llvm/llvm-project/pull/71248

Large sections are meant to be referenced with 64-bit relocations.
PC32 relocations to large section can still accidentally happen, typically due to hand-written assembly.
Add a flag to warn on these so they're easily findable. Off by default since this noticeably impacts link times.

perf stat instruction counts for linking bin/opt with --threads=1:
w/o patch (small code model):                                       1,917M
w/ patch (small code model):                                        1,918M
w/ patch, --warn-32-bit-reloc-to-large-section (small code model):  1,919M

w/o patch (medium code model):                                      1,918M
w/ patch, (medium code model):                                      1,919M
w/ patch, --warn-32-bit-reloc-to-large-section (medium code model): 1,967M


>From b3dca9bd63c8412c5fb9d34d343bc95223e4a302 Mon Sep 17 00:00:00 2001
From: Arthur Eubanks <aeubanks at google.com>
Date: Thu, 2 Nov 2023 16:05:37 -0700
Subject: [PATCH] [lld/ELF] Warn when a PC32 relocation references a large
 section

Large sections are meant to be referenced with 64-bit relocations.
PC32 relocations to large section can still accidentally happen, typically due to hand-written assembly.
Add a flag to warn on these so they're easily findable. Off by default since this noticeably impacts link times.

perf stat instruction counts for linking bin/opt with --threads=1:
w/o patch (small code model):                                       1,917M
w/ patch (small code model):                                        1,918M
w/ patch, --warn-32-bit-reloc-to-large-section (small code model):  1,919M

w/o patch (medium code model):                                      1,918M
w/ patch, (medium code model):                                      1,919M
w/ patch, --warn-32-bit-reloc-to-large-section (medium code model): 1,967M
---
 lld/ELF/Arch/X86_64.cpp   | 25 ++++++++++++++++++++++++-
 lld/ELF/Config.h          |  3 +++
 lld/ELF/Driver.cpp        |  2 ++
 lld/ELF/Options.td        |  4 ++++
 lld/ELF/Relocations.cpp   |  2 +-
 lld/ELF/Target.h          |  2 ++
 lld/ELF/Writer.cpp        |  6 +++++-
 lld/test/ELF/warn-large.s | 32 ++++++++++++++++++++++++++++++++
 8 files changed, 73 insertions(+), 3 deletions(-)
 create mode 100644 lld/test/ELF/warn-large.s

diff --git a/lld/ELF/Arch/X86_64.cpp b/lld/ELF/Arch/X86_64.cpp
index 2135ac234864513..9fc7555beec28ab 100644
--- a/lld/ELF/Arch/X86_64.cpp
+++ b/lld/ELF/Arch/X86_64.cpp
@@ -757,6 +757,26 @@ int64_t X86_64::getImplicitAddend(const uint8_t *buf, RelType type) const {
 
 static void relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val);
 
+static void warnIfRelocToLargeSection(uint8_t *loc, const Relocation &rel) {
+  if (!rel.sym || !rel.sym->getOutputSection())
+    return;
+  Symbol &sym = *rel.sym;
+  if (sym.getOutputSection()->flags & SHF_X86_64_LARGE) {
+    ErrorPlace errPlace = getErrorPlace(loc);
+    std::string hint;
+    if (!sym.isSection())
+      hint = "; references '" + lld::toString(sym) + '\'';
+    else if (auto *d = dyn_cast<Defined>(&sym))
+      hint = ("; references section '" + d->section->name + "'").str();
+    if (!errPlace.srcLoc.empty())
+      hint += "\n>>> referenced by " + errPlace.srcLoc;
+    if (!sym.isSection())
+      hint += getDefinedLocation(sym);
+    warn(errPlace.loc +
+         "Large section should not be addressed with PC32 relocation" + hint);
+  }
+}
+
 void X86_64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
   switch (rel.type) {
   case R_X86_64_8:
@@ -779,11 +799,14 @@ void X86_64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
     checkUInt(loc, val, 32, rel);
     write32le(loc, val);
     break;
+  case R_X86_64_PC32:
+    if (config->warnLarge && ctx.hasLargeSection)
+      warnIfRelocToLargeSection(loc, rel);
+    [[fallthrough]];
   case R_X86_64_32S:
   case R_X86_64_GOT32:
   case R_X86_64_GOTPC32:
   case R_X86_64_GOTPCREL:
-  case R_X86_64_PC32:
   case R_X86_64_PLT32:
   case R_X86_64_DTPOFF32:
   case R_X86_64_SIZE32:
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 56229334f9a44ae..9c57ca95979f0c4 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -298,6 +298,7 @@ struct Config {
   bool warnCommon;
   bool warnMissingEntry;
   bool warnSymbolOrdering;
+  bool warnLarge;
   bool writeAddends;
   bool zCombreloc;
   bool zCopyreloc;
@@ -482,6 +483,8 @@ struct Ctx {
   // True if all native vtable symbols have corresponding type info symbols
   // during LTO.
   bool ltoAllVtablesHaveTypeInfos;
+  // True if any output section has the SHF_X86_64_LARGE flag set.
+  bool hasLargeSection;
 
   // Each symbol assignment and DEFINED(sym) reference is assigned an increasing
   // order. Each DEFINED(sym) evaluation checks whether the reference happens
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 5f88389a5840824..0c05d3af68c84d5 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -111,6 +111,7 @@ void Ctx::reset() {
   scriptSymOrderCounter = 1;
   scriptSymOrder.clear();
   ltoAllVtablesHaveTypeInfos = false;
+  hasLargeSection = false;
 }
 
 llvm::raw_fd_ostream Ctx::openAuxiliaryFile(llvm::StringRef filename,
@@ -1440,6 +1441,7 @@ static void readConfigs(opt::InputArgList &args) {
   config->warnCommon = args.hasFlag(OPT_warn_common, OPT_no_warn_common, false);
   config->warnSymbolOrdering =
       args.hasFlag(OPT_warn_symbol_ordering, OPT_no_warn_symbol_ordering, true);
+  config->warnLarge = args.hasFlag(OPT_warn_large, OPT_no_warn_large, false);
   config->whyExtract = args.getLastArgValue(OPT_why_extract);
   config->zCombreloc = getZFlag(args, "combreloc", "nocombreloc", true);
   config->zCopyreloc = getZFlag(args, "copyreloc", "nocopyreloc", true);
diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index 9a23f48350644a0..7dd01fe54fc9005 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -522,6 +522,10 @@ defm warn_symbol_ordering: BB<"warn-symbol-ordering",
     "Warn about problems with the symbol ordering file (default)",
     "Do not warn about problems with the symbol ordering file">;
 
+defm warn_large: BB<"warn-32-bit-reloc-to-large-section",
+    "Warn about PC32 relocations to a large section",
+    "Do not warn about PC32 relocations to a large section">;
+
 def warn_unresolved_symbols: F<"warn-unresolved-symbols">,
   HelpText<"Report unresolved symbols as warnings">;
 
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index f3fb0c71a8b3064..418847faeefc5a3 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -73,7 +73,7 @@ static std::optional<std::string> getLinkerScriptLocation(const Symbol &sym) {
   return std::nullopt;
 }
 
-static std::string getDefinedLocation(const Symbol &sym) {
+std::string elf::getDefinedLocation(const Symbol &sym) {
   const char msg[] = "\n>>> defined in ";
   if (sym.file)
     return msg + toString(sym.file);
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index 6264ab1a3da74a7..16406d222ada36c 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -242,6 +242,8 @@ TargetInfo *getTarget();
 
 template <class ELFT> bool isMipsPIC(const Defined *sym);
 
+std::string getDefinedLocation(const Symbol &sym);
+
 void reportRangeError(uint8_t *loc, const Relocation &rel, const Twine &v,
                       int64_t min, uint64_t max);
 void reportRangeError(uint8_t *loc, int64_t v, int n, const Symbol &sym,
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 57e1aa06c6aa873..2c1ff97b4d5ac81 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -25,6 +25,7 @@
 #include "lld/Common/Filesystem.h"
 #include "lld/Common/Strings.h"
 #include "llvm/ADT/StringMap.h"
+#include "llvm/BinaryFormat/ELF.h"
 #include "llvm/Support/BLAKE3.h"
 #include "llvm/Support/Parallel.h"
 #include "llvm/Support/RandomNumberGenerator.h"
@@ -2233,8 +2234,11 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
   // Fill other section headers. The dynamic table is finalized
   // at the end because some tags like RELSZ depend on result
   // of finalizing other sections.
-  for (OutputSection *sec : outputSections)
+  for (OutputSection *sec : outputSections) {
     sec->finalize();
+    if (sec->flags & SHF_X86_64_LARGE)
+      ctx.hasLargeSection = true;
+  }
 
   script->checkFinalScriptConditions();
 
diff --git a/lld/test/ELF/warn-large.s b/lld/test/ELF/warn-large.s
new file mode 100644
index 000000000000000..aec9dc8f2688ae6
--- /dev/null
+++ b/lld/test/ELF/warn-large.s
@@ -0,0 +1,32 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
+# RUN: ld.lld %t.o -o /dev/null 2>&1 --warn-32-bit-reloc-to-large-section | FileCheck %s
+# RUN: ld.lld %t.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=NO --allow-empty
+
+# CHECK: warning: {{.*}}warn-large.s{{.*}}:(.text+{{.*}}): Large section should not be addressed with PC32 relocation; references 'hello'
+# CHECK-NEXT: >>> referenced by foo.c
+# CHECK-NEXT: >>> defined in {{.*}}warn-large.s{{.*}}
+
+# CHECK: warning: {{.*}}warn-large.s{{.*}}:(.text+{{.*}}): Large section should not be addressed with PC32 relocation; references section 'ldata'
+# CHECK-NEXT: >>> referenced by foo.c
+
+# NO-NOT: warning
+
+.text
+.file "foo.c"
+.globl _start
+.type _start, @function
+_start:
+  movq hello(%rip), %rax
+  movq ok(%rip), %rax
+
+.section ldata,"awl", at progbits
+
+.type   hello, @object
+.globl  hello
+.p2align        2, 0x0
+hello:
+.long   1
+
+ok:
+.long   1



More information about the llvm-commits mailing list