Index: ELF/SymbolTable.cpp =================================================================== --- ELF/SymbolTable.cpp +++ ELF/SymbolTable.cpp @@ -288,63 +288,6 @@ static uint8_t getVisibility(uint8_t StOther) { return StOther & 3; } -// Do extra check for --warn-backrefs. -// -// --warn-backrefs is an option to prevent an undefined reference from -// fetching an archive member written earlier in the command line. It can be -// used to keep your program compatible with GNU linkers after you switch to -// lld. I'll explain the feature and why you may find it useful in this -// comment. -// -// lld's symbol resolution semantics is more relaxed than traditional Unix -// linkers. For example, -// -// ld.lld foo.a bar.o -// -// succeeds even if bar.o contains an undefined symbol that have to be -// resolved by some object file in foo.a. Traditional Unix linkers don't -// allow this kind of backward reference, as they visit each file only once -// from left to right in the command line while resolving all undefined -// symbols at the moment of visiting. -// -// In the above case, since there's no undefined symbol when a linker visits -// foo.a, no files are pulled out from foo.a, and because the linker forgets -// about foo.a after visiting, it can't resolve undefined symbols in bar.o -// that could have been resolved otherwise. -// -// That lld accepts more relaxed form means that (besides it'd make more -// sense) you can accidentally write a command line or a build file that -// works only with lld, even if you have a plan to distribute it to wider -// users who may be using GNU linkers. With --warn-backrefs, you can detect -// a library order that doesn't work with other Unix linkers. -// -// The option is also useful to detect cyclic dependencies between static -// archives. Again, lld accepts -// -// ld.lld foo.a bar.a -// -// even if foo.a and bar.a depend on each other. With --warn-backrefs, it is -// handled as an error. -// -// Here is how the option works. We assign a group ID to each file. A file -// with a smaller group ID can pull out object files from an archive file -// with an equal or greater group ID. Otherwise, it is a reverse dependency -// and an error. -// -// A file outside --{start,end}-group gets a fresh ID when instantiated. All -// files within the same --{start,end}-group get the same group ID. E.g. -// -// ld.lld A B --start-group C D --end-group E -// -// A forms group 0. B form group 1. C and D (including their member object -// files) form group 2. E forms group 3. I think that you can see how this -// group assignment rule simulates the traditional linker's semantics. -static void checkBackrefs(StringRef Name, InputFile *Old, InputFile *New) { - if (Config->WarnBackrefs && New && Old->GroupId < New->GroupId) - warn("backward reference detected: " + Name + " in " + toString(New) + - " refers to " + toString(Old)); -} - template Symbol *SymbolTable::addUndefined(StringRef Name, uint8_t Binding, uint8_t StOther, uint8_t Type, @@ -377,8 +320,64 @@ return S; } - checkBackrefs(Name, S->File, File); + // Do extra check for --warn-backrefs. + // + // --warn-backrefs is an option to prevent an undefined reference from + // fetching an archive member written earlier in the command line. It can be + // used to keep compatibility with GNU linkers to some degree. + // I'll explain the feature and why you may find it useful in this comment. + // + // lld's symbol resolution semantics is more relaxed than traditional Unix + // linkers. For example, + // + // ld.lld foo.a bar.o + // + // succeeds even if bar.o contains an undefined symbol that has to be + // resolved by some object file in foo.a. Traditional Unix linkers don't + // allow this kind of backward reference, as they visit each file only once + // from left to right in the command line while resolving all undefined + // symbols at the moment of visiting. + // + // In the above case, since there's no undefined symbol when a linker visits + // foo.a, no files are pulled out from foo.a, and because the linker forgets + // about foo.a after visiting, it can't resolve undefined symbols in bar.o + // that could have been resolved otherwise. + // + // That lld accepts more relaxed form means that (besides it'd make more + // sense) you can accidentally write a command line or a build file that + // works only with lld, even if you have a plan to distribute it to wider + // users who may be using GNU linkers. With --warn-backrefs, you can detect + // a library order that doesn't work with other Unix linkers. + // + // The option is also useful to detect cyclic dependencies between static + // archives. Again, lld accepts + // + // ld.lld foo.a bar.a + // + // even if foo.a and bar.a depend on each other. With --warn-backrefs, it is + // handled as an error. + // + // Here is how the option works. We assign a group ID to each file. A file + // with a smaller group ID can pull out object files from an archive file + // with an equal or greater group ID. Otherwise, it is a reverse dependency + // and an error. + // + // A file outside --{start,end}-group gets a fresh ID when instantiated. All + // files within the same --{start,end}-group get the same group ID. E.g. + // + // ld.lld A B --start-group C D --end-group E + // + // A forms group 0. B form group 1. C and D (including their member object + // files) form group 2. E forms group 3. I think that you can see how this + // group assignment rule simulates the traditional linker's semantics. + bool Backref = + Config->WarnBackrefs && File && S->File->GroupId < File->GroupId; fetchLazy(S); + // We don't report backward references to weak symbols as they can be + // overriden later. + if (Backref && S->Binding != STB_WEAK) + warn("backward reference detected: " + Name + " in " + toString(File) + + " refers to " + toString(S->File)); } return S; } Index: test/ELF/warn-backrefs.s =================================================================== --- test/ELF/warn-backrefs.s +++ test/ELF/warn-backrefs.s @@ -39,6 +39,10 @@ # RUN: echo ".globl foo; foo: call bar" | llvm-mc -filetype=obj -triple=x86_64-unknown-linux - -o %t4.o # RUN: ld.lld --fatal-warnings --warn-backrefs %t1.o --start-lib %t3.o %t4.o --end-lib -o %t.exe +# We don't report backward references to weak symbols as they can be overriden later. +# RUN: echo ".weak foo; foo:" | llvm-mc -filetype=obj -triple=x86_64-unknown-linux - -o %t5.o +# RUN: ld.lld --fatal-warnings --warn-backrefs --start-lib %t5.o --end-lib %t1.o %t2.o -o %t.exe + .globl _start, foo _start: call foo