[lld] [ELF] Implement --force-group-allocation (PR #94704)

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 6 17:17:29 PDT 2024


https://github.com/MaskRay created https://github.com/llvm/llvm-project/pull/94704

GNU ld's relocatable linking behaviors:

* Sections with the `SHF_GROUP` flag are handled like sections matched
  by the `--unique=pattern` option. They are processed like orphan
  sections and ignored by input section descriptions.
* Section groups' (usually named `.group`) content is updated as the
  section indexes are updated. Section groups can be discarded with
  `/DISCARD/ : { *(.group) }`.

`-r --force-group-allocation` discards section groups and allows
sections with the `SHF_GROUP` flag to be matched like normal sections.
If two section group members are placed into the same output section,
their relocation sections (if present) are combined as well.
This behavior can be useful when -r output is used as a pseudo shared
object (e.g., FreeBSD's amd64 kernel modules, CHERIoT compartments).

This patch implements --force-group-allocation:

* Input SHT_GROUP sections are discarded.
* Input sections do not get the SHF_GROUP flag, so `addInputSec`
  will combine relocation sections if their relocated section group
  members are combined.

--inhibit-group-allocation restores the default behavior:

* Input SHT_GROUP sections are retained.
* Input SHF_GROUP sections can be matched (unlike GNU ld)
* Input SHF_GROUP sections keep the SHF_GROUP flag, so `addInputSec`
  will create different OutputDesc copies.

GNU ld provides the `FORCE_GROUP_ALLOCATION` command, which is not
implemented.


>From 880b593466ddba2adbdda5da0b6d4b1c741b54cc Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Thu, 6 Jun 2024 17:17:19 -0700
Subject: [PATCH] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20initia?=
 =?UTF-8?q?l=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.5-bogner
---
 lld/ELF/Config.h                  |  1 +
 lld/ELF/Driver.cpp                |  3 +++
 lld/ELF/InputFiles.cpp            |  2 +-
 lld/ELF/InputSection.cpp          |  9 +++++----
 lld/ELF/Options.td                |  5 +++++
 lld/docs/ld.lld.1                 |  4 ++++
 lld/test/ELF/relocatable-comdat.s | 12 ++++++++++++
 7 files changed, 31 insertions(+), 5 deletions(-)

diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 883c4a2f84294..0173be396163e 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -286,6 +286,7 @@ struct Config {
   bool relax;
   bool relaxGP;
   bool relocatable;
+  bool resolveGroups;
   bool relrGlibc = false;
   bool relrPackDynRelocs = false;
   llvm::DenseSet<llvm::StringRef> saveTempsArgs;
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index ddc574a11314b..e8442cc993821 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1384,6 +1384,9 @@ static void readConfigs(opt::InputArgList &args) {
   config->relaxGP = args.hasFlag(OPT_relax_gp, OPT_no_relax_gp, false);
   config->rpath = getRpath(args);
   config->relocatable = args.hasArg(OPT_relocatable);
+  config->resolveGroups = !args.hasArg(OPT_relocatable) ||
+                          args.hasFlag(OPT_force_group_allocation,
+                                       OPT_inhibit_group_allocation, false);
 
   if (args.hasArg(OPT_save_temps)) {
     // --save-temps implies saving all temps.
diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp
index d760dddcf5ec5..9021bbd91b5f7 100644
--- a/lld/ELF/InputFiles.cpp
+++ b/lld/ELF/InputFiles.cpp
@@ -676,7 +676,7 @@ template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
         symtab.comdatGroups.try_emplace(CachedHashStringRef(signature), this)
             .second;
     if (keepGroup) {
-      if (config->relocatable)
+      if (!config->resolveGroups)
         this->sections[i] = createInputSection(
             i, sec, check(obj.getSectionName(sec, shstrtab)));
       continue;
diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index e6c5996c0b392..0f176f144d46b 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -76,12 +76,13 @@ InputSectionBase::InputSectionBase(InputFile *file, uint64_t flags,
     invokeELFT(parseCompressedHeader,);
 }
 
-// Drop SHF_GROUP bit unless we are producing a re-linkable object file.
-// SHF_GROUP is a marker that a section belongs to some comdat group.
-// That flag doesn't make sense in an executable.
+// SHF_INFO_LINK and SHF_GROUP are normally resolved and not copied to the
+// output section. However, for relocatable linking with the default
+// --inhibit-group-allocation, the SHF_GROUP marker and section groups are
+// retained.
 static uint64_t getFlags(uint64_t flags) {
   flags &= ~(uint64_t)SHF_INFO_LINK;
-  if (!config->relocatable)
+  if (config->resolveGroups)
     flags &= ~(uint64_t)SHF_GROUP;
   return flags;
 }
diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index ff61a566f52f7..23385c76182eb 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -254,6 +254,11 @@ def fix_cortex_a53_843419: F<"fix-cortex-a53-843419">,
 def fix_cortex_a8: F<"fix-cortex-a8">,
   HelpText<"Apply fixes for ARM Cortex-A8 erratum 657417">;
 
+def force_group_allocation: FF<"force-group-allocation">,
+  HelpText<"Only meaningful for -r. Section groups are discarded. If two section group members are combined into the same output section, combine their relocations as well">;
+def inhibit_group_allocation: FF<"inhibit-group-allocation">,
+  HelpText<"This is the default for -r. Section groups are retained. Section group members' relocations are not combined">;
+
 defm format: Eq<"format", "Change the input format of the inputs following this option">,
   MetaVarName<"[default,elf,binary]">;
 
diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1
index da3b926d02a28..82d590a1d084f 100644
--- a/lld/docs/ld.lld.1
+++ b/lld/docs/ld.lld.1
@@ -278,6 +278,10 @@ Set the
 field to the specified value.
 .It Fl -fini Ns = Ns Ar symbol
 Specify a finalizer function.
+.It Fl -force-group-allocation
+Only meaningful for -r. Section groups are discarded. If two section group members are combined into the same output section, combine their relocations as well.
+.It Fl -inhibit-group-allocation
+This is the default for -r. Section groups are retained. Section group members' relocations are not combined.
 .It Fl -format Ns = Ns Ar input-format , Fl b Ar input-format
 Specify the format of the inputs following this option.
 .Ar input-format
diff --git a/lld/test/ELF/relocatable-comdat.s b/lld/test/ELF/relocatable-comdat.s
index 45ca9fb7a2484..8ca8f107f885e 100644
--- a/lld/test/ELF/relocatable-comdat.s
+++ b/lld/test/ELF/relocatable-comdat.s
@@ -47,6 +47,18 @@
 # COMBINE-NEXT: .rela.text
 # COMBINE-NEXT: .rela.text
 
+## If --force-group-allocation is specified, discard .group and combine .rela.* if their relocated sections are combined.
+# RUN: ld.lld -r -T combine.lds a.o a.o --force-group-allocation -o combine-a.ro
+# RUN: llvm-readelf -g -S combine-a.ro | FileCheck %s --check-prefix=COMBINE-A
+## --inhibit-group-allocation restores the default behavior.
+# RUN: ld.lld -r -T combine.lds a.o a.o --force-group-allocation --inhibit-group-allocation -o - | cmp - combine.ro
+
+# COMBINE-A:      Name            Type     Address          Off    Size   ES Flg Lk    Inf   Al
+# COMBINE-A:      .rodata         PROGBITS 0000000000000000 {{.*}} 000002 00   A  0      0    1
+# COMBINE-A-NEXT: .text           PROGBITS 0000000000000000 {{.*}} 000010 00  AX  0      0    4
+# COMBINE-A-NEXT: .rela.text      RELA     0000000000000000 {{.*}} 000030 18   I [[#]] [[#]]  8
+# COMBINE-A-NEXT: .note.GNU-stack
+
 # RUN: echo 'SECTIONS { /DISCARD/ : {*(.rodata.*)} }' > discard-rodata.lds
 # RUN: ld.lld -r -T discard-rodata.lds a.o a.o -o discard-rodata.ro
 # RUN: llvm-readelf -g -S discard-rodata.ro | FileCheck %s --check-prefix=NO-RODATA



More information about the llvm-commits mailing list