Skip to content

Commit

Permalink
ELF2: Implement --as-needed.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
rui314 committed Oct 11, 2015
1 parent 78ed9b4 commit 35da9b6
Show file tree
Hide file tree
Showing 13 changed files with 106 additions and 17 deletions.
1 change: 1 addition & 0 deletions lld/ELF/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
13 changes: 11 additions & 2 deletions lld/ELF/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion lld/ELF/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
7 changes: 6 additions & 1 deletion lld/ELF/InputFiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ class ObjectFile : public ObjectFileBase, public ELFData<ELFT> {
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();
Expand Down Expand Up @@ -198,6 +198,11 @@ class SharedFileBase : public ELFFileBase {
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>
Expand Down
2 changes: 1 addition & 1 deletion lld/ELF/InputSection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 4 additions & 1 deletion lld/ELF/LinkerScript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
6 changes: 4 additions & 2 deletions lld/ELF/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;

Expand Down Expand Up @@ -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">;

Expand Down Expand Up @@ -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">;
Expand Down
9 changes: 7 additions & 2 deletions lld/ELF/OutputSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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));
Expand Down
7 changes: 5 additions & 2 deletions lld/ELF/Symbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -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().
Expand Down Expand Up @@ -263,8 +264,10 @@ template <class ELFT> class SharedSymbol : public Defined<ELFT> {
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
Expand Down
19 changes: 14 additions & 5 deletions lld/ELF/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -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});
}
}

Expand Down
6 changes: 6 additions & 0 deletions lld/test/elf2/Inputs/shared2.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.global bar2
.type bar2, @function
bar2:

.global zed2
zed2:
3 changes: 3 additions & 0 deletions lld/test/elf2/Inputs/shared3.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.global baz
.type barz, @function
baz:
43 changes: 43 additions & 0 deletions lld/test/elf2/as-needed.s
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 35da9b6

Please sign in to comment.