Skip to content
Permalink
Browse files

[ELF] Handle -u before input files

If both a.a and b.so define foo

```
ld.bfd -u foo a.a b.so  # foo is defined
ld.bfd a.a b.so -u foo  # foo is defined
ld.bfd -u foo b.so a.a  # foo is undefined (provided at runtime by b.so)
ld.bfd b.so a.a -u foo  # foo is undefined (provided at runtime by b.so)
```

In all cases we make foo undefined in the output.  I tend to think the
GNU ld behavior makes more sense.

* In their model, they have to treat -u as a fake object file with an
  undefined symbol before all input files, otherwise the first archive would not be fetched.
* Following their behavior allows us to drop a --warn-backrefs special case.

Reviewed By: psmith

Differential Revision: https://reviews.llvm.org/D81052
  • Loading branch information
MaskRay committed Jun 5, 2020
1 parent c063b4a commit 7bee6e30fe634624a99b43615261f4b5311e7dd1
Showing with 24 additions and 11 deletions.
  1. +16 −9 lld/ELF/Driver.cpp
  2. +8 −2 lld/test/ELF/undefined-opt.s
@@ -1470,16 +1470,12 @@ static void excludeLibs(opt::InputArgList &args) {
visit(file);
}

// Force Sym to be entered in the output. Used for -u or equivalent.
// Force Sym to be entered in the output.
static void handleUndefined(Symbol *sym) {
// Since a symbol may not be used inside the program, LTO may
// eliminate it. Mark the symbol as "used" to prevent it.
sym->isUsedInRegularObj = true;

// GNU linkers allow -u foo -ldef -lref. We should not treat it as a backward
// reference.
backwardReferences.erase(sym);

if (sym->isLazy())
sym->fetch();
}
@@ -1675,6 +1671,12 @@ static Symbol *addUndefined(StringRef name) {
Undefined{nullptr, name, STB_GLOBAL, STV_DEFAULT, 0});
}

static Symbol *addUnusedUndefined(StringRef name) {
Undefined sym{nullptr, name, STB_GLOBAL, STV_DEFAULT, 0};
sym.isUsedInRegularObj = false;
return symtab->addSymbol(sym);
}

// This function is where all the optimizations of link-time
// optimization takes place. When LTO is in use, some input files are
// not in native object file format but in the LLVM bitcode format.
@@ -1851,6 +1853,11 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
for (auto *arg : args.filtered(OPT_trace_symbol))
symtab->insert(arg->getValue())->traced = true;

// Handle -u/--undefined before input files. If both a.a and b.so define foo,
// -u foo a.a b.so will fetch a.a.
for (StringRef name : config->undefined)
addUnusedUndefined(name);

// Add all files to the symbol table. This will add almost all
// symbols that we need to the symbol table. This process might
// add files to the link, via autolinking, these files are always
@@ -1875,10 +1882,10 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
for (StringRef name : script->referencedSymbols)
addUndefined(name);

// Handle the `--undefined <sym>` options.
for (StringRef arg : config->undefined)
if (Symbol *sym = symtab->find(arg))
handleUndefined(sym);
// Prevent LTO from removing any definition referenced by -u.
for (StringRef name : config->undefined)
if (Defined *sym = dyn_cast_or_null<Defined>(symtab->find(name)))
sym->isUsedInRegularObj = true;

// If an entry symbol is in a static archive, pull out that file now.
if (Symbol *sym = symtab->find(config->entry))
@@ -54,13 +54,19 @@
# UNK-UNDEFINED-SO: ]

# Added undefined symbols should appear in the dynamic table if necessary.
# RUN: ld.lld -shared -o %t5 %t.o -u export
# RUN: llvm-readobj --dyn-symbols %t5 | \
# RUN: ld.lld -shared -soname=t -o %t.so %t.o -u export
# RUN: llvm-readobj --dyn-symbols %t.so | \
# RUN: FileCheck --check-prefix=EXPORT-SO %s
# EXPORT-SO: DynamicSymbols [
# EXPORT-SO: Name: export
# EXPORT-SO: ]

## Test that we handle -u before input files: if we handle -u after
## %t.so, foo would be undefined in the output.
# RUN: rm -f %t.a && llvm-ar rc %t.a %t.o
# RUN: ld.lld -u export %t.a %t.so -o %t5
# RUN: llvm-readobj --dyn-symbols %t5 | FileCheck --check-prefix=EXPORT-SO %s

.globl _start
_start:

0 comments on commit 7bee6e3

Please sign in to comment.
You can’t perform that action at this time.