Skip to content

Commit

Permalink
Change the error message format for undefined symbols.
Browse files Browse the repository at this point in the history
Previously, undefined symbol errors are one line like this
and wasn't easy to read.

  /ssd/clang/bin/ld.lld: error: /ssd/llvm-project/lld/ELF/Writer.cpp:207: undefined symbol 'lld::elf::EhFrameSection<llvm::object::ELFType<(llvm::support::endianness)0, true> >::addSection(lld::elf::InputSectionBase*)'

This patch make it more structured like this.

  bin/ld.lld: error: undefined symbol: lld::elf::EhFrameSection<llvm::object::ELFType<(llvm::support::endianness)0, true>
  >>> Referenced by Writer.cpp:207 (/ssd/llvm-project/lld/ELF/Writer.cpp:207)
  >>>               Writer.cpp.o in archive lib/liblldELF.a

Discussion thread:
http://lists.llvm.org/pipermail/llvm-dev/2017-March/111459.html

Differential Revision: https://reviews.llvm.org/D31481

llvm-svn: 299097
  • Loading branch information
rui314 committed Mar 30, 2017
1 parent edaec6d commit b876020
Show file tree
Hide file tree
Showing 20 changed files with 194 additions and 50 deletions.
19 changes: 16 additions & 3 deletions lld/ELF/Error.cpp
Expand Up @@ -33,6 +33,18 @@ StringRef elf::Argv0;
// 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) {
Expand Down Expand Up @@ -62,13 +74,16 @@ void elf::warn(const Twine &Msg) {
error(Msg);
return;
}

std::lock_guard<std::mutex> Lock(Mu);
newline(Msg);
print("warning: ", raw_ostream::MAGENTA);
*ErrorOS << Msg << "\n";
}

void elf::error(const Twine &Msg) {
std::lock_guard<std::mutex> Lock(Mu);
newline(Msg);

if (Config->ErrorLimit == 0 || ErrorCount < Config->ErrorLimit) {
print("error: ", raw_ostream::RED);
Expand Down Expand Up @@ -96,8 +111,6 @@ void elf::exitLld(int Val) {
}

void elf::fatal(const Twine &Msg) {
std::lock_guard<std::mutex> Lock(Mu);
print("error: ", raw_ostream::RED);
*ErrorOS << Msg << "\n";
error(Msg);
exitLld(1);
}
20 changes: 15 additions & 5 deletions lld/ELF/InputFiles.cpp
Expand Up @@ -92,15 +92,15 @@ template <class ELFT> void elf::ObjectFile<ELFT>::initializeDwarfLine() {
// Returns source line information for a given offset
// using DWARF debug info.
template <class ELFT>
std::string elf::ObjectFile<ELFT>::getLineInfo(InputSectionBase *S,
uint64_t Offset) {
Optional<DILineInfo> elf::ObjectFile<ELFT>::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.
Expand All @@ -109,8 +109,18 @@ std::string elf::ObjectFile<ELFT>::getLineInfo(InputSectionBase *S,
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 <class ELFT>
std::string elf::ObjectFile<ELFT>::getLineInfo(InputSectionBase *S,
uint64_t Offset) {
if (Optional<DILineInfo> Info = getDILineInfo(S, Offset))
return Info->FileName + ":" + std::to_string(Info->Line);
return "";
}

// Returns "(internal)", "foo.a(bar.o)" or "baz.o".
Expand Down
2 changes: 2 additions & 0 deletions lld/ELF/InputFiles.h
Expand Up @@ -30,6 +30,7 @@
namespace llvm {
class DWARFDebugLine;
class TarWriter;
struct DILineInfo;
namespace lto {
class InputFile;
}
Expand Down Expand Up @@ -175,6 +176,7 @@ template <class ELFT> class ObjectFile : public ELFFileBase<ELFT> {
// Returns source line information for a given offset.
// If no information is available, returns "".
std::string getLineInfo(InputSectionBase *S, uint64_t Offset);
llvm::Optional<llvm::DILineInfo> 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
Expand Down
68 changes: 68 additions & 0 deletions lld/ELF/InputSection.cpp
Expand Up @@ -22,13 +22,15 @@
#include "llvm/Object/Decompressor.h"
#include "llvm/Support/Compression.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Path.h"
#include <mutex>

using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
using namespace llvm::support;
using namespace llvm::support::endian;
using namespace llvm::sys;

using namespace lld;
using namespace lld::elf;
Expand Down Expand Up @@ -215,6 +217,62 @@ std::string InputSectionBase::getLocation(uint64_t Offset) {
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 <class ELFT> std::string InputSectionBase::getSrcMsg(uint64_t Offset) {
// Synthetic sections don't have input files.
elf::ObjectFile<ELFT> *File = getFile<ELFT>();
if (!File)
return "";

Optional<DILineInfo> 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 <class ELFT> std::string InputSectionBase::getObjMsg(uint64_t Off) {
// Synthetic sections don't have input files.
elf::ObjectFile<ELFT> *File = getFile<ELFT>();
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<ELFT>()->getSymbols())
if (auto *D = dyn_cast<DefinedRegular>(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,
Expand Down Expand Up @@ -833,6 +891,16 @@ template std::string InputSectionBase::getLocation<ELF32BE>(uint64_t);
template std::string InputSectionBase::getLocation<ELF64LE>(uint64_t);
template std::string InputSectionBase::getLocation<ELF64BE>(uint64_t);

template std::string InputSectionBase::getSrcMsg<ELF32LE>(uint64_t);
template std::string InputSectionBase::getSrcMsg<ELF32BE>(uint64_t);
template std::string InputSectionBase::getSrcMsg<ELF64LE>(uint64_t);
template std::string InputSectionBase::getSrcMsg<ELF64BE>(uint64_t);

template std::string InputSectionBase::getObjMsg<ELF32LE>(uint64_t);
template std::string InputSectionBase::getObjMsg<ELF32BE>(uint64_t);
template std::string InputSectionBase::getObjMsg<ELF64LE>(uint64_t);
template std::string InputSectionBase::getObjMsg<ELF64BE>(uint64_t);

template void InputSection::writeTo<ELF32LE>(uint8_t *);
template void InputSection::writeTo<ELF32BE>(uint8_t *);
template void InputSection::writeTo<ELF64LE>(uint8_t *);
Expand Down
2 changes: 2 additions & 0 deletions lld/ELF/InputSection.h
Expand Up @@ -163,6 +163,8 @@ class InputSectionBase : public SectionBase {

// Returns a source location string. Used to construct an error message.
template <class ELFT> std::string getLocation(uint64_t Offset);
template <class ELFT> std::string getSrcMsg(uint64_t Offset);
template <class ELFT> std::string getObjMsg(uint64_t Offset);

template <class ELFT> void relocate(uint8_t *Buf, uint8_t *BufEnd);

Expand Down
10 changes: 8 additions & 2 deletions lld/ELF/Relocations.cpp
Expand Up @@ -616,14 +616,20 @@ static void reportUndefined(SymbolBody &Sym, InputSectionBase &S,
if (Config->UnresolvedSymbols == UnresolvedPolicy::Ignore && CanBeExternal)
return;

std::string Msg = S.getLocation<ELFT>(Offset) + ": undefined symbol '" +
toString(Sym) + "'";
std::string Msg =
"undefined symbol: " + toString(Sym) + "\n>>> referenced by ";

std::string Src = S.getSrcMsg<ELFT>(Offset);
if (!Src.empty())
Msg += Src + "\n>>> ";
Msg += S.getObjMsg<ELFT>(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"
Expand Down
3 changes: 2 additions & 1 deletion lld/test/ELF/libsearch.s
Expand Up @@ -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 \
Expand Down
9 changes: 6 additions & 3 deletions lld/test/ELF/linkerscript/edata-etext.s
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion lld/test/ELF/linkerscript/ehdr_start.s
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions lld/test/ELF/lto/archive-no-index.ll
Expand Up @@ -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"
Expand Down
4 changes: 3 additions & 1 deletion lld/test/ELF/lto/combined-lto-object-name.ll
Expand Up @@ -11,4 +11,6 @@ define void @_start() {
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)
3 changes: 2 additions & 1 deletion lld/test/ELF/sysroot.s
Expand Up @@ -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 \
Expand Down
3 changes: 2 additions & 1 deletion lld/test/ELF/tls-static.s
Expand Up @@ -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)
9 changes: 6 additions & 3 deletions lld/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

Expand Down
36 changes: 28 additions & 8 deletions lld/test/ELF/undef.s
Expand Up @@ -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"

Expand Down

0 comments on commit b876020

Please sign in to comment.