Index: lld/trunk/ELF/Error.cpp =================================================================== --- lld/trunk/ELF/Error.cpp +++ lld/trunk/ELF/Error.cpp @@ -33,6 +33,18 @@ // but outs() or errs() are not thread-safe. We protect them using a mutex. static std::mutex Mu; +// Prints "\n" or does nothing, depending on Msg contents of +// the previous call of this function. +static void newline(const Twine &Msg) { + // True if the previous error message contained "\n". + // We want to separate multi-line error messages with a newline. + static bool Flag; + + if (Flag) + *ErrorOS << "\n"; + Flag = (StringRef(Msg.str()).find('\n') != StringRef::npos); +} + static void print(StringRef S, raw_ostream::Colors C) { *ErrorOS << Argv0 + ": "; if (Config->ColorDiagnostics) { @@ -62,13 +74,16 @@ error(Msg); return; } + std::lock_guard Lock(Mu); + newline(Msg); print("warning: ", raw_ostream::MAGENTA); *ErrorOS << Msg << "\n"; } void elf::error(const Twine &Msg) { std::lock_guard Lock(Mu); + newline(Msg); if (Config->ErrorLimit == 0 || ErrorCount < Config->ErrorLimit) { print("error: ", raw_ostream::RED); @@ -96,8 +111,6 @@ } void elf::fatal(const Twine &Msg) { - std::lock_guard Lock(Mu); - print("error: ", raw_ostream::RED); - *ErrorOS << Msg << "\n"; + error(Msg); exitLld(1); } Index: lld/trunk/ELF/InputFiles.h =================================================================== --- lld/trunk/ELF/InputFiles.h +++ lld/trunk/ELF/InputFiles.h @@ -30,6 +30,7 @@ namespace llvm { class DWARFDebugLine; class TarWriter; +struct DILineInfo; namespace lto { class InputFile; } @@ -175,6 +176,7 @@ // Returns source line information for a given offset. // If no information is available, returns "". std::string getLineInfo(InputSectionBase *S, uint64_t Offset); + llvm::Optional getDILineInfo(InputSectionBase *, uint64_t); // MIPS GP0 value defined by this file. This value represents the gp value // used to create the relocatable object and required to support Index: lld/trunk/ELF/InputFiles.cpp =================================================================== --- lld/trunk/ELF/InputFiles.cpp +++ lld/trunk/ELF/InputFiles.cpp @@ -92,15 +92,15 @@ // Returns source line information for a given offset // using DWARF debug info. template -std::string elf::ObjectFile::getLineInfo(InputSectionBase *S, - uint64_t Offset) { +Optional elf::ObjectFile::getDILineInfo(InputSectionBase *S, + uint64_t Offset) { if (!DwarfLine) initializeDwarfLine(); // The offset to CU is 0. const DWARFDebugLine::LineTable *Tbl = DwarfLine->getLineTable(0); if (!Tbl) - return ""; + return None; // Use fake address calcuated by adding section file offset and offset in // section. See comments for ObjectInfo class. @@ -109,8 +109,18 @@ S->getOffsetInFile() + Offset, nullptr, DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, Info); if (Info.Line == 0) - return ""; - return Info.FileName + ":" + std::to_string(Info.Line); + return None; + return Info; +} + +// Returns source line information for a given offset +// using DWARF debug info. +template +std::string elf::ObjectFile::getLineInfo(InputSectionBase *S, + uint64_t Offset) { + if (Optional Info = getDILineInfo(S, Offset)) + return Info->FileName + ":" + std::to_string(Info->Line); + return ""; } // Returns "(internal)", "foo.a(bar.o)" or "baz.o". Index: lld/trunk/ELF/InputSection.h =================================================================== --- lld/trunk/ELF/InputSection.h +++ lld/trunk/ELF/InputSection.h @@ -163,6 +163,8 @@ // Returns a source location string. Used to construct an error message. template std::string getLocation(uint64_t Offset); + template std::string getSrcMsg(uint64_t Offset); + template std::string getObjMsg(uint64_t Offset); template void relocate(uint8_t *Buf, uint8_t *BufEnd); Index: lld/trunk/ELF/InputSection.cpp =================================================================== --- lld/trunk/ELF/InputSection.cpp +++ lld/trunk/ELF/InputSection.cpp @@ -22,6 +22,7 @@ #include "llvm/Object/Decompressor.h" #include "llvm/Support/Compression.h" #include "llvm/Support/Endian.h" +#include "llvm/Support/Path.h" #include using namespace llvm; @@ -29,6 +30,7 @@ using namespace llvm::object; using namespace llvm::support; using namespace llvm::support::endian; +using namespace llvm::sys; using namespace lld; using namespace lld::elf; @@ -215,6 +217,62 @@ return (SrcFile + ":(" + Name + "+0x" + utohexstr(Offset) + ")").str(); } +// Returns a source location string. This function is intended to be +// used for constructing an error message. The returned message looks +// like this: +// +// foo.c:42 (/home/alice/possibly/very/long/path/foo.c:42) +// +// Returns an empty string if there's no way to get line info. +template std::string InputSectionBase::getSrcMsg(uint64_t Offset) { + // Synthetic sections don't have input files. + elf::ObjectFile *File = getFile(); + if (!File) + return ""; + + Optional Info = File->getDILineInfo(this, Offset); + + // File->SourceFile contains STT_FILE symbol, and that is a last resort. + if (!Info) + return File->SourceFile; + + std::string Path = Info->FileName; + std::string Filename = path::filename(Path); + std::string Lineno = ":" + std::to_string(Info->Line); + if (Filename == Path) + return Filename + Lineno; + return Filename + Lineno + " (" + Path + Lineno + ")"; +} + +// Returns a filename string along with an optional section name. This +// function is intended to be used for constructing an error +// message. The returned message looks like this: +// +// path/to/foo.o:(function bar) +// +// or +// +// path/to/foo.o:(function bar) in archive path/to/bar.a +template std::string InputSectionBase::getObjMsg(uint64_t Off) { + // Synthetic sections don't have input files. + elf::ObjectFile *File = getFile(); + std::string Filename = File ? File->getName() : "(internal)"; + + std::string Archive; + if (!File->ArchiveName.empty()) + Archive = (" in archive " + File->ArchiveName).str(); + + // Find a symbol that encloses a given location. + for (SymbolBody *B : getFile()->getSymbols()) + if (auto *D = dyn_cast(B)) + if (D->Section == this && D->Value <= Off && Off < D->Value + D->Size) + return Filename + ":(" + toString(*D) + ")" + Archive; + + // If there's no symbol, print out the offset in the section. + return (Filename + ":(" + Name + "+0x" + utohexstr(Off) + ")" + Archive) + .str(); +} + InputSectionBase InputSectionBase::Discarded; InputSection::InputSection(uint64_t Flags, uint32_t Type, uint32_t Alignment, @@ -833,6 +891,16 @@ template std::string InputSectionBase::getLocation(uint64_t); template std::string InputSectionBase::getLocation(uint64_t); +template std::string InputSectionBase::getSrcMsg(uint64_t); +template std::string InputSectionBase::getSrcMsg(uint64_t); +template std::string InputSectionBase::getSrcMsg(uint64_t); +template std::string InputSectionBase::getSrcMsg(uint64_t); + +template std::string InputSectionBase::getObjMsg(uint64_t); +template std::string InputSectionBase::getObjMsg(uint64_t); +template std::string InputSectionBase::getObjMsg(uint64_t); +template std::string InputSectionBase::getObjMsg(uint64_t); + template void InputSection::writeTo(uint8_t *); template void InputSection::writeTo(uint8_t *); template void InputSection::writeTo(uint8_t *); Index: lld/trunk/ELF/Relocations.cpp =================================================================== --- lld/trunk/ELF/Relocations.cpp +++ lld/trunk/ELF/Relocations.cpp @@ -616,14 +616,20 @@ if (Config->UnresolvedSymbols == UnresolvedPolicy::Ignore && CanBeExternal) return; - std::string Msg = S.getLocation(Offset) + ": undefined symbol '" + - toString(Sym) + "'"; + std::string Msg = + "undefined symbol: " + toString(Sym) + "\n>>> referenced by "; + + std::string Src = S.getSrcMsg(Offset); + if (!Src.empty()) + Msg += Src + "\n>>> "; + Msg += S.getObjMsg(Offset); if (Config->UnresolvedSymbols == UnresolvedPolicy::WarnAll || (Config->UnresolvedSymbols == UnresolvedPolicy::Warn && CanBeExternal)) { warn(Msg); } else { error(Msg); + if (Config->ArchiveWithoutSymbolsSeen) { message("At least one archive listed no symbols in its index." " This can happen when creating archives with a version" Index: lld/trunk/test/ELF/libsearch.s =================================================================== --- lld/trunk/test/ELF/libsearch.s +++ lld/trunk/test/ELF/libsearch.s @@ -22,7 +22,8 @@ // Should not link because of undefined symbol _bar // RUN: not ld.lld -o %t3 %t.o %tbar.o 2>&1 \ // RUN: | FileCheck --check-prefix=UNDEFINED %s -// UNDEFINED: error: {{.*}}:(.bar+0x0): undefined symbol '_bar' +// UNDEFINED: error: undefined symbol: _bar +// UNDEFINED: >>> referenced by {{.*}}:(.bar+0x0) // Should fail if cannot find specified library (without -L switch) // RUN: not ld.lld -o %t3 %t.o -lls 2>&1 \ Index: lld/trunk/test/ELF/linkerscript/edata-etext.s =================================================================== --- lld/trunk/test/ELF/linkerscript/edata-etext.s +++ lld/trunk/test/ELF/linkerscript/edata-etext.s @@ -2,9 +2,12 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o # RUN: echo "SECTIONS { }" > %t.script # RUN: not ld.lld %t.o -script %t.script -o %t 2>&1 | FileCheck %s -# CHECK: error: {{.*}}:(.text+0x0): undefined symbol '_edata' -# CHECK: error: {{.*}}:(.text+0x8): undefined symbol '_etext' -# CHECK: error: {{.*}}:(.text+0x10): undefined symbol '_end' +# CHECK: error: undefined symbol: _edata +# CHECK: >>> referenced by {{.*}}:(.text+0x0) +# CHECK: error: undefined symbol: _etext +# CHECK: >>> referenced by {{.*}}:(.text+0x8) +# CHECK: error: undefined symbol: _end +# CHECK: >>> referenced by {{.*}}:(.text+0x10) .global _start,_end,_etext,_edata .text Index: lld/trunk/test/ELF/linkerscript/ehdr_start.s =================================================================== --- lld/trunk/test/ELF/linkerscript/ehdr_start.s +++ lld/trunk/test/ELF/linkerscript/ehdr_start.s @@ -3,7 +3,8 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o # RUN: echo "SECTIONS { }" > %t.script # RUN: not ld.lld %t.o -script %t.script -o %t 2>&1 | FileCheck %s -# CHECK: error: {{.*}}:(.text+0x0): undefined symbol '__ehdr_start' +# CHECK: error: undefined symbol: __ehdr_start +# CHECK: >>> referenced by {{.*}}:(.text+0x0) .text .global _start, __ehdr_start Index: lld/trunk/test/ELF/lto/archive-no-index.ll =================================================================== --- lld/trunk/test/ELF/lto/archive-no-index.ll +++ lld/trunk/test/ELF/lto/archive-no-index.ll @@ -22,10 +22,10 @@ ; RUN: not ld.lld -emain -m elf_x86_64 %t.o -o %t %T/archive-no-index/libfoo.a \ ; RUN: 2>&1 | FileCheck --check-prefix=NO-NOTE %s -; NOTE: undefined symbol 'f' +; NOTE: undefined symbol: f ; NOTE: archive listed no symbols -; NO-NOTE: undefined symbol 'f' +; NO-NOTE: undefined symbol: f ; NO-NOTE-NOT: archive listed no symbols target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" Index: lld/trunk/test/ELF/lto/combined-lto-object-name.ll =================================================================== --- lld/trunk/test/ELF/lto/combined-lto-object-name.ll +++ lld/trunk/test/ELF/lto/combined-lto-object-name.ll @@ -11,4 +11,6 @@ ret void } -; CHECK: error: ld-temp.o:(function _start): undefined symbol 'foo' +; CHECK: error: undefined symbol: foo +; CHECK: >>> referenced by ld-temp.o +; CHECK: {{.*}}:(_start) Index: lld/trunk/test/ELF/sysroot.s =================================================================== --- lld/trunk/test/ELF/sysroot.s +++ lld/trunk/test/ELF/sysroot.s @@ -9,7 +9,8 @@ // Should not link because of undefined symbol _bar // RUN: not ld.lld -o %t/r %t/m.o 2>&1 \ // RUN: | FileCheck --check-prefix=UNDEFINED %s -// UNDEFINED: error: {{.*}}:(.text+0x1): undefined symbol '_bar' +// UNDEFINED: error: undefined symbol: _bar +// UNDEFINED: >>> referenced by {{.*}}:(.text+0x1) // We need to be sure that there is no suitable library in the /lib directory // RUN: not ld.lld -o %t/r %t/m.o -L/lib -l:libls.a 2>&1 \ Index: lld/trunk/test/ELF/tls-static.s =================================================================== --- lld/trunk/test/ELF/tls-static.s +++ lld/trunk/test/ELF/tls-static.s @@ -10,4 +10,5 @@ _start: call __tls_get_addr -// CHECK: error: {{.*}}:(.text+0x1): undefined symbol '__tls_get_addr' +// CHECK: error: undefined symbol: __tls_get_addr +// CHECK: >>> referenced by {{.*}}:(.text+0x1) Index: lld/trunk/test/ELF/undef-shared.s =================================================================== --- lld/trunk/test/ELF/undef-shared.s +++ lld/trunk/test/ELF/undef-shared.s @@ -1,15 +1,18 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o # RUN: not ld.lld %t.o -o %t.so -shared 2>&1 | FileCheck %s -# CHECK: error: {{.*}}:(.data+0x0): undefined symbol 'hidden' +# CHECK: error: undefined symbol: hidden +# CHECK: >>> referenced by {{.*}}:(.data+0x0) .global hidden .hidden hidden -# CHECK: error: {{.*}}:(.data+0x8): undefined symbol 'internal' +# CHECK: error: undefined symbol: internal +# CHECK: >>> referenced by {{.*}}:(.data+0x8) .global internal .internal internal -# CHECK: error: {{.*}}:(.data+0x10): undefined symbol 'protected' +# CHECK: error: undefined symbol: protected +# CHECK: >>> referenced by {{.*}}:(.data+0x10) .global protected .protected protected Index: lld/trunk/test/ELF/undef.s =================================================================== --- lld/trunk/test/ELF/undef.s +++ lld/trunk/test/ELF/undef.s @@ -5,17 +5,37 @@ # RUN: llvm-ar rc %t2.a %t2.o # RUN: not ld.lld %t.o %t2.a %t3.o -o %t.exe 2>&1 | FileCheck %s # RUN: not ld.lld -pie %t.o %t2.a %t3.o -o %t.exe 2>&1 | FileCheck %s -# CHECK: error: undef.s:(.text+0x1): undefined symbol 'foo' -# CHECK: error: undef.s:(.text+0x6): undefined symbol 'bar' -# CHECK: error: undef.s:(.text+0x10): undefined symbol 'foo(int)' -# CHECK: error: {{.*}}2.a({{.*}}.o):(.text+0x0): undefined symbol 'zed2' -# CHECK: error: dir{{/|\\}}undef-debug.s:3: undefined symbol 'zed3' -# CHECK: error: dir{{/|\\}}undef-debug.s:7: undefined symbol 'zed4' -# CHECK: error: dir{{/|\\}}undef-debug.s:11: undefined symbol 'zed5' + +# CHECK: error: undefined symbol: foo +# CHECK: >>> referenced by undef.s +# CHECK: {{.*}}:(.text+0x1) + +# CHECK: error: undefined symbol: bar +# CHECK: >>> referenced by undef.s +# CHECK: >>> {{.*}}:(.text+0x6) + +# CHECK: error: undefined symbol: foo(int) +# CHECK: >>> referenced by undef.s +# CHECK: >>> {{.*}}:(.text+0x10) + +# CHECK: error: undefined symbol: zed2 +# CHECK: >>> referenced by {{.*}}.o:(.text+0x0) in archive {{.*}}2.a + +# CHECK: error: undefined symbol: zed3 +# CHECK: >>> referenced by undef-debug.s:3 (dir{{/|\\}}undef-debug.s:3) +# CHECK: >>> {{.*}}.o:(.text+0x0) + +# CHECK: error: undefined symbol: zed4 +# CHECK: >>> referenced by undef-debug.s:7 (dir{{/|\\}}undef-debug.s:7) +# CHECK: >>> {{.*}}.o:(.text.1+0x0) + +# CHECK: error: undefined symbol: zed5 +# CHECK: >>> referenced by undef-debug.s:11 (dir{{/|\\}}undef-debug.s:11) +# CHECK: >>> {{.*}}.o:(.text.2+0x0) # RUN: not ld.lld %t.o %t2.a -o %t.exe -no-demangle 2>&1 | \ # RUN: FileCheck -check-prefix=NO-DEMANGLE %s -# NO-DEMANGLE: error: undef.s:(.text+0x10): undefined symbol '_Z3fooi' +# NO-DEMANGLE: error: undefined symbol: _Z3fooi .file "undef.s" Index: lld/trunk/test/ELF/unresolved-symbols.s =================================================================== --- lld/trunk/test/ELF/unresolved-symbols.s +++ lld/trunk/test/ELF/unresolved-symbols.s @@ -6,7 +6,8 @@ ## Check that %t2.o contains undefined symbol undef. # RUN: not ld.lld %t1.o %t2.o -o %t 2>&1 | \ # RUN: FileCheck -check-prefix=UNDCHECK %s -# UNDCHECK: error: {{.*}}2.o:(.text+0x1): undefined symbol 'undef' +# UNDCHECK: error: undefined symbol: undef +# UNDCHECK: >>> referenced by {{.*}}2.o:(.text+0x1) ## Error out if unknown option value was set. # RUN: not ld.lld %t1.o %t2.o -o %t --unresolved-symbols=xxx 2>&1 | \ @@ -19,7 +20,9 @@ # RUN: llvm-readobj %t1_1 > /dev/null 2>&1 # RUN: not ld.lld %t2.o -o %t1_2 --unresolved-symbols=ignore-all --no-undefined 2>&1 | \ # RUN: FileCheck -check-prefix=ERRUND %s -# ERRUND: error: {{.*}}:(.text+0x1): undefined symbol 'undef' +# ERRUND: error: undefined symbol: undef +# ERRUND: >>> referenced by {{.*}}:(.text+0x1) + ## Also ignore all should not produce error for symbols from DSOs. # RUN: ld.lld %t1.o %t.so -o %t1_3 --unresolved-symbols=ignore-all # RUN: llvm-readobj %t1_3 > /dev/null 2>&1 @@ -56,8 +59,7 @@ # RUN: llvm-readobj %t6_1 > /dev/null 2>&1 # RUN: not ld.lld %t2.o -o %t7 --unresolved-symbols=report-all 2>&1 | \ # RUN: FileCheck -check-prefix=ERRUND %s -# RUN: not ld.lld %t2.o -o %t7_1 2>&1 | \ -# RUN: FileCheck -check-prefix=ERRUND %s +# RUN: not ld.lld %t2.o -o %t7_1 2>&1 | FileCheck -check-prefix=ERRUND %s .globl _start _start: Index: lld/trunk/test/ELF/verneed-local.s =================================================================== --- lld/trunk/test/ELF/verneed-local.s +++ lld/trunk/test/ELF/verneed-local.s @@ -2,7 +2,8 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o # RUN: not ld.lld %t.o %S/Inputs/verneed1.so -o %t 2>&1 | FileCheck %s -# CHECK: error: {{.*}}:(.text+0x1): undefined symbol 'f3' +# CHECK: error: undefined symbol: f3 +# CHECK: >>> referenced by {{.*}}:(.text+0x1) .globl _start _start: call f3 Index: lld/trunk/test/ELF/warn-unresolved-symbols-hidden.s =================================================================== --- lld/trunk/test/ELF/warn-unresolved-symbols-hidden.s +++ lld/trunk/test/ELF/warn-unresolved-symbols-hidden.s @@ -2,13 +2,13 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o # RUN: not ld.lld -shared %t.o -o %t.so -z defs --warn-unresolved-symbols 2>&1| FileCheck %s -# CHECK: warning: {{.*}}: undefined symbol 'foo' -# CHECK: error: {{.*}}: undefined symbol 'bar' -# CHECK: error: {{.*}}: undefined symbol 'zed' +# CHECK: warning: undefined symbol: foo +# CHECK: error: undefined symbol: bar +# CHECK: error: undefined symbol: zed - .data - .quad foo - .hidden bar - .quad bar - .protected zed - .quad zed +.data +.quad foo +.hidden bar +.quad bar +.protected zed +.quad zed Index: lld/trunk/test/ELF/warn-unresolved-symbols.s =================================================================== --- lld/trunk/test/ELF/warn-unresolved-symbols.s +++ lld/trunk/test/ELF/warn-unresolved-symbols.s @@ -31,10 +31,17 @@ # RUN: ld.lld -shared %t1.o -o %t10.so --warn-unresolved-symbols 2>&1 | \ # RUN: FileCheck -allow-empty -check-prefix=NOWARN %s -# ERRUND: error: {{.*}}:(.text+0x1): undefined symbol 'undef' -# WARNUND: warning: {{.*}}:(.text+0x1): undefined symbol 'undef' -# NOERR-NOT: error: {{.*}}:(.text+0x1): undefined symbol 'undef' -# NOWARN-NOT: warning: {{.*}}:(.text+0x1): undefined symbol 'undef' +# ERRUND: error: undefined symbol: undef +# ERRUND: >>> referenced by {{.*}}:(.text+0x1) + +# WARNUND: warning: undefined symbol: undef +# WARNUND: >>> referenced by {{.*}}:(.text+0x1) + +# NOERR-NOT: error: undefined symbol: undef +# NOERR-NOT: >>> referenced by {{.*}}:(.text+0x1) + +# NOWARN-NOT: warning: undefined symbol: undef +# NOWARN-NOT: >>> referenced by {{.*}}:(.text+0x1) .globl _start _start: Index: lld/trunk/test/ELF/zdefs.s =================================================================== --- lld/trunk/test/ELF/zdefs.s +++ lld/trunk/test/ELF/zdefs.s @@ -2,6 +2,7 @@ # RUN: ld.lld -shared %t.o -o %t1.so # RUN: not ld.lld -z defs -shared %t.o -o %t1.so 2>&1 | FileCheck -check-prefix=ERR %s -# ERR: error: {{.*}}:(.text+0x1): undefined symbol 'foo' +# ERR: error: undefined symbol: foo +# ERR: >>> referenced by {{.*}}:(.text+0x1) callq foo@PLT