diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -17,6 +17,7 @@ #include "llvm/Support/CachePruning.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/Endian.h" +#include "llvm/Support/GlobPattern.h" #include #include @@ -201,6 +202,7 @@ bool unique; bool useAndroidRelrTags = false; bool warnBackrefs; + std::vector warnBackrefsExclude; bool warnCommon; bool warnIfuncTextrel; bool warnMissingEntry; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1148,6 +1148,14 @@ {s, /*isExternCpp=*/false, /*hasWildcard=*/false}); } + for (opt::Arg *arg : args.filtered(OPT_warn_backrefs_exclude)) { + StringRef pattern(arg->getValue()); + if (Expected pat = GlobPattern::create(pattern)) + config->warnBackrefsExclude.push_back(std::move(*pat)); + else + error(arg->getSpelling() + ": " + toString(pat.takeError())); + } + // Parses -dynamic-list and -export-dynamic-symbol. They make some // symbols private. Note that -export-dynamic takes precedence over them // as it says all symbols should be exported. diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -403,6 +403,12 @@ "Warn about backward symbol references to fetch archive members", "Do not warn about backward symbol references to fetch archive members (default)">; +defm warn_backrefs_exclude + : Eq<"warn-backrefs-exclude", + "Glob describing an archive (or an object file within --start-lib) " + "which should be ignored for --warn-backrefs.">, + MetaVarName<"">; + defm warn_common: B<"warn-common", "Warn about duplicate common symbols", "Do not warn about duplicate common symbols (default)">; diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -16,6 +16,7 @@ #include "lld/Common/ErrorHandler.h" #include "lld/Common/Strings.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include @@ -514,6 +515,17 @@ // group assignment rule simulates the traditional linker's semantics. bool backref = config->warnBackrefs && other.file && file->groupId < other.file->groupId; + if (backref) { + // Some libraries have known problems and can cause noise. Filter them out + // with --warn-backrefs-exclude=. + StringRef name = + !file->archiveName.empty() ? file->archiveName : file->getName(); + for (const llvm::GlobPattern &pat : config->warnBackrefsExclude) + if (pat.match(name)) { + backref = false; + break; + } + } fetch(); // We don't report backward references to weak symbols as they can be 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 @@ -574,6 +574,10 @@ Warn about reverse or cyclic dependencies to or between static archives. This can be used to ensure linker invocation remains compatible with traditional Unix-like linkers. +.It Fl -warn-backrefs-exclude Ns = Ns Ar glob +Glob describing an archive (or an object file within --start-lib) +which should be ignored for +.Fl -warn-backrefs .It Fl -warn-common Warn about duplicate common symbols. .It Fl -warn-ifunc-textrel diff --git a/lld/test/ELF/warn-backrefs.s b/lld/test/ELF/warn-backrefs.s --- a/lld/test/ELF/warn-backrefs.s +++ b/lld/test/ELF/warn-backrefs.s @@ -15,9 +15,16 @@ # RUN: ld.lld --fatal-warnings --warn-backrefs %t1.lds -o /dev/null ## A backward reference from %t1.o to %t2.a +## Warn unless the archive is excluded by --warn-backrefs-exclude # RUN: ld.lld --fatal-warnings %t2.a %t1.o -o /dev/null # RUN: ld.lld --warn-backrefs %t2.a %t1.o -o /dev/null 2>&1 | FileCheck %s # RUN: ld.lld --warn-backrefs %t2.a '-(' %t1.o '-)' -o /dev/null 2>&1 | FileCheck %s +# RUN: ld.lld --warn-backrefs --warn-backrefs-exclude='*3.a' %t2.a %t1.o -o /dev/null 2>&1 | FileCheck %s +# RUN: ld.lld --fatal-warnings --warn-backrefs --warn-backrefs-exclude='*2.a' %t2.a %t1.o -o /dev/null +# RUN: ld.lld --fatal-warnings --warn-backrefs --warn-backrefs-exclude '*2.a' \ +# RUN: --warn-backrefs-exclude not_exist %t2.a %t1.o -o /dev/null +## Without --warn-backrefs, --warn-backrefs-exclude is ignored. +# RUN: ld.lld --fatal-warnings --warn-backrefs-exclude=not_exist %t2.a %t1.o -o /dev/null ## Placing the definition and the backward reference in a group can suppress the warning. # RUN: echo 'GROUP("%t2.a" "%t1.o")' > %t2.lds @@ -28,14 +35,17 @@ # RUN: echo 'GROUP("%t2.a")' > %t3.lds # RUN: ld.lld --warn-backrefs %t3.lds %t1.o -o /dev/null 2>&1 | FileCheck %s # RUN: ld.lld --fatal-warnings --warn-backrefs '-(' %t3.lds %t1.o '-)' -o /dev/null +# RUN: ld.lld --fatal-warnings --warn-backrefs --warn-backrefs-exclude='*2.a' -o /dev/null %t3.lds %t1.o ## If a lazy definition appears after the backward reference, don't warn. # RUN: ld.lld --fatal-warnings --warn-backrefs %t3.lds %t1.o %t3.lds -o /dev/null # CHECK: warning: backward reference detected: foo in {{.*}}1.o refers to {{.*}}2.a ## A backward reference from %t1.o to %t2.o +## --warn-backrefs-exclude= applies to --start-lib covered object files. # RUN: ld.lld --warn-backrefs --start-lib %t2.o --end-lib %t1.o -o /dev/null 2>&1 | \ # RUN: FileCheck --check-prefix=OBJECT %s +# RUN: ld.lld --fatal-warnings --warn-backrefs --warn-backrefs-exclude=%/t2.o --start-lib %/t2.o --end-lib %t1.o -o /dev/null ## If a lazy definition appears after the backward reference, don't warn. # RUN: ld.lld --fatal-warnings --warn-backrefs --start-lib %t2.o --end-lib %t1.o --start-lib %t2.o --end-lib -o /dev/null @@ -68,6 +78,9 @@ ## In GNU gold, --export-dynamic-symbol does not make a backward reference. # RUN: ld.lld --fatal-warnings --warn-backrefs --export-dynamic-symbol foo %t2.a %t1.o -o /dev/null +# RUN: not ld.lld --warn-backrefs-exclude='[' 2>&1 | FileCheck --check-prefix=INVALID %s +# INVALID: error: --warn-backrefs-exclude: invalid glob pattern: [ + .globl _start, foo _start: call foo