ELF2: Implement --as-needed.

This patch adds AsNeeded and IsUsed bool fields to SharedFile. AsNeeded bit
is set if the DSO is enclosed with --as-needed and --no-as-needed. IsUsed
bit is off by default. When we adds a symbol to the symbol table for dynamic
linking, we set its SharedFile's IsUsed bit.

If AsNeeded is set but IsUsed is not set, we don't want to write that
file's SO name to DT_NEEDED field.

http://reviews.llvm.org/D13579

llvm-svn: 249998
This commit is contained in:
Rui Ueyama 2015-10-11 20:59:12 +00:00
parent 78ed9b43fd
commit 35da9b6e1c
13 changed files with 106 additions and 17 deletions

View File

@ -43,6 +43,7 @@ struct Configuration {
std::string RPath;
std::vector<llvm::StringRef> SearchPaths;
bool AllowMultipleDefinition;
bool AsNeeded = false;
bool DiscardAll;
bool DiscardLocals;
bool DiscardNone;

View File

@ -100,9 +100,12 @@ void LinkerDriver::addFile(StringRef Path) {
}
Files.push_back(make_unique<ArchiveFile>(MBRef));
return;
case file_magic::elf_shared_object:
Files.push_back(createELFFile<SharedFile>(MBRef));
case file_magic::elf_shared_object: {
std::unique_ptr<ELFFileBase> File = createELFFile<SharedFile>(MBRef);
cast<SharedFileBase>(File.get())->AsNeeded = Config->AsNeeded;
Files.push_back(std::move(File));
return;
}
default:
Files.push_back(createELFFile<ObjectFile>(MBRef));
}
@ -187,6 +190,12 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) {
case OPT_script:
addFile(Arg->getValue());
break;
case OPT_as_needed:
Config->AsNeeded = true;
break;
case OPT_no_as_needed:
Config->AsNeeded = false;
break;
case OPT_Bstatic:
Config->Static = true;
break;

View File

@ -329,7 +329,7 @@ template <class ELFT> void SharedFile<ELFT>::parse() {
error(NameOrErr.getError());
StringRef Name = *NameOrErr;
SymbolBodies.emplace_back(Name, Sym);
SymbolBodies.emplace_back(this, Name, Sym);
}
}

View File

@ -146,7 +146,7 @@ public:
uint32_t FirstNonLocal = this->Symtab->sh_info;
if (SymbolIndex < FirstNonLocal)
return nullptr;
return SymbolBodies[SymbolIndex - FirstNonLocal]->repl();
return SymbolBodies[SymbolIndex - FirstNonLocal];
}
Elf_Sym_Range getLocalSymbols();
@ -198,6 +198,11 @@ public:
StringRef getSoName() const { return SoName; }
virtual void parseSoName() = 0;
virtual void parse() = 0;
// Used for --as-needed
bool AsNeeded = false;
bool IsUsed = false;
bool isNeeded() const { return !AsNeeded || IsUsed; }
};
template <class ELFT>

View File

@ -46,7 +46,7 @@ void InputSection<ELFT>::relocate(
continue;
SymVA = getLocalSymVA(Sym, File);
} else {
SymbolBody &Body = *File.getSymbolBody(SymIndex);
SymbolBody &Body = *File.getSymbolBody(SymIndex)->repl();
SymVA = getSymVA<ELFT>(Body);
if (Target->relocNeedsPlt(Type, Body)) {
SymVA = Out<ELFT>::Plt->getEntryAddr(Body);

View File

@ -155,12 +155,15 @@ void LinkerScript::addFile(StringRef S) {
void LinkerScript::readAsNeeded() {
expect("(");
bool Orig = Config->AsNeeded;
Config->AsNeeded = true;
for (;;) {
StringRef Tok = next();
if (Tok == ")")
return;
break;
addFile(Tok);
}
Config->AsNeeded = Orig;
}
void LinkerScript::readEntry() {

View File

@ -14,6 +14,8 @@ def allow_multiple_definition: Flag<["--"], "allow-multiple-definition">,
def allow_shlib_undefined : Flag<["--", "-"], "allow-shlib-undefined">;
def as_needed : Flag<["--"], "as-needed">;
def disable_new_dtags : Flag<["--"], "disable-new-dtags">,
HelpText<"Disable new dynamic tags">;
@ -52,6 +54,8 @@ def m : Separate<["-"], "m">,
def no_allow_shlib_undefined : Flag<["--"], "no-allow-shlib-undefined">;
def no_as_needed : Flag<["--"], "no-as-needed">;
def no_whole_archive : Flag<["--"], "no-whole-archive">,
HelpText<"Restores the default behavior of loading archive members">;
@ -111,14 +115,12 @@ def alias_undefined_u : Separate<["-"], "u">, Alias<undefined>;
// Options listed below are silently ignored now.
def O3 : Flag<["-"], "O3">;
def as_needed : Flag<["--"], "as-needed">;
def build_id : Flag<["--"], "build-id">;
def eh_frame_hdr : Flag<["--"], "eh-frame-hdr">;
def end_group : Flag<["--"], "end-group">;
def gc_sections : Flag<["--"], "gc-sections">;
def hash_style : Joined<["--"], "hash-style=">;
def no_add_needed : Flag<["--"], "no-add-needed">;
def no_as_needed : Flag<["--"], "no-as-needed">;
def no_fatal_warnings : Flag<["--"], "no-fatal-warnings">;
def start_group : Flag<["--"], "start-group">;
def strip_all : Flag<["--"], "strip-all">;

View File

@ -116,8 +116,10 @@ template <class ELFT> void RelocationSection<ELFT>::writeTo(uint8_t *Buf) {
OutputSection<ELFT> *OutSec = C.getOutputSection();
uint32_t SymIndex = RI.getSymbol(IsMips64EL);
const ObjectFile<ELFT> &File = *C.getFile();
const SymbolBody *Body = File.getSymbolBody(SymIndex);
SymbolBody *Body = File.getSymbolBody(SymIndex);
const ELFFile<ELFT> &Obj = File.getObj();
if (Body)
Body = Body->repl();
uint32_t Type = RI.getType(IsMips64EL);
@ -279,6 +281,8 @@ template <class ELFT> void DynamicSection<ELFT>::finalize() {
NumEntries += 2;
for (const std::unique_ptr<SharedFile<ELFT>> &F : SymTab.getSharedFiles()) {
if (!F->isNeeded())
continue;
Out<ELFT>::DynStrTab->add(F->getSoName());
++NumEntries;
}
@ -356,7 +360,8 @@ template <class ELFT> void DynamicSection<ELFT>::writeTo(uint8_t *Buf) {
WriteArray(DT_FINI_ARRAY, DT_FINI_ARRAYSZ, FiniArraySec);
for (const std::unique_ptr<SharedFile<ELFT>> &F : SymTab.getSharedFiles())
WriteVal(DT_NEEDED, Out<ELFT>::DynStrTab->getFileOff(F->getSoName()));
if (F->isNeeded())
WriteVal(DT_NEEDED, Out<ELFT>::DynStrTab->getFileOff(F->getSoName()));
if (InitSym)
WritePtr(DT_INIT, getSymVA<ELFT>(*InitSym));

View File

@ -24,6 +24,7 @@ class InputFile;
class SymbolBody;
template <class ELFT> class ObjectFile;
template <class ELFT> class OutputSection;
template <class ELFT> class SharedFile;
// Initializes global objects defined in this file.
// Called at the beginning of main().
@ -263,8 +264,10 @@ public:
return S->kind() == Base::SharedKind;
}
SharedSymbol(StringRef Name, const Elf_Sym &Sym)
: Defined<ELFT>(Base::SharedKind, Name, Sym) {}
SharedSymbol(SharedFile<ELFT> *F, StringRef Name, const Elf_Sym &Sym)
: Defined<ELFT>(Base::SharedKind, Name, Sym), File(F) {}
SharedFile<ELFT> *File;
};
// This class represents a symbol defined in an archive file. It is

View File

@ -174,6 +174,14 @@ void Writer<ELFT>::scanRelocs(
uint32_t SymIndex = RI.getSymbol(IsMips64EL);
SymbolBody *Body = File.getSymbolBody(SymIndex);
uint32_t Type = RI.getType(IsMips64EL);
// Set "used" bit for --as-needed.
if (Body && Body->isUndefined() && !Body->isWeak())
if (auto *S = dyn_cast<SharedSymbol<ELFT>>(Body->repl()))
S->File->IsUsed = true;
if (Body)
Body = Body->repl();
if (Body) {
if (Target->relocNeedsPlt(Type, *Body)) {
if (Body->isInPlt())
@ -186,12 +194,13 @@ void Writer<ELFT>::scanRelocs(
Out<ELFT>::Got->addEntry(Body);
}
}
if (canBePreempted(Body)) {
bool CBP = canBePreempted(Body);
if (!CBP && (!Config->Shared || Target->isRelRelative(Type)))
continue;
if (CBP)
Body->setUsedInDynamicReloc();
Out<ELFT>::RelaDyn->addReloc({C, RI});
} else if (Config->Shared && !Target->isRelRelative(Type)) {
Out<ELFT>::RelaDyn->addReloc({C, RI});
}
Out<ELFT>::RelaDyn->addReloc({C, RI});
}
}

View File

@ -0,0 +1,6 @@
.global bar2
.type bar2, @function
bar2:
.global zed2
zed2:

View File

@ -0,0 +1,3 @@
.global baz
.type barz, @function
baz:

43
lld/test/elf2/as-needed.s Normal file
View File

@ -0,0 +1,43 @@
// REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple=i686-unknown-linux %s -o %t.o
// RUN: llvm-mc -filetype=obj -triple=i686-unknown-linux %p/Inputs/shared.s -o %t2.o
// RUN: llvm-mc -filetype=obj -triple=i686-unknown-linux %p/Inputs/shared2.s -o %t3.o
// RUN: llvm-mc -filetype=obj -triple=i686-unknown-linux %p/Inputs/shared3.s -o %t4.o
// RUN: ld.lld2 -shared %t2.o -soname shared1 -o %t2.so
// RUN: ld.lld2 -shared %t3.o -soname shared2 -o %t3.so
// RUN: ld.lld2 -shared %t4.o -soname shared3 -o %t4.so
/// Check if --as-needed actually works.
// RUN: ld.lld2 %t.o %t2.so %t3.so %t4.so -o %t2
// RUN: llvm-readobj -dynamic-table %t2 | FileCheck %s
// RUN: ld.lld2 --as-needed %t.o %t2.so %t3.so %t4.so -o %t2
// RUN: llvm-readobj -dynamic-table %t2 | FileCheck -check-prefix=CHECK2 %s
// RUN: ld.lld2 --as-needed %t.o %t2.so --no-as-needed %t3.so %t4.so -o %t2
// RUN: llvm-readobj -dynamic-table %t2 | FileCheck %s
/// GROUP directive is the same as --as-needed.
// RUN: echo "GROUP(" %t2.so %t3.so %t4.so ")" > %t.script
// RUN: ld.lld2 %t.o %t.script -o %t2
// RUN: llvm-readobj -dynamic-table %t2 | FileCheck %s
// RUN: echo "GROUP(AS_NEEDED(" %t2.so %t3.so %t4.so "))" > %t.script
// RUN: ld.lld2 %t.o %t.script -o %t2
// RUN: llvm-readobj -dynamic-table %t2 | FileCheck -check-prefix=CHECK2 %s
// CHECK: NEEDED SharedLibrary (shared1)
// CHECK: NEEDED SharedLibrary (shared2)
// CHECK: NEEDED SharedLibrary (shared3)
// CHECK2: NEEDED SharedLibrary (shared1)
// CHECK2-NOT: NEEDED SharedLibrary (shared2)
// CHECK2-NOT: NEEDED SharedLibrary (shared3)
.global _start
_start:
.long bar
.long zed
.weak baz