[ELF] - Added support for --unresolved-symbols option.

Option has next description (http://linux.die.net/man/1/ld):
"--unresolved-symbols=method
Determine how to handle unresolved symbols. There are four possible values for method
according to documentation:

ignore-all: Do not report any unresolved symbols.
report-all: Report all unresolved symbols. This is the default.
ignore-in-object-files: Report unresolved symbols that are contained in shared libraries, but ignore them if they come from regular object files.
ignore-in-shared-libs: Report unresolved symbols that come from regular object files, but ignore them if they come from shared libraries."

Since report-all is default and we traditionally do not report about undefined symbols in lld,
report-all does not report about undefines from DSO. 
ignore-in-object-files also does not do that. Handling of that option differs from what gnu linkers do.

Option works in next way in lld:
ignore-all: Do not report any unresolved symbols.
report-all: Report all unresolved symbols except symbols from DSOs. This is the default.
ignore-in-object-files: The same as ignore-all.
gnore-in-shared-libs: The same as report-all.

This is PR24524.

Differential revision: http://reviews.llvm.org/D21794

llvm-svn: 274123
This commit is contained in:
George Rimar 2016-06-29 12:35:04 +00:00
parent e5b3aebfb5
commit e86dcd0cbd
6 changed files with 100 additions and 12 deletions

View File

@ -33,6 +33,8 @@ enum ELFKind {
enum class BuildIdKind { None, Fnv1, Md5, Sha1, Hexstring }; enum class BuildIdKind { None, Fnv1, Md5, Sha1, Hexstring };
enum class UnresolvedPolicy { NoUndef, Error, Warn, Ignore };
// This struct contains symbols version definition that // This struct contains symbols version definition that
// can be found in version script if it is used for link. // can be found in version script if it is used for link.
struct Version { struct Version {
@ -85,9 +87,7 @@ struct Configuration {
bool ICF; bool ICF;
bool Mips64EL = false; bool Mips64EL = false;
bool NoGnuUnique; bool NoGnuUnique;
bool NoUndefined;
bool NoUndefinedVersion; bool NoUndefinedVersion;
bool NoinhibitExec;
bool Pic; bool Pic;
bool Pie; bool Pie;
bool PrintGcSections; bool PrintGcSections;
@ -110,6 +110,7 @@ struct Configuration {
bool ZNow; bool ZNow;
bool ZOrigin; bool ZOrigin;
bool ZRelro; bool ZRelro;
UnresolvedPolicy UnresolvedSymbols;
BuildIdKind BuildId = BuildIdKind::None; BuildIdKind BuildId = BuildIdKind::None;
ELFKind EKind = ELFNoneKind; ELFKind EKind = ELFNoneKind;
uint16_t EMachine = llvm::ELF::EM_NONE; uint16_t EMachine = llvm::ELF::EM_NONE;

View File

@ -296,6 +296,25 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr) {
} }
} }
static UnresolvedPolicy getUnresolvedSymbolOption(opt::InputArgList &Args) {
if (Args.hasArg(OPT_noinhibit_exec))
return UnresolvedPolicy::Warn;
if (Args.hasArg(OPT_no_undefined) || hasZOption(Args, "defs"))
return UnresolvedPolicy::NoUndef;
if (Config->Relocatable)
return UnresolvedPolicy::Ignore;
if (auto *Arg = Args.getLastArg(OPT_unresolved_symbols)) {
StringRef S = Arg->getValue();
if (S == "ignore-all" || S == "ignore-in-object-files")
return UnresolvedPolicy::Ignore;
if (S == "ignore-in-shared-libs" || S == "report-all")
return UnresolvedPolicy::Error;
error("unknown --unresolved-symbols value: " + S);
}
return UnresolvedPolicy::Error;
}
// Initializes Config members by the command line options. // Initializes Config members by the command line options.
void LinkerDriver::readConfigs(opt::InputArgList &Args) { void LinkerDriver::readConfigs(opt::InputArgList &Args) {
for (auto *Arg : Args.filtered(OPT_L)) for (auto *Arg : Args.filtered(OPT_L))
@ -328,10 +347,7 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
Config->GcSections = Args.hasArg(OPT_gc_sections); Config->GcSections = Args.hasArg(OPT_gc_sections);
Config->ICF = Args.hasArg(OPT_icf); Config->ICF = Args.hasArg(OPT_icf);
Config->NoGnuUnique = Args.hasArg(OPT_no_gnu_unique); Config->NoGnuUnique = Args.hasArg(OPT_no_gnu_unique);
Config->NoUndefined =
Args.hasArg(OPT_no_undefined) || hasZOption(Args, "defs");
Config->NoUndefinedVersion = Args.hasArg(OPT_no_undefined_version); Config->NoUndefinedVersion = Args.hasArg(OPT_no_undefined_version);
Config->NoinhibitExec = Args.hasArg(OPT_noinhibit_exec);
Config->Pie = Args.hasArg(OPT_pie); Config->Pie = Args.hasArg(OPT_pie);
Config->PrintGcSections = Args.hasArg(OPT_print_gc_sections); Config->PrintGcSections = Args.hasArg(OPT_print_gc_sections);
Config->Relocatable = Args.hasArg(OPT_relocatable); Config->Relocatable = Args.hasArg(OPT_relocatable);
@ -412,6 +428,8 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
for (auto *Arg : Args.filtered(OPT_undefined)) for (auto *Arg : Args.filtered(OPT_undefined))
Config->Undefined.push_back(Arg->getValue()); Config->Undefined.push_back(Arg->getValue());
Config->UnresolvedSymbols = getUnresolvedSymbolOption(Args);
if (auto *Arg = Args.getLastArg(OPT_dynamic_list)) if (auto *Arg = Args.getLastArg(OPT_dynamic_list))
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue())) if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
parseDynamicList(*Buffer); parseDynamicList(*Buffer);

View File

@ -150,6 +150,9 @@ def trace_symbol : J<"trace-symbol=">, HelpText<"Trace references to symbols">;
def undefined: J<"undefined=">, def undefined: J<"undefined=">,
HelpText<"Force undefined symbol during linking">; HelpText<"Force undefined symbol during linking">;
def unresolved_symbols: J<"unresolved-symbols=">,
HelpText<"Determine how to handle unresolved symbols">;
def verbose: F<"verbose">, HelpText<"Verbose mode">; def verbose: F<"verbose">, HelpText<"Verbose mode">;
def version: F<"version">, HelpText<"Display the version number">; def version: F<"version">, HelpText<"Display the version number">;

View File

@ -275,17 +275,17 @@ template <bool Is64Bits> struct DenseMapInfo<SectionKey<Is64Bits>> {
template <class ELFT> template <class ELFT>
static void reportUndefined(SymbolTable<ELFT> &Symtab, SymbolBody *Sym) { static void reportUndefined(SymbolTable<ELFT> &Symtab, SymbolBody *Sym) {
if (!Config->NoUndefined) { if (Config->UnresolvedSymbols == UnresolvedPolicy::Ignore)
if (Config->Relocatable) return;
return;
if (Config->Shared && Sym->symbol()->Visibility == STV_DEFAULT) if (Config->Shared && Sym->symbol()->Visibility == STV_DEFAULT &&
return; Config->UnresolvedSymbols != UnresolvedPolicy::NoUndef)
} return;
std::string Msg = "undefined symbol: " + Sym->getName().str(); std::string Msg = "undefined symbol: " + Sym->getName().str();
if (InputFile *File = Sym->getSourceFile<ELFT>()) if (InputFile *File = Sym->getSourceFile<ELFT>())
Msg += " in " + getFilename(File); Msg += " in " + getFilename(File);
if (Config->NoinhibitExec) if (Config->UnresolvedSymbols == UnresolvedPolicy::Warn)
warning(Msg); warning(Msg);
else else
error(Msg); error(Msg);

View File

@ -0,0 +1,3 @@
.globl _shared
_shared:
callq undef@PLT

View File

@ -0,0 +1,63 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t1.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/unresolved-symbols.s -o %t2.o
# RUN: ld.lld -shared %t2.o -o %t.so
## 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
## Error out if unknown option value was set.
# RUN: not ld.lld %t1.o %t2.o -o %t --unresolved-symbols=xxx 2>&1 | \
# RUN: FileCheck -check-prefix=ERR1 %s
# ERR1: unknown --unresolved-symbols value: xxx
## Ignore all should not produce error for symbols from object except
## case when --no-undefined specified.
# RUN: ld.lld %t2.o -o %t1_1 --unresolved-symbols=ignore-all
# 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
## 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
## Ignoring undefines in objects should not produce error for symbol from object.
# RUN: ld.lld %t1.o %t2.o -o %t2 --unresolved-symbols=ignore-in-object-files
# RUN: llvm-readobj %t2 > /dev/null 2>&1
## And still should not should produce for undefines from DSOs.
# RUN: ld.lld %t1.o %t.so -o %t2_1 --unresolved-symbols=ignore-in-object-files
# RUN: llvm-readobj %t2 > /dev/null 2>&1
## Ignoring undefines in shared should produce error for symbol from object.
# RUN: not ld.lld %t2.o -o %t3 --unresolved-symbols=ignore-in-shared-libs 2>&1 | \
# RUN: FileCheck -check-prefix=ERRUND %s
## And should not produce errors for symbols from DSO.
# RUN: ld.lld %t1.o %t.so -o %t3_1 --unresolved-symbols=ignore-in-shared-libs
# RUN: llvm-readobj %t3_1 > /dev/null 2>&1
## Ignoring undefines in shared libs should not produce error for symbol from object
## if we are linking DSO.
# RUN: ld.lld -shared %t1.o -o %t4 --unresolved-symbols=ignore-in-shared-libs
# RUN: llvm-readobj %t4 > /dev/null 2>&1
## Do not report undefines if linking relocatable.
# RUN: ld.lld -r %t1.o %t2.o -o %t5 --unresolved-symbols=report-all
# RUN: llvm-readobj %t5 > /dev/null 2>&1
## report-all is the default one. Check that we do not report
## undefines from DSO and do report undefines from object. With
## report-all specified and without.
# RUN: ld.lld -shared %t1.o %t.so -o %t6 --unresolved-symbols=report-all
# RUN: llvm-readobj %t6 > /dev/null 2>&1
# RUN: ld.lld -shared %t1.o %t.so -o %t6_1
# 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
.globl _start
_start: