forked from OSchip/llvm-project
[ELF] Better error reporting for undefined symbols
This patch make lld show following details for undefined symbol errors: - file (line) - file (function name) - file (section name + offset) Differential revision: https://reviews.llvm.org/D25826 llvm-svn: 285186
This commit is contained in:
parent
9bcb064f19
commit
b380b24e6e
|
|
@ -18,6 +18,7 @@
|
|||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Bitcode/ReaderWriter.h"
|
||||
#include "llvm/CodeGen/Analysis.h"
|
||||
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/LTO/LTO.h"
|
||||
|
|
@ -35,6 +36,39 @@ using namespace lld::elf;
|
|||
|
||||
std::vector<InputFile *> InputFile::Pool;
|
||||
|
||||
template <class ELFT> DIHelper<ELFT>::DIHelper(elf::InputFile *F) {
|
||||
Expected<std::unique_ptr<object::ObjectFile>> Obj =
|
||||
object::ObjectFile::createObjectFile(F->MB);
|
||||
if (!Obj)
|
||||
return;
|
||||
|
||||
DWARFContextInMemory Dwarf(*Obj.get());
|
||||
DwarfLine.reset(new DWARFDebugLine(&Dwarf.getLineSection().Relocs));
|
||||
DataExtractor LineData(Dwarf.getLineSection().Data,
|
||||
ELFT::TargetEndianness == support::little,
|
||||
ELFT::Is64Bits ? 8 : 4);
|
||||
// The second parameter is offset in .debug_line section
|
||||
// for compilation unit (CU) of interest. We have only one
|
||||
// CU (object file), so offset is always 0.
|
||||
DwarfLine->getOrParseLineTable(LineData, 0);
|
||||
}
|
||||
|
||||
template <class ELFT> std::string DIHelper<ELFT>::getLineInfo(uintX_t Offset) {
|
||||
if (!DwarfLine)
|
||||
return "";
|
||||
|
||||
DILineInfo LineInfo;
|
||||
DILineInfoSpecifier Spec;
|
||||
// The offset to CU is 0 (see DIHelper constructor).
|
||||
const DWARFDebugLine::LineTable *LineTbl = DwarfLine->getLineTable(0);
|
||||
if (!LineTbl)
|
||||
return "";
|
||||
LineTbl->getFileLineInfoForAddress(Offset, nullptr, Spec.FLIKind, LineInfo);
|
||||
return LineInfo.Line != 0
|
||||
? LineInfo.FileName + " (" + std::to_string(LineInfo.Line) + ")"
|
||||
: "";
|
||||
}
|
||||
|
||||
// Deletes all InputFile instances created so far.
|
||||
void InputFile::freePool() {
|
||||
// Files are freed in reverse order so that files created
|
||||
|
|
@ -132,6 +166,13 @@ ArrayRef<SymbolBody *> elf::ObjectFile<ELFT>::getSymbols() {
|
|||
return makeArrayRef(this->SymbolBodies).slice(1);
|
||||
}
|
||||
|
||||
template <class ELFT> DIHelper<ELFT> *elf::ObjectFile<ELFT>::getDIHelper() {
|
||||
if (!DIH)
|
||||
DIH.reset(new DIHelper<ELFT>(this));
|
||||
|
||||
return DIH.get();
|
||||
}
|
||||
|
||||
template <class ELFT> uint32_t elf::ObjectFile<ELFT>::getMipsGp0() const {
|
||||
if (ELFT::Is64Bits && MipsOptions && MipsOptions->Reginfo)
|
||||
return MipsOptions->Reginfo->ri_gp_value;
|
||||
|
|
@ -432,6 +473,8 @@ SymbolBody *elf::ObjectFile<ELFT>::createSymbolBody(const Elf_Sym *Sym) {
|
|||
int Binding = Sym->getBinding();
|
||||
InputSectionBase<ELFT> *Sec = getSection(*Sym);
|
||||
if (Binding == STB_LOCAL) {
|
||||
if (Sym->getType() == STT_FILE)
|
||||
SourceFile = check(Sym->getName(this->StringTable));
|
||||
if (Sym->st_shndx == SHN_UNDEF)
|
||||
return new (this->Alloc)
|
||||
Undefined(Sym->st_name, Sym->st_other, Sym->getType(), this);
|
||||
|
|
@ -897,3 +940,8 @@ template InputFile *BinaryFile::createELF<ELF32LE>();
|
|||
template InputFile *BinaryFile::createELF<ELF32BE>();
|
||||
template InputFile *BinaryFile::createELF<ELF64LE>();
|
||||
template InputFile *BinaryFile::createELF<ELF64BE>();
|
||||
|
||||
template class elf::DIHelper<ELF32LE>;
|
||||
template class elf::DIHelper<ELF32BE>;
|
||||
template class elf::DIHelper<ELF64LE>;
|
||||
template class elf::DIHelper<ELF64BE>;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include <map>
|
||||
|
||||
namespace llvm {
|
||||
class DWARFDebugLine;
|
||||
namespace lto {
|
||||
class InputFile;
|
||||
}
|
||||
|
|
@ -43,6 +44,21 @@ class InputFile;
|
|||
class Lazy;
|
||||
class SymbolBody;
|
||||
|
||||
// Debugging information helper class. The main purpose is to
|
||||
// retrieve source file and line for error reporting. Linker may
|
||||
// find reasonable number of errors in a single object file, so
|
||||
// we cache debugging information in order to parse it only once
|
||||
// for each object file we link.
|
||||
template <class ELFT> class DIHelper {
|
||||
public:
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
|
||||
DIHelper(InputFile *F);
|
||||
std::string getLineInfo(uintX_t Offset);
|
||||
private:
|
||||
std::unique_ptr<llvm::DWARFDebugLine> DwarfLine;
|
||||
};
|
||||
|
||||
// The root class of input files.
|
||||
class InputFile {
|
||||
public:
|
||||
|
|
@ -171,6 +187,10 @@ public:
|
|||
|
||||
const Elf_Shdr *getSymbolTable() const { return this->Symtab; };
|
||||
|
||||
// DI helper allows manipilating debugging information for this
|
||||
// object file. Used for error reporting.
|
||||
DIHelper<ELFT> *getDIHelper();
|
||||
|
||||
// Get MIPS GP0 value defined by this file. This value represents the gp value
|
||||
// used to create the relocatable object and required to support
|
||||
// R_MIPS_GPREL16 / R_MIPS_GPREL32 relocations.
|
||||
|
|
@ -184,6 +204,11 @@ public:
|
|||
// using this buffer.
|
||||
llvm::BumpPtrAllocator Alloc;
|
||||
|
||||
// Name of source file obtained from STT_FILE symbol value,
|
||||
// or empty string if there is no such symbol in object file
|
||||
// symbol table.
|
||||
StringRef SourceFile;
|
||||
|
||||
private:
|
||||
void
|
||||
initializeSections(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
|
||||
|
|
@ -211,6 +236,7 @@ private:
|
|||
llvm::SpecificBumpPtrAllocator<InputSection<ELFT>> IAlloc;
|
||||
llvm::SpecificBumpPtrAllocator<MergeInputSection<ELFT>> MAlloc;
|
||||
llvm::SpecificBumpPtrAllocator<EhInputSection<ELFT>> EHAlloc;
|
||||
std::unique_ptr<DIHelper<ELFT>> DIH;
|
||||
};
|
||||
|
||||
// LazyObjectFile is analogous to ArchiveFile in the sense that
|
||||
|
|
|
|||
|
|
@ -522,7 +522,48 @@ static typename ELFT::uint computeAddend(const elf::ObjectFile<ELFT> &File,
|
|||
return Addend;
|
||||
}
|
||||
|
||||
static void reportUndefined(SymbolBody &Sym) {
|
||||
// Find symbol that encloses given offset. Used for error reporting.
|
||||
template <class ELFT>
|
||||
static DefinedRegular<ELFT> *getSymbolAt(InputSectionBase<ELFT> *S,
|
||||
typename ELFT::uint Offset) {
|
||||
for (SymbolBody *B : S->getFile()->getSymbols())
|
||||
if (auto *D = dyn_cast<DefinedRegular<ELFT>>(B))
|
||||
if (D->Value <= Offset && D->Value + D->Size > Offset && D->Section == S)
|
||||
return D;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static std::string getLocation(SymbolBody &Sym, InputSectionBase<ELFT> &S,
|
||||
typename ELFT::uint Offset) {
|
||||
ObjectFile<ELFT> *File = S.getFile();
|
||||
|
||||
// First check if we can get desired values from debugging information.
|
||||
std::string LineInfo = File->getDIHelper()->getLineInfo(Offset);
|
||||
if (!LineInfo.empty())
|
||||
return LineInfo;
|
||||
|
||||
// If don't have STT_FILE typed symbol in object file then
|
||||
// use object file name.
|
||||
std::string SrcFile = File->SourceFile;
|
||||
if (SrcFile.empty())
|
||||
SrcFile = Sym.File ? getFilename(Sym.File) : getFilename(File);
|
||||
|
||||
DefinedRegular<ELFT> *Encl = getSymbolAt(&S, Offset);
|
||||
if (Encl && Encl->Type == STT_FUNC) {
|
||||
StringRef Func = getSymbolName(*File, *Encl);
|
||||
return SrcFile + " (function " +
|
||||
(Config->Demangle ? demangle(Func) : Func.str()) + ")";
|
||||
}
|
||||
|
||||
return (SrcFile + " (" + S.Name + "+0x" + Twine::utohexstr(Offset) + ")")
|
||||
.str();
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static void reportUndefined(SymbolBody &Sym, InputSectionBase<ELFT> &S,
|
||||
typename ELFT::uint Offset) {
|
||||
if (Config->UnresolvedSymbols == UnresolvedPolicy::Ignore)
|
||||
return;
|
||||
|
||||
|
|
@ -530,11 +571,10 @@ static void reportUndefined(SymbolBody &Sym) {
|
|||
Config->UnresolvedSymbols != UnresolvedPolicy::NoUndef)
|
||||
return;
|
||||
|
||||
std::string Msg = "undefined symbol: ";
|
||||
Msg += Config->Demangle ? demangle(Sym.getName()) : Sym.getName().str();
|
||||
std::string Msg =
|
||||
getLocation(Sym, S, Offset) + ": undefined symbol '" +
|
||||
(Config->Demangle ? demangle(Sym.getName()) : Sym.getName().str()) + "'";
|
||||
|
||||
if (Sym.File)
|
||||
Msg += " in " + getFilename(Sym.File);
|
||||
if (Config->UnresolvedSymbols == UnresolvedPolicy::Warn)
|
||||
warn(Msg);
|
||||
else
|
||||
|
|
@ -583,7 +623,7 @@ static void scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
|
|||
// We only report undefined symbols if they are referenced somewhere in the
|
||||
// code.
|
||||
if (!Body.isLocal() && Body.isUndefined() && !Body.symbol()->isWeak())
|
||||
reportUndefined(Body);
|
||||
reportUndefined(Body, C, RI.r_offset);
|
||||
|
||||
RelExpr Expr = Target->getRelExpr(Type, Body);
|
||||
bool Preemptible = isPreemptible(Body, Type);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
.file 1 "undef-debug.s"
|
||||
.loc 1 3
|
||||
.quad zed3
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
// 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: undefined symbol: _bar
|
||||
// UNDEFINED: error: {{.*}} (.bar+0x0): undefined symbol '_bar'
|
||||
|
||||
// Should fail if cannot find specified library (without -L switch)
|
||||
// RUN: not ld.lld -o %t3 %t.o -lls 2>&1 \
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
# 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: undefined symbol: _edata
|
||||
# CHECK: undefined symbol: _etext
|
||||
# CHECK: undefined symbol: _end
|
||||
# CHECK: error: {{.*}} (.text+0x0): undefined symbol '_edata'
|
||||
# CHECK: error: {{.*}} (.text+0x8): undefined symbol '_etext'
|
||||
# CHECK: error: {{.*}} (.text+0x10): undefined symbol '_end'
|
||||
|
||||
.global _start,_end,_etext,_edata
|
||||
.text
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
# 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: undefined symbol: __ehdr_start
|
||||
# CHECK: error: {{.*}} (.text+0x0): undefined symbol '__ehdr_start'
|
||||
|
||||
.text
|
||||
.global _start, __ehdr_start
|
||||
|
|
|
|||
|
|
@ -11,4 +11,4 @@ define void @_start() {
|
|||
ret void
|
||||
}
|
||||
|
||||
; CHECK: undefined symbol: foo in {{.*}}combined-lto-object-name.ll.tmp.o
|
||||
; CHECK: error: ld-temp.o (function _start): undefined symbol 'foo'
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
// 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: undefined symbol: _bar
|
||||
// UNDEFINED: error: {{.*}} (.text+0x1): undefined symbol '_bar'
|
||||
|
||||
// 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 \
|
||||
|
|
|
|||
|
|
@ -10,4 +10,4 @@
|
|||
_start:
|
||||
call __tls_get_addr
|
||||
|
||||
// CHECK: undefined symbol: __tls_get_addr
|
||||
// CHECK: error: {{.*}} (.text+0x1): undefined symbol '__tls_get_addr'
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
# 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: undefined symbol: hidden in {{.*}}
|
||||
# CHECK: error: {{.*}} (.data+0x0): undefined symbol 'hidden'
|
||||
.global hidden
|
||||
.hidden hidden
|
||||
|
||||
# CHECK: undefined symbol: internal in {{.*}}
|
||||
# CHECK: error: {{.*}} (.data+0x8): undefined symbol 'internal'
|
||||
.global internal
|
||||
.internal internal
|
||||
|
||||
# CHECK: undefined symbol: protected in {{.*}}
|
||||
# CHECK: error: {{.*}} (.data+0x10): undefined symbol 'protected'
|
||||
.global protected
|
||||
.protected protected
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,21 @@
|
|||
# REQUIRES: x86
|
||||
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
|
||||
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/undef.s -o %t2.o
|
||||
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/undef-debug.s -o %t3.o
|
||||
# RUN: llvm-ar rc %t2.a %t2.o
|
||||
# RUN: not ld.lld %t.o %t2.a -o %t.exe 2>&1 | FileCheck %s
|
||||
# RUN: not ld.lld -pie %t.o %t2.a -o %t.exe 2>&1 | FileCheck %s
|
||||
# CHECK: undefined symbol: foo in
|
||||
# CHECK: undefined symbol: bar in
|
||||
# CHECK: undefined symbol: foo(int) in
|
||||
# CHECK: undefined symbol: zed2 in {{.*}}2.a({{.*}}.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: undef-debug.s (3): undefined symbol 'zed3'
|
||||
|
||||
# RUN: not ld.lld %t.o %t2.a -o %t.exe -no-demangle 2>&1 | \
|
||||
# RUN: FileCheck -check-prefix=NO-DEMANGLE %s
|
||||
# NO-DEMANGLE: undefined symbol: _Z3fooi in
|
||||
# NO-DEMANGLE: error: undef.s (.text+0x10): undefined symbol '_Z3fooi'
|
||||
|
||||
.file "undef.s"
|
||||
|
||||
.globl _start
|
||||
_start:
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
## 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: undefined symbol: undef in {{.*}}2.o
|
||||
# UNDCHECK: error: {{.*}}2.o (.text+0x1): undefined symbol 'undef'
|
||||
|
||||
## 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 +19,7 @@
|
|||
# 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: undefined symbol: undef
|
||||
# ERRUND: error: {{.*}} (.text+0x1): undefined symbol 'undef'
|
||||
## 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
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# 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: undefined symbol: f3 in
|
||||
# CHECK: error: {{.*}} (.text+0x1): undefined symbol 'f3'
|
||||
.globl _start
|
||||
_start:
|
||||
call f3
|
||||
|
|
|
|||
|
|
@ -2,6 +2,6 @@
|
|||
# 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: undefined symbol: foo
|
||||
# ERR: error: {{.*}} (.text+0x1): undefined symbol 'foo'
|
||||
|
||||
callq foo@PLT
|
||||
|
|
|
|||
Loading…
Reference in New Issue