diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index e774583de445..d9c3a4b43f83 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -20,6 +20,8 @@ namespace elf2 { struct Configuration { llvm::StringRef DynamicLinker; llvm::StringRef Entry; + llvm::StringRef Fini = "_fini"; + llvm::StringRef Init = "_init"; llvm::StringRef OutputFile = "a.out"; llvm::StringRef SoName; llvm::StringRef Sysroot; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 078382c0c4b0..79217648e901 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -117,6 +117,12 @@ void LinkerDriver::link(ArrayRef ArgsArr) { if (auto *Arg = Args.getLastArg(OPT_entry)) Config->Entry = Arg->getValue(); + if (auto *Arg = Args.getLastArg(OPT_fini)) + Config->Fini = Arg->getValue(); + + if (auto *Arg = Args.getLastArg(OPT_init)) + Config->Init = Arg->getValue(); + if (auto *Arg = Args.getLastArg(OPT_soname)) Config->SoName = Arg->getValue(); diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td index a9b0cf84d1c9..abdd4399b21a 100644 --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -32,6 +32,12 @@ def entry : Separate<["--", "-"], "entry">, MetaVarName<"">, def export_dynamic : Flag<["--", "-"], "export-dynamic">, HelpText<"Put symbols in the dynamic symbol table">; +def fini : Separate<["-"], "fini">, MetaVarName<"">, + HelpText<"Specify a finalizer function">; + +def init : Separate<["-"], "init">, MetaVarName<"">, + HelpText<"Specify an initializer function">; + def l : Joined<["-"], "l">, MetaVarName<"">, HelpText<"Root name of library to use">; @@ -77,6 +83,8 @@ def alias_L__library_path : Joined<["--"], "library-path=">, Alias; def alias_discard_all_x: Flag<["-"], "x">, Alias; def alias_discard_locals_X: Flag<["-"], "X">, Alias; def alias_entry_e : Separate<["-"], "e">, Alias; +def alias_fini_fini : Joined<["-"], "fini=">, Alias; +def alias_init_init : Joined<["-"], "init=">, Alias; def alias_l__library : Joined<["--"], "library=">, Alias; def alias_rpath_rpath : Joined<["-"], "rpath=">, Alias; def alias_soname_h : Separate<["-"], "h">, Alias; diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp index 7e7ad4990cad..c9a353b666eb 100644 --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -170,13 +170,14 @@ template void HashTableSection::addSymbol(SymbolBody *S) { template DynamicSection::DynamicSection(SymbolTable &SymTab, HashTableSection &HashSec, - RelocationSection &RelaDynSec) + RelocationSection &RelaDynSec, + const OutputSection &BssSec) : OutputSectionBase(".dynamic", llvm::ELF::SHT_DYNAMIC, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE), HashSec(HashSec), DynSymSec(HashSec.getDynSymSec()), DynStrSec(DynSymSec.getStrTabSec()), RelaDynSec(RelaDynSec), - SymTab(SymTab) { + BssSec(BssSec), SymTab(SymTab) { typename Base::HeaderT &Header = this->Header; Header.sh_addralign = ELFT::Is64Bits ? 8 : 4; Header.sh_entsize = ELFT::Is64Bits ? 16 : 8; @@ -224,6 +225,16 @@ template void DynamicSection::finalize() { DynStrSec.add(File->getSoName()); NumEntries += SharedFiles.size(); + if (Symbol *S = SymTab.getSymbols().lookup(Config->Init)) + InitSym = dyn_cast>(S->Body); + if (Symbol *S = SymTab.getSymbols().lookup(Config->Fini)) + FiniSym = dyn_cast>(S->Body); + + if (InitSym) + ++NumEntries; // DT_INIT + if (FiniSym) + ++NumEntries; // DT_FINI + ++NumEntries; // DT_NULL Header.sh_size = NumEntries * Header.sh_entsize; @@ -280,6 +291,11 @@ template void DynamicSection::writeTo(uint8_t *Buf) { for (const std::unique_ptr &File : SharedFiles) WriteVal(DT_NEEDED, DynStrSec.getFileOff(File->getSoName())); + if (InitSym) + WritePtr(DT_INIT, getSymVA(*InitSym, BssSec)); + if (FiniSym) + WritePtr(DT_FINI, getSymVA(*FiniSym, BssSec)); + WriteVal(DT_NULL, 0); } diff --git a/lld/ELF/OutputSections.h b/lld/ELF/OutputSections.h index e36ac9eee7c8..5e2119630d70 100644 --- a/lld/ELF/OutputSections.h +++ b/lld/ELF/OutputSections.h @@ -320,7 +320,8 @@ class DynamicSection final : public OutputSectionBase { public: DynamicSection(SymbolTable &SymTab, HashTableSection &HashSec, - RelocationSection &RelaDynSec); + RelocationSection &RelaDynSec, + const OutputSection &BssSec); void finalize() override; void writeTo(uint8_t *Buf) override; @@ -333,7 +334,10 @@ private: SymbolTableSection &DynSymSec; StringTableSection &DynStrSec; RelocationSection &RelaDynSec; + const OutputSection &BssSec; SymbolTable &SymTab; + const ELFSymbolBody *InitSym = nullptr; + const ELFSymbolBody *FiniSym = nullptr; }; } } diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index c7dbafe86ed7..4fadfd237f45 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -85,7 +85,7 @@ public: Writer(SymbolTable *T) : SymTabSec(*T, StrTabSec, BssSec), DynSymSec(*T, DynStrSec, BssSec), RelaDynSec(DynSymSec, GotSec, T->shouldUseRela()), PltSec(GotSec), - HashSec(DynSymSec), DynamicSec(*T, HashSec, RelaDynSec), + HashSec(DynSymSec), DynamicSec(*T, HashSec, RelaDynSec, BssSec), BssSec(PltSec, GotSec, BssSec, ".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE) {} void run(); diff --git a/lld/test/elf2/init-fini.s b/lld/test/elf2/init-fini.s new file mode 100644 index 000000000000..cd1649efe3f1 --- /dev/null +++ b/lld/test/elf2/init-fini.s @@ -0,0 +1,43 @@ +// REQUIRES: x86 +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t + +// Should use "_init" and "_fini" by default when fills dynamic table +// RUN: lld -flavor gnu2 -shared %t -o %t2 +// RUN: llvm-readobj -dynamic-table %t2 | FileCheck --check-prefix=BYDEF %s +// BYDEF: INIT 0x11010 +// BYDEF: FINI 0x11020 + +// -init and -fini override symbols to use +// RUN: lld -flavor gnu2 -shared %t -o %t2 -init _foo -fini _bar +// RUN: llvm-readobj -dynamic-table %t2 | FileCheck --check-prefix=OVR %s +// OVR: INIT 0x11030 +// OVR: FINI 0x11040 + +// Check aliases as well +// RUN: lld -flavor gnu2 -shared %t -o %t2 -init=_foo -fini=_bar +// RUN: llvm-readobj -dynamic-table %t2 | FileCheck --check-prefix=OVR %s + +// Should add a dynamic table entry even if a given symbol stay undefined +// RUN: lld -flavor gnu2 -shared %t -o %t2 -init=_undef -fini=_undef +// RUN: llvm-readobj -dynamic-table %t2 | FileCheck --check-prefix=UNDEF %s +// UNDEF: INIT 0x0 +// UNDEF: FINI 0x0 + +// Should not add new entries to the symbol table +// and should not require given symbols to be resolved +// RUN: lld -flavor gnu2 -shared %t -o %t2 -init=_unknown -fini=_unknown +// RUN: llvm-readobj -symbols -dynamic-table %t2 | FileCheck --check-prefix=NOENTRY %s +// NOENTRY: Symbols [ +// NOENTRY-NOT: Name: _unknown +// NOENTRY: ] +// NOENTRY: DynamicSection [ +// NOENTRY-NOT: INIT +// NOENTRY-NOT: FINI +// NOENTRY: ] + +.global _start,_init,_fini,_foo,_bar,_undef; +_start: +_init = 0x11010 +_fini = 0x11020 +_foo = 0x11030 +_bar = 0x11040