Skip to content
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

[ELF] Fix PROVIDE_HIDDEN -shared regression with bitcode file references #112386

Conversation

MaskRay
Copy link
Member

@MaskRay MaskRay commented Oct 15, 2024

The inaccurate #111945 condition fixes a PROVIDE regression (#111478)
but introduces another regression: in a DSO link, if a symbol referenced
only by bitcode files is defined as PROVIDE_HIDDEN, lld would not set
the visibility correctly, leading to an assertion failure in
DynamicReloc::getSymIndex (https://reviews.llvm.org/D123985).
This is because (sym->isUsedInRegularObj || sym->exportDynamic) is
initially false (bitcode undef does not set isUsedInRegularObj) then
true (in addSymbol, after LTO compilation).

Fix this by making the condition accurate: use a map to track defined
symbols.

Created using spr 1.3.6-beta.1
@llvmbot
Copy link
Member

llvmbot commented Oct 15, 2024

@llvm/pr-subscribers-lld-elf

@llvm/pr-subscribers-lld

Author: Fangrui Song (MaskRay)

Changes

The inaccurate #111945 condition fixes a PROVIDE regression (#111478)
but introduces another regression: in a DSO link, if a symbol referenced
only by bitcode files is defined as PROVIDE_HIDDEN, lld would not set
the visibility correctly, leading to an assertion failure in
DynamicReloc::getSymIndex (https://reviews.llvm.org/D123985).

Fix this by making the condition accurate: use a map to track defined
symbols.


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

3 Files Affected:

  • (modified) lld/ELF/LinkerScript.cpp (+13-10)
  • (modified) lld/ELF/LinkerScript.h (+2-1)
  • (modified) lld/test/ELF/linkerscript/provide-defined.s (+26-2)
diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index 418f689cee2167..0560065ffa4780 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -200,7 +200,7 @@ static bool shouldDefineSym(Ctx &ctx, SymbolAssignment *cmd) {
   if (cmd->name == ".")
     return false;
 
-  return !cmd->provide || LinkerScript::shouldAddProvideSym(ctx, cmd->name);
+  return !cmd->provide || ctx.script->shouldAddProvideSym(cmd->name);
 }
 
 // Called by processSymbolAssignments() to assign definitions to
@@ -1798,7 +1798,7 @@ void LinkerScript::addScriptReferencedSymbolsToSymTable() {
   DenseSet<StringRef> added;
   SmallVector<const SmallVector<StringRef, 0> *, 0> symRefsVec;
   for (const auto &[name, symRefs] : provideMap)
-    if (shouldAddProvideSym(ctx, name) && added.insert(name).second)
+    if (shouldAddProvideSym(name) && added.insert(name).second)
       symRefsVec.push_back(&symRefs);
   while (symRefsVec.size()) {
     for (StringRef name : *symRefsVec.pop_back_val()) {
@@ -1806,7 +1806,7 @@ void LinkerScript::addScriptReferencedSymbolsToSymTable() {
       // Prevent the symbol from being discarded by --gc-sections.
       referencedSymbols.push_back(name);
       auto it = provideMap.find(name);
-      if (it != provideMap.end() && shouldAddProvideSym(ctx, name) &&
+      if (it != provideMap.end() && shouldAddProvideSym(name) &&
           added.insert(name).second) {
         symRefsVec.push_back(&it->second);
       }
@@ -1814,14 +1814,17 @@ void LinkerScript::addScriptReferencedSymbolsToSymTable() {
   }
 }
 
-bool LinkerScript::shouldAddProvideSym(Ctx &ctx, StringRef symName) {
+bool LinkerScript::shouldAddProvideSym(StringRef symName) {
   // This function is called before and after garbage collection. To prevent
   // undefined references from the RHS, the result of this function for a
-  // symbol must be the same for each call. We use isUsedInRegularObj to not
-  // change the return value of a demoted symbol. The exportDynamic condition,
-  // while not so accurate, allows PROVIDE to define a symbol referenced by a
-  // DSO.
+  // symbol must be the same for each call. We use unusedProvideSyms to not
+  // change the return value of a demoted symbol.
   Symbol *sym = ctx.symtab->find(symName);
-  return sym && !sym->isDefined() && !sym->isCommon() &&
-         (sym->isUsedInRegularObj || sym->exportDynamic);
+  if (!sym)
+    return false;
+  if (sym->isDefined() || sym->isCommon()) {
+    unusedProvideSyms.insert(sym);
+    return false;
+  }
+  return !unusedProvideSyms.count(sym);
 }
diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h
index eda152dd3582ab..f5c418d1f6af52 100644
--- a/lld/ELF/LinkerScript.h
+++ b/lld/ELF/LinkerScript.h
@@ -389,7 +389,7 @@ class LinkerScript final {
   // Returns true if the PROVIDE symbol should be added to the link.
   // A PROVIDE symbol is added to the link only if it satisfies an
   // undefined reference.
-  static bool shouldAddProvideSym(Ctx &, StringRef symName);
+  bool shouldAddProvideSym(StringRef symName);
 
   // SECTIONS command list.
   SmallVector<SectionCommand *, 0> sectionCommands;
@@ -433,6 +433,7 @@ class LinkerScript final {
   //
   // then provideMap should contain the mapping: 'v' -> ['a', 'b', 'c']
   llvm::MapVector<StringRef, SmallVector<StringRef, 0>> provideMap;
+  llvm::DenseSet<Symbol *> unusedProvideSyms;
 
   // List of potential spill locations (PotentialSpillSection) for an input
   // section.
diff --git a/lld/test/ELF/linkerscript/provide-defined.s b/lld/test/ELF/linkerscript/provide-defined.s
index 1d44bef3d4068d..650925cac4051f 100644
--- a/lld/test/ELF/linkerscript/provide-defined.s
+++ b/lld/test/ELF/linkerscript/provide-defined.s
@@ -4,15 +4,27 @@
 # RUN: rm -rf %t && split-file %s %t && cd %t
 # RUN: llvm-mc -filetype=obj -triple=x86_64 a.s -o a.o
 # RUN: llvm-mc -filetype=obj -triple=x86_64 b.s -o b.o
+# RUN: llvm-as c.ll -o c.bc
 # RUN: ld.lld -T a.t --gc-sections a.o b.o -o a
 # RUN: llvm-readelf -s a | FileCheck %s
 
+# RUN: ld.lld -T a.t -shared a.o b.o c.bc -o a.so
+# RUN: llvm-readelf -s -r a.so | FileCheck %s --check-prefix=DSO
+
 # CHECK:     1: {{.*}}               0 NOTYPE  GLOBAL DEFAULT     1 _start
-# CHECK-NEXT:2: {{.*}}               0 NOTYPE  GLOBAL DEFAULT     2 f3
+# CHECK-NEXT:2: {{.*}}               0 NOTYPE  WEAK   DEFAULT     2 f3
 # CHECK-NOT: {{.}}
 
+# DSO:     .rela.plt
+# DSO-NOT: f5
+# DSO:     Symbol table '.dynsym'
+# DSO-NOT: f5
+# DSO:     Symbol table '.symtab'
+# DSO:     {{.*}}               0 NOTYPE  LOCAL  HIDDEN  [[#]] f5
+
 #--- a.s
-.global _start, f1, f2, f3, bar
+.global _start, f1, f2, bar
+.weak f3
 _start:
   call f3
 
@@ -26,6 +38,17 @@ _start:
 #--- b.s
   call f2
 
+#--- c.ll
+target triple = "x86_64-unknown-linux-gnu"
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+
+declare void @f5()
+
+define void @f3() {
+  call void @f5()
+  ret void
+}
+
 #--- a.t
 SECTIONS {
   . = . + SIZEOF_HEADERS;
@@ -33,4 +56,5 @@ SECTIONS {
   PROVIDE(f2 = bar+2);
   PROVIDE(f3 = bar+3);
   PROVIDE(f4 = comm+4);
+  PROVIDE_HIDDEN(f5 = bar+5);
 }

Copy link
Collaborator

@smithp35 smithp35 left a comment

Choose a reason for hiding this comment

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

LGTM. A map will definitely keep the return values in sync. Apologies for missing the corner case in first review.

Created using spr 1.3.6-beta.1
@MaskRay MaskRay merged commit a2359a8 into main Oct 15, 2024
5 of 6 checks passed
@MaskRay MaskRay deleted the users/MaskRay/spr/elf-fix-provide_hidden-shared-regression-with-bitcode-file-references branch October 15, 2024 16:20
DanielCChen pushed a commit to DanielCChen/llvm-project that referenced this pull request Oct 16, 2024
The inaccurate llvm#111945 condition fixes a PROVIDE regression (llvm#111478)
but introduces another regression: in a DSO link, if a symbol referenced
only by bitcode files is defined as PROVIDE_HIDDEN, lld would not set
the visibility correctly, leading to an assertion failure in
DynamicReloc::getSymIndex (https://reviews.llvm.org/D123985).
This is because `(sym->isUsedInRegularObj || sym->exportDynamic)` is
initially false (bitcode undef does not set `isUsedInRegularObj`) then
true (in `addSymbol`, after LTO compilation).

Fix this by making the condition accurate: use a map to track defined
symbols.

Reviewers: smithp35

Reviewed By: smithp35

Pull Request: llvm#112386
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants