From 1c6688ae3449da9c8fee1e1c12c892223496fb4c Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Fri, 11 Oct 2024 08:47:07 -0700 Subject: [PATCH] [ELF] Make shouldAddProvideSym return values consistent when demoted to Undefined Case: `PROVIDE(f1 = bar);` when both `f1` and `bar` are in separate sections that would be discarded by GC. Due to `demoteDefined`, `shouldAddProvideSym(f1)` may initially return false (when Defined) and then return true (been demoted to Undefined). ``` addScriptReferencedSymbolsToSymTable shouldAddProvideSym(f1): false // the RHS (bar) is not added to `referencedSymbols` and may be GCed declareSymbols shouldAddProvideSym(f1): false markLive demoteSymbolsAndComputeIsPreemptible // demoted f1 to Undefined processSymbolAssignments addSymbol shouldAddProvideSym(f1): true ``` The inconsistency can cause `cmd->expression()` in `addSymbol` to be evaluated, leading to `symbol not found: bar` errors (since `bar` in the RHS is not in `referencedSymbols` and is GCed) (#111478). Fix this by adding a `sym->isUsedInRegularObj` condition, making `shouldAddProvideSym(f1)` values consistent. In addition, we need a `sym->exportDynamic` condition to keep provide-shared.s working. Fixes: ebb326a51fec37b5a47e5702e8ea157cd4f835cd Pull Request: https://github.com/llvm/llvm-project/pull/111945 --- lld/ELF/LinkerScript.cpp | 9 +++++- lld/test/ELF/linkerscript/provide-defined.s | 36 +++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 lld/test/ELF/linkerscript/provide-defined.s diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp index b736cb1beef37e..21d243c5703da8 100644 --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -1814,6 +1814,13 @@ void LinkerScript::addScriptReferencedSymbolsToSymTable() { } 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 *sym = elf::ctx.symtab->find(symName); - return sym && !sym->isDefined() && !sym->isCommon(); + return sym && !sym->isDefined() && !sym->isCommon() && + (sym->isUsedInRegularObj || sym->exportDynamic); } diff --git a/lld/test/ELF/linkerscript/provide-defined.s b/lld/test/ELF/linkerscript/provide-defined.s new file mode 100644 index 00000000000000..1d44bef3d4068d --- /dev/null +++ b/lld/test/ELF/linkerscript/provide-defined.s @@ -0,0 +1,36 @@ +# REQUIRES: x86 +## Test the GC behavior when the PROVIDE symbol is defined by a relocatable file. + +# 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: ld.lld -T a.t --gc-sections a.o b.o -o a +# RUN: llvm-readelf -s a | FileCheck %s + +# CHECK: 1: {{.*}} 0 NOTYPE GLOBAL DEFAULT 1 _start +# CHECK-NEXT:2: {{.*}} 0 NOTYPE GLOBAL DEFAULT 2 f3 +# CHECK-NOT: {{.}} + +#--- a.s +.global _start, f1, f2, f3, bar +_start: + call f3 + +.section .text.f1,"ax"; f1: +.section .text.f2,"ax"; f2: # referenced by another relocatable file +.section .text.f3,"ax"; f3: # live +.section .text.bar,"ax"; bar: + +.comm comm,4,4 + +#--- b.s + call f2 + +#--- a.t +SECTIONS { + . = . + SIZEOF_HEADERS; + PROVIDE(f1 = bar+1); + PROVIDE(f2 = bar+2); + PROVIDE(f3 = bar+3); + PROVIDE(f4 = comm+4); +}