diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -50,6 +50,7 @@ #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compression.h" +#include "llvm/Support/GlobPattern.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/Path.h" #include "llvm/Support/TarWriter.h" @@ -1319,12 +1320,8 @@ } // Force Sym to be entered in the output. Used for -u or equivalent. -static void handleUndefined(StringRef Name) { - Symbol *Sym = Symtab->find(Name); - if (!Sym) - return; - - // Since symbol S may not be used inside the program, LTO may +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; @@ -1332,6 +1329,29 @@ Sym->fetch(); } +// As an extention to GNU linkers, lld supports a variant of `-u` +// which supports wildcard patterns. All symbols that match a given +// pattern are handled as if they were given by `-u`. +static void handleUndefinedGlob(StringRef Arg) { + Expected Pat = GlobPattern::create(Arg); + if (!Pat) { + error("--undefined-glob: " + toString(Pat.takeError())); + return; + } + + std::vector Syms; + Symtab->forEachSymbol([&](Symbol *Sym) { + // Calling Sym->fetch() from here is not safe because it may + // add new symbols to the symbol table, invalidating the + // current iterator. So we just keep a note. + if (Pat->match(Sym->getName())) + Syms.push_back(Sym); + }); + + for (Symbol *Sym : Syms) + handleUndefined(Sym); +} + static void handleLibcall(StringRef Name) { Symbol *Sym = Symtab->find(Name); if (!Sym || !Sym->isLazy()) @@ -1698,11 +1718,17 @@ addUndefined(Name); // Handle the `--undefined ` options. - for (StringRef S : Config->Undefined) - handleUndefined(S); + for (StringRef Arg : Config->Undefined) + if (Symbol *Sym = Symtab->find(Arg)) + handleUndefined(Sym); // If an entry symbol is in a static archive, pull out that file now. - handleUndefined(Config->Entry); + if (Symbol *Sym = Symtab->find(Config->Entry)) + handleUndefined(Sym); + + // Handle the `--undefined-glob ` options. + for (StringRef Pat : args::getStrings(Args, OPT_undefined_glob)) + handleUndefinedGlob(Pat); // If any of our inputs are bitcode files, the LTO code generator may create // references to certain library functions that might not be explicit in the diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -366,6 +366,9 @@ defm undefined: Eq<"undefined", "Force undefined symbol during linking">, MetaVarName<"">; +defm undefined_glob: Eq<"undefined-glob", "Force undefined symbol during linking">, + MetaVarName<"">; + defm unresolved_symbols: Eq<"unresolved-symbols", "Determine how to handle unresolved symbols">; diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1 --- a/lld/docs/ld.lld.1 +++ b/lld/docs/ld.lld.1 @@ -504,9 +504,23 @@ Trace references to .Ar symbol . .It Fl -undefined Ns = Ns Ar symbol , Fl u Ar symbol -Force +If .Ar symbol -to be an undefined symbol during linking. +is not defined after symbol resolution, and there's a static library +that contains an object file defining the symbol, load the member +to include the object file in the output file. +.It Fl -undefined-glob Ns = Ns Ar pattern +Synonym for +.Fl -undefined , +except that it takes a glob pattern. In a glob pattern, +.Cm * +matches zero or more characters, +.Cm ? +matches any single character, and +.Cm [...] +matches the characters within brackets. All symbols that match +a given pattern are handled as if they were given as arguments of +.Fl -undefined . .It Fl -unresolved-symbols Ns = Ns Ar value Determine how to handle unresolved symbols. .It Fl -use-android-relr-tags diff --git a/lld/test/ELF/undefined-glob.s b/lld/test/ELF/undefined-glob.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/undefined-glob.s @@ -0,0 +1,58 @@ +# REQUIRES: x86 + +# RUN: echo '.globl foo1; foo1:' | \ +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux - -o %t1.o +# RUN: echo '.globl foo2; foo2:' | \ +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux - -o %t2.o +# RUN: echo '.globl foo32; foo32:' | \ +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux - -o %t3.o +# RUN: echo '.globl bar; bar:' | \ +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux - -o %t4.o +# RUN: rm -f %t.a +# RUN: llvm-ar rcs %t.a %t1.o %t2.o %t3.o %t4.o + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o + +# RUN: ld.lld -o %t.exe %t.o %t.a +# RUN: llvm-readobj --symbols %t.exe | FileCheck --check-prefix=NO-OPT %s + +# NO-OPT-NOT: foo +# NO-OPT-NOT: bar + +# RUN: ld.lld -o %t.exe %t.o %t.a --undefined-glob foo1 +# RUN: llvm-readobj --symbols %t.exe | FileCheck --check-prefix=FOO1 %s + +# FOO1: foo1 +# FOO1-NOT: foo2 + +# RUN: ld.lld -o %t.exe %t.o %t.a --undefined-glob 'foo*' +# RUN: llvm-readobj --symbols %t.exe | FileCheck --check-prefix=FOO-STAR %s + +# FOO-STAR: foo1 +# FOO-STAR: foo2 +# FOO-STAR: foo32 +# FOO-STAR-NOT: bar + +# RUN: ld.lld -o %t.exe %t.o %t.a --undefined-glob 'foo?' +# RUN: llvm-readobj --symbols %t.exe | FileCheck --check-prefix=FOO-Q %s + +# FOO-Q: foo1 +# FOO-Q: foo2 +# FOO-Q-NOT: foo32 +# FOO-Q-NOT: bar + +# RUN: ld.lld -o %t.exe %t.o %t.a --undefined-glob 'foo[13]*' +# RUN: llvm-readobj --symbols %t.exe | FileCheck --check-prefix=FOO13 %s + +# FOO13: foo1 +# FOO13-NOT: foo2 +# FOO13: foo32 +# FOO13-NOT: bar + +# RUN: not ld.lld -o %t.exe %t.o %t.a --undefined-glob '[' 2>&1 | \ +# RUN: FileCheck -check-prefix=BAD-PATTERN %s + +# BAD-PATTERN: --undefined-glob: invalid glob pattern: [ + +.globl _start +_start: