aboutsummaryrefslogtreecommitdiff
path: root/ELF/Driver.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2017-04-16 16:03:39 +0000
committerDimitry Andric <dim@FreeBSD.org>2017-04-16 16:03:39 +0000
commitd2d3ebb81992e107edf95c1c136d7a342d9b1418 (patch)
treebb1af8fff2b1400cf240e3b2532a1e5d22a121da /ELF/Driver.cpp
parent16787c9ce0b96aaa669d7fab3a495916b35ce758 (diff)
downloadsrc-d2d3ebb81992e107edf95c1c136d7a342d9b1418.tar.gz
src-d2d3ebb81992e107edf95c1c136d7a342d9b1418.zip
Vendor import of lld trunk r300422:vendor/lld/lld-trunk-r300422
Notes
Notes: svn path=/vendor/lld/dist/; revision=317025 svn path=/vendor/lld/lld-trunk-r300422/; revision=317026; tag=vendor/lld/lld-trunk-r300422
Diffstat (limited to 'ELF/Driver.cpp')
-rw-r--r--ELF/Driver.cpp419
1 files changed, 276 insertions, 143 deletions
diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp
index 50b701175d3e..47ecd607a48f 100644
--- a/ELF/Driver.cpp
+++ b/ELF/Driver.cpp
@@ -6,15 +6,34 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
+//
+// The driver drives the entire linking process. It is responsible for
+// parsing command line options and doing whatever it is instructed to do.
+//
+// One notable thing in the LLD's driver when compared to other linkers is
+// that the LLD's driver is agnostic on the host operating system.
+// Other linkers usually have implicit default values (such as a dynamic
+// linker path or library paths) for each host OS.
+//
+// I don't think implicit default values are useful because they are
+// usually explicitly specified by the compiler driver. They can even
+// be harmful when you are doing cross-linking. Therefore, in LLD, we
+// simply trust the compiler driver to pass all required options and
+// don't try to make effort on our side.
+//
+//===----------------------------------------------------------------------===//
#include "Driver.h"
#include "Config.h"
#include "Error.h"
+#include "Filesystem.h"
#include "ICF.h"
#include "InputFiles.h"
#include "InputSection.h"
#include "LinkerScript.h"
#include "Memory.h"
+#include "OutputSections.h"
+#include "ScriptParser.h"
#include "Strings.h"
#include "SymbolTable.h"
#include "Target.h"
@@ -48,16 +67,19 @@ BumpPtrAllocator elf::BAlloc;
StringSaver elf::Saver{BAlloc};
std::vector<SpecificAllocBase *> elf::SpecificAllocBase::Instances;
+static void setConfigs();
+
bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly,
raw_ostream &Error) {
ErrorCount = 0;
ErrorOS = &Error;
Argv0 = Args[0];
+ InputSections.clear();
Tar = nullptr;
Config = make<Configuration>();
Driver = make<LinkerDriver>();
- ScriptConfig = make<ScriptConfiguration>();
+ Script = make<LinkerScript>();
Driver->main(Args, CanExitEarly);
freeArena();
@@ -78,10 +100,8 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
.Cases("aarch64elf", "aarch64linux", {ELF64LEKind, EM_AARCH64})
.Case("armelf_linux_eabi", {ELF32LEKind, EM_ARM})
.Case("elf32_x86_64", {ELF32LEKind, EM_X86_64})
- .Case("elf32btsmip", {ELF32BEKind, EM_MIPS})
- .Case("elf32ltsmip", {ELF32LEKind, EM_MIPS})
- .Case("elf32btsmipn32", {ELF32BEKind, EM_MIPS})
- .Case("elf32ltsmipn32", {ELF32LEKind, EM_MIPS})
+ .Cases("elf32btsmip", "elf32btsmipn32", {ELF32BEKind, EM_MIPS})
+ .Cases("elf32ltsmip", "elf32ltsmipn32", {ELF32LEKind, EM_MIPS})
.Case("elf32ppc", {ELF32BEKind, EM_PPC})
.Case("elf64btsmip", {ELF64BEKind, EM_MIPS})
.Case("elf64ltsmip", {ELF64LEKind, EM_MIPS})
@@ -133,7 +153,7 @@ LinkerDriver::getArchiveMembers(MemoryBufferRef MB) {
// Opens and parses a file. Path has to be resolved already.
// Newly created memory buffers are owned by this driver.
-void LinkerDriver::addFile(StringRef Path) {
+void LinkerDriver::addFile(StringRef Path, bool WithLOption) {
using namespace sys::fs;
Optional<MemoryBufferRef> Buffer = readFile(Path);
@@ -164,6 +184,19 @@ void LinkerDriver::addFile(StringRef Path) {
return;
}
Files.push_back(createSharedFile(MBRef));
+
+ // DSOs usually have DT_SONAME tags in their ELF headers, and the
+ // sonames are used to identify DSOs. But if they are missing,
+ // they are identified by filenames. We don't know whether the new
+ // file has a DT_SONAME or not because we haven't parsed it yet.
+ // Here, we set the default soname for the file because we might
+ // need it later.
+ //
+ // If a file was specified by -lfoo, the directory part is not
+ // significant, as a user did not specify it. This behavior is
+ // compatible with GNU.
+ Files.back()->DefaultSoName =
+ WithLOption ? sys::path::filename(Path) : Path;
return;
default:
if (InLib)
@@ -176,7 +209,7 @@ void LinkerDriver::addFile(StringRef Path) {
// Add a given library by searching it from input search paths.
void LinkerDriver::addLibrary(StringRef Name) {
if (Optional<std::string> Path = searchLibrary(Name))
- addFile(*Path);
+ addFile(*Path, /*WithLOption=*/true);
else
error("unable to find library -l" + Name);
}
@@ -281,11 +314,27 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) {
return;
}
- // GNU linkers disagree here. Though both -version and -v are mentioned
- // in help to print the version information, GNU ld just normally exits,
- // while gold can continue linking. We are compatible with ld.bfd here.
- if (Args.hasArg(OPT_version) || Args.hasArg(OPT_v))
- outs() << getLLDVersion() << "\n";
+ // Handle -v or -version.
+ //
+ // A note about "compatible with GNU linkers" message: this is a hack for
+ // scripts generated by GNU Libtool 2.4.6 (released in February 2014 and
+ // still the newest version in March 2017) or earlier to recognize LLD as
+ // a GNU compatible linker. As long as an output for the -v option
+ // contains "GNU" or "with BFD", they recognize us as GNU-compatible.
+ //
+ // This is somewhat ugly hack, but in reality, we had no choice other
+ // than doing this. Considering the very long release cycle of Libtool,
+ // it is not easy to improve it to recognize LLD as a GNU compatible
+ // linker in a timely manner. Even if we can make it, there are still a
+ // lot of "configure" scripts out there that are generated by old version
+ // of Libtool. We cannot convince every software developer to migrate to
+ // the latest version and re-generate scripts. So we have this hack.
+ if (Args.hasArg(OPT_v) || Args.hasArg(OPT_version))
+ message(getLLDVersion() + " (compatible with GNU linkers)");
+
+ // ld.bfd always exits after printing out the version string.
+ // ld.gold proceeds if a given option is -v. Because gold's behavior
+ // is more permissive than ld.bfd, we chose what gold does here.
if (Args.hasArg(OPT_version))
return;
@@ -311,6 +360,7 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) {
initLLVM(Args);
createFiles(Args);
inferMachineType();
+ setConfigs();
checkOptions(Args);
if (ErrorCount)
return;
@@ -333,26 +383,68 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) {
}
}
-static UnresolvedPolicy getUnresolvedSymbolOption(opt::InputArgList &Args) {
+static bool getArg(opt::InputArgList &Args, unsigned K1, unsigned K2,
+ bool Default) {
+ if (auto *Arg = Args.getLastArg(K1, K2))
+ return Arg->getOption().getID() == K1;
+ return Default;
+}
+
+static std::vector<StringRef> getArgs(opt::InputArgList &Args, int Id) {
+ std::vector<StringRef> V;
+ for (auto *Arg : Args.filtered(Id))
+ V.push_back(Arg->getValue());
+ return V;
+}
+
+static std::string getRPath(opt::InputArgList &Args) {
+ std::vector<StringRef> V = getArgs(Args, OPT_rpath);
+ return llvm::join(V.begin(), V.end(), ":");
+}
+
+// Determines what we should do if there are remaining unresolved
+// symbols after the name resolution.
+static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &Args) {
+ // -noinhibit-exec or -r imply some default values.
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;
+ return UnresolvedPolicy::WarnAll;
+ if (Args.hasArg(OPT_relocatable))
+ return UnresolvedPolicy::IgnoreAll;
- 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::ReportError;
- error("unknown --unresolved-symbols value: " + S);
+ UnresolvedPolicy ErrorOrWarn = getArg(Args, OPT_error_unresolved_symbols,
+ OPT_warn_unresolved_symbols, true)
+ ? UnresolvedPolicy::ReportError
+ : UnresolvedPolicy::Warn;
+
+ // Process the last of -unresolved-symbols, -no-undefined or -z defs.
+ for (auto *Arg : llvm::reverse(Args)) {
+ switch (Arg->getOption().getID()) {
+ case 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 ErrorOrWarn;
+ error("unknown --unresolved-symbols value: " + S);
+ continue;
+ }
+ case OPT_no_undefined:
+ return ErrorOrWarn;
+ case OPT_z:
+ if (StringRef(Arg->getValue()) == "defs")
+ return ErrorOrWarn;
+ continue;
+ }
}
- return UnresolvedPolicy::ReportError;
+
+ // -shared implies -unresolved-symbols=ignore-all because missing
+ // symbols are likely to be resolved at runtime using other DSOs.
+ if (Config->Shared)
+ return UnresolvedPolicy::Ignore;
+ return ErrorOrWarn;
}
-static Target2Policy getTarget2Option(opt::InputArgList &Args) {
+static Target2Policy getTarget2(opt::InputArgList &Args) {
if (auto *Arg = Args.getLastArg(OPT_target2)) {
StringRef S = Arg->getValue();
if (S == "rel")
@@ -376,16 +468,10 @@ static bool isOutputFormatBinary(opt::InputArgList &Args) {
return false;
}
-static bool getArg(opt::InputArgList &Args, unsigned K1, unsigned K2,
- bool Default) {
- if (auto *Arg = Args.getLastArg(K1, K2))
- return Arg->getOption().getID() == K1;
- return Default;
-}
-
-static DiscardPolicy getDiscardOption(opt::InputArgList &Args) {
- if (Config->Relocatable)
+static DiscardPolicy getDiscard(opt::InputArgList &Args) {
+ if (Args.hasArg(OPT_relocatable))
return DiscardPolicy::None;
+
auto *Arg =
Args.getLastArg(OPT_discard_all, OPT_discard_locals, OPT_discard_none);
if (!Arg)
@@ -397,13 +483,23 @@ static DiscardPolicy getDiscardOption(opt::InputArgList &Args) {
return DiscardPolicy::None;
}
-static StripPolicy getStripOption(opt::InputArgList &Args) {
- if (auto *Arg = Args.getLastArg(OPT_strip_all, OPT_strip_debug)) {
- if (Arg->getOption().getID() == OPT_strip_all)
- return StripPolicy::All;
- return StripPolicy::Debug;
- }
- return StripPolicy::None;
+static StringRef getDynamicLinker(opt::InputArgList &Args) {
+ auto *Arg = Args.getLastArg(OPT_dynamic_linker, OPT_no_dynamic_linker);
+ if (!Arg || Arg->getOption().getID() == OPT_no_dynamic_linker)
+ return "";
+ return Arg->getValue();
+}
+
+static StripPolicy getStrip(opt::InputArgList &Args) {
+ if (Args.hasArg(OPT_relocatable))
+ return StripPolicy::None;
+
+ auto *Arg = Args.getLastArg(OPT_strip_all, OPT_strip_debug);
+ if (!Arg)
+ return StripPolicy::None;
+ if (Arg->getOption().getID() == OPT_strip_all)
+ return StripPolicy::All;
+ return StripPolicy::Debug;
}
static uint64_t parseSectionAddress(StringRef S, opt::Arg *Arg) {
@@ -433,7 +529,7 @@ static StringMap<uint64_t> getSectionStartMap(opt::InputArgList &Args) {
return Ret;
}
-static SortSectionPolicy getSortKind(opt::InputArgList &Args) {
+static SortSectionPolicy getSortSection(opt::InputArgList &Args) {
StringRef S = getString(Args, OPT_sort_section);
if (S == "alignment")
return SortSectionPolicy::Alignment;
@@ -444,6 +540,17 @@ static SortSectionPolicy getSortKind(opt::InputArgList &Args) {
return SortSectionPolicy::Default;
}
+static std::pair<bool, bool> getHashStyle(opt::InputArgList &Args) {
+ StringRef S = getString(Args, OPT_hash_style, "sysv");
+ if (S == "sysv")
+ return {true, false};
+ if (S == "gnu")
+ return {false, true};
+ if (S != "both")
+ error("unknown -hash-style: " + S);
+ return {true, true};
+}
+
static std::vector<StringRef> getLines(MemoryBufferRef MB) {
SmallVector<StringRef, 0> Arr;
MB.getBuffer().split(Arr, '\n');
@@ -459,116 +566,112 @@ static std::vector<StringRef> getLines(MemoryBufferRef MB) {
// Initializes Config members by the command line options.
void LinkerDriver::readConfigs(opt::InputArgList &Args) {
- for (auto *Arg : Args.filtered(OPT_L))
- Config->SearchPaths.push_back(Arg->getValue());
-
- std::vector<StringRef> RPaths;
- for (auto *Arg : Args.filtered(OPT_rpath))
- RPaths.push_back(Arg->getValue());
- if (!RPaths.empty())
- Config->RPath = llvm::join(RPaths.begin(), RPaths.end(), ":");
-
- if (auto *Arg = Args.getLastArg(OPT_m)) {
- // Parse ELF{32,64}{LE,BE} and CPU type.
- StringRef S = Arg->getValue();
- std::tie(Config->EKind, Config->EMachine, Config->OSABI) =
- parseEmulation(S);
- Config->MipsN32Abi = (S == "elf32btsmipn32" || S == "elf32ltsmipn32");
- Config->Emulation = S;
- }
-
Config->AllowMultipleDefinition = Args.hasArg(OPT_allow_multiple_definition);
+ Config->AuxiliaryList = getArgs(Args, OPT_auxiliary);
Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic);
Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions);
+ Config->DefineCommon = getArg(Args, OPT_define_common, OPT_no_define_common,
+ !Args.hasArg(OPT_relocatable));
Config->Demangle = getArg(Args, OPT_demangle, OPT_no_demangle, true);
Config->DisableVerify = Args.hasArg(OPT_disable_verify);
+ Config->Discard = getDiscard(Args);
+ Config->DynamicLinker = getDynamicLinker(Args);
Config->EhFrameHdr = Args.hasArg(OPT_eh_frame_hdr);
+ Config->EmitRelocs = Args.hasArg(OPT_emit_relocs);
Config->EnableNewDtags = !Args.hasArg(OPT_disable_new_dtags);
- Config->ExportDynamic = Args.hasArg(OPT_export_dynamic);
- Config->FatalWarnings = Args.hasArg(OPT_fatal_warnings);
+ Config->Entry = getString(Args, OPT_entry);
+ Config->ExportDynamic =
+ getArg(Args, OPT_export_dynamic, OPT_no_export_dynamic, false);
+ Config->FatalWarnings =
+ getArg(Args, OPT_fatal_warnings, OPT_no_fatal_warnings, false);
+ Config->Fini = getString(Args, OPT_fini, "_fini");
Config->GcSections = getArg(Args, OPT_gc_sections, OPT_no_gc_sections, false);
Config->GdbIndex = Args.hasArg(OPT_gdb_index);
Config->ICF = Args.hasArg(OPT_icf);
+ Config->Init = getString(Args, OPT_init, "_init");
+ Config->LTOAAPipeline = getString(Args, OPT_lto_aa_pipeline);
+ Config->LTONewPmPasses = getString(Args, OPT_lto_newpm_passes);
+ Config->LTOO = getInteger(Args, OPT_lto_O, 2);
+ Config->LTOPartitions = getInteger(Args, OPT_lto_partitions, 1);
+ Config->MapFile = getString(Args, OPT_Map);
Config->NoGnuUnique = Args.hasArg(OPT_no_gnu_unique);
Config->NoUndefinedVersion = Args.hasArg(OPT_no_undefined_version);
Config->Nostdlib = Args.hasArg(OPT_nostdlib);
- Config->OMagic = Args.hasArg(OPT_omagic);
+ Config->OFormatBinary = isOutputFormatBinary(Args);
+ Config->Omagic = Args.hasArg(OPT_omagic);
+ Config->OptRemarksFilename = getString(Args, OPT_opt_remarks_filename);
+ Config->OptRemarksWithHotness = Args.hasArg(OPT_opt_remarks_with_hotness);
+ Config->Optimize = getInteger(Args, OPT_O, 1);
+ Config->OutputFile = getString(Args, OPT_o);
Config->Pie = getArg(Args, OPT_pie, OPT_nopie, false);
Config->PrintGcSections = Args.hasArg(OPT_print_gc_sections);
+ Config->RPath = getRPath(Args);
Config->Relocatable = Args.hasArg(OPT_relocatable);
- Config->DefineCommon = getArg(Args, OPT_define_common, OPT_no_define_common,
- !Config->Relocatable);
- Config->Discard = getDiscardOption(Args);
Config->SaveTemps = Args.hasArg(OPT_save_temps);
- Config->SingleRoRx = Args.hasArg(OPT_no_rosegment);
+ Config->SearchPaths = getArgs(Args, OPT_L);
+ Config->SectionStartMap = getSectionStartMap(Args);
Config->Shared = Args.hasArg(OPT_shared);
+ Config->SingleRoRx = Args.hasArg(OPT_no_rosegment);
+ Config->SoName = getString(Args, OPT_soname);
+ Config->SortSection = getSortSection(Args);
+ Config->Strip = getStrip(Args);
+ Config->Sysroot = getString(Args, OPT_sysroot);
Config->Target1Rel = getArg(Args, OPT_target1_rel, OPT_target1_abs, false);
+ Config->Target2 = getTarget2(Args);
+ Config->ThinLTOCacheDir = getString(Args, OPT_thinlto_cache_dir);
+ Config->ThinLTOCachePolicy =
+ check(parseCachePruningPolicy(getString(Args, OPT_thinlto_cache_policy)),
+ "--thinlto-cache-policy: invalid cache policy");
+ Config->ThinLTOJobs = getInteger(Args, OPT_thinlto_jobs, -1u);
Config->Threads = getArg(Args, OPT_threads, OPT_no_threads, true);
Config->Trace = Args.hasArg(OPT_trace);
+ Config->Undefined = getArgs(Args, OPT_undefined);
+ Config->UnresolvedSymbols = getUnresolvedSymbolPolicy(Args);
Config->Verbose = Args.hasArg(OPT_verbose);
Config->WarnCommon = Args.hasArg(OPT_warn_common);
+ Config->ZCombreloc = !hasZOption(Args, "nocombreloc");
+ Config->ZExecstack = hasZOption(Args, "execstack");
+ Config->ZNocopyreloc = hasZOption(Args, "nocopyreloc");
+ Config->ZNodelete = hasZOption(Args, "nodelete");
+ Config->ZNodlopen = hasZOption(Args, "nodlopen");
+ Config->ZNow = hasZOption(Args, "now");
+ Config->ZOrigin = hasZOption(Args, "origin");
+ Config->ZRelro = !hasZOption(Args, "norelro");
+ Config->ZStackSize = getZOptionValue(Args, "stack-size", 0);
+ Config->ZText = !hasZOption(Args, "notext");
+ Config->ZWxneeded = hasZOption(Args, "wxneeded");
- Config->DynamicLinker = getString(Args, OPT_dynamic_linker);
- Config->Entry = getString(Args, OPT_entry);
- Config->Fini = getString(Args, OPT_fini, "_fini");
- Config->Init = getString(Args, OPT_init, "_init");
- Config->LTOAAPipeline = getString(Args, OPT_lto_aa_pipeline);
- Config->LTONewPmPasses = getString(Args, OPT_lto_newpm_passes);
- Config->OutputFile = getString(Args, OPT_o);
- Config->SoName = getString(Args, OPT_soname);
- Config->Sysroot = getString(Args, OPT_sysroot);
-
- Config->Optimize = getInteger(Args, OPT_O, 1);
- Config->LTOO = getInteger(Args, OPT_lto_O, 2);
if (Config->LTOO > 3)
error("invalid optimization level for LTO: " + getString(Args, OPT_lto_O));
- Config->LTOPartitions = getInteger(Args, OPT_lto_partitions, 1);
if (Config->LTOPartitions == 0)
error("--lto-partitions: number of threads must be > 0");
- Config->ThinLTOJobs = getInteger(Args, OPT_thinlto_jobs, -1u);
if (Config->ThinLTOJobs == 0)
error("--thinlto-jobs: number of threads must be > 0");
- Config->ZCombreloc = !hasZOption(Args, "nocombreloc");
- Config->ZExecstack = hasZOption(Args, "execstack");
- Config->ZNodelete = hasZOption(Args, "nodelete");
- Config->ZNow = hasZOption(Args, "now");
- Config->ZOrigin = hasZOption(Args, "origin");
- Config->ZRelro = !hasZOption(Args, "norelro");
- Config->ZStackSize = getZOptionValue(Args, "stack-size", -1);
- Config->ZWxneeded = hasZOption(Args, "wxneeded");
+ if (auto *Arg = Args.getLastArg(OPT_m)) {
+ // Parse ELF{32,64}{LE,BE} and CPU type.
+ StringRef S = Arg->getValue();
+ std::tie(Config->EKind, Config->EMachine, Config->OSABI) =
+ parseEmulation(S);
+ Config->MipsN32Abi = (S == "elf32btsmipn32" || S == "elf32ltsmipn32");
+ Config->Emulation = S;
+ }
- Config->OFormatBinary = isOutputFormatBinary(Args);
- Config->SectionStartMap = getSectionStartMap(Args);
- Config->SortSection = getSortKind(Args);
- Config->Target2 = getTarget2Option(Args);
- Config->UnresolvedSymbols = getUnresolvedSymbolOption(Args);
+ if (Args.hasArg(OPT_print_map))
+ Config->MapFile = "-";
// --omagic is an option to create old-fashioned executables in which
// .text segments are writable. Today, the option is still in use to
// create special-purpose programs such as boot loaders. It doesn't
// make sense to create PT_GNU_RELRO for such executables.
- if (Config->OMagic)
+ if (Config->Omagic)
Config->ZRelro = false;
- if (!Config->Relocatable)
- Config->Strip = getStripOption(Args);
-
- // Config->Pic is true if we are generating position-independent code.
- Config->Pic = Config->Pie || Config->Shared;
-
- if (auto *Arg = Args.getLastArg(OPT_hash_style)) {
- StringRef S = Arg->getValue();
- if (S == "gnu") {
- Config->GnuHash = true;
- Config->SysvHash = false;
- } else if (S == "both") {
- Config->GnuHash = true;
- } else if (S != "sysv")
- error("unknown hash style: " + S);
- }
+ std::tie(Config->SysvHash, Config->GnuHash) = getHashStyle(Args);
- // Parse --build-id or --build-id=<style>.
+ // Parse --build-id or --build-id=<style>. We handle "tree" as a
+ // synonym for "sha1" because all of our hash functions including
+ // -build-id=sha1 are tree hashes for performance reasons.
if (Args.hasArg(OPT_build_id))
Config->BuildId = BuildIdKind::Fast;
if (auto *Arg = Args.getLastArg(OPT_build_id_eq)) {
@@ -589,15 +692,10 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
}
}
- for (auto *Arg : Args.filtered(OPT_auxiliary))
- Config->AuxiliaryList.push_back(Arg->getValue());
if (!Config->Shared && !Config->AuxiliaryList.empty())
error("-f may not be used without -shared");
- for (auto *Arg : Args.filtered(OPT_undefined))
- Config->Undefined.push_back(Arg->getValue());
-
- if (auto *Arg = Args.getLastArg(OPT_dynamic_list))
+ for (auto *Arg : Args.filtered(OPT_dynamic_list))
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
readDynamicList(*Buffer);
@@ -605,13 +703,14 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
Config->SymbolOrderingFile = getLines(*Buffer);
- // If --retain-symbol-file is used, we'll retail only the symbols listed in
+ // If --retain-symbol-file is used, we'll keep only the symbols listed in
// the file and discard all others.
if (auto *Arg = Args.getLastArg(OPT_retain_symbols_file)) {
- Config->Discard = DiscardPolicy::RetainFile;
+ Config->DefaultSymbolVersion = VER_NDX_LOCAL;
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
for (StringRef S : getLines(*Buffer))
- Config->RetainSymbolsFile.insert(S);
+ Config->VersionScriptGlobals.push_back(
+ {S, /*IsExternCpp*/ false, /*HasWildcard*/ false});
}
for (auto *Arg : Args.filtered(OPT_export_dynamic_symbol))
@@ -627,11 +726,37 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
Config->DefaultSymbolVersion = VER_NDX_LOCAL;
}
+ if (getArg(Args, OPT_export_dynamic, OPT_no_export_dynamic, false))
+ Config->DefaultSymbolVersion = VER_NDX_GLOBAL;
+
if (auto *Arg = Args.getLastArg(OPT_version_script))
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
readVersionScript(*Buffer);
}
+// Some Config members do not directly correspond to any particular
+// command line options, but computed based on other Config values.
+// This function initialize such members. See Config.h for the details
+// of these values.
+static void setConfigs() {
+ ELFKind Kind = Config->EKind;
+ uint16_t Machine = Config->EMachine;
+
+ // There is an ILP32 ABI for x86-64, although it's not very popular.
+ // It is called the x32 ABI.
+ bool IsX32 = (Kind == ELF32LEKind && Machine == EM_X86_64);
+
+ Config->CopyRelocs = (Config->Relocatable || Config->EmitRelocs);
+ Config->Is64 = (Kind == ELF64LEKind || Kind == ELF64BEKind);
+ Config->IsLE = (Kind == ELF32LEKind || Kind == ELF64LEKind);
+ Config->Endianness =
+ Config->IsLE ? support::endianness::little : support::endianness::big;
+ Config->IsMips64EL = (Kind == ELF64LEKind && Machine == EM_MIPS);
+ Config->IsRela = Config->Is64 || IsX32 || Config->MipsN32Abi;
+ Config->Pic = Config->Pie || Config->Shared;
+ Config->Wordsize = Config->Is64 ? 8 : 4;
+}
+
// Returns a value of "-format" option.
static bool getBinaryOption(StringRef S) {
if (S == "binary")
@@ -650,7 +775,7 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) {
addLibrary(Arg->getValue());
break;
case OPT_INPUT:
- addFile(Arg->getValue());
+ addFile(Arg->getValue(), /*WithLOption=*/false);
break;
case OPT_alias_script_T:
case OPT_script:
@@ -744,12 +869,7 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
SymbolTable<ELFT> Symtab;
elf::Symtab<ELFT>::X = &Symtab;
Target = createTarget();
- ScriptBase = Script<ELFT>::X = make<LinkerScript<ELFT>>();
- Config->Rela =
- ELFT::Is64Bits || Config->EMachine == EM_X86_64 || Config->MipsN32Abi;
- Config->Mips64EL =
- (Config->EMachine == EM_MIPS && Config->EKind == ELF64LEKind);
Config->MaxPageSize = getMaxPageSize(Args);
Config->ImageBase = getImageBase(Args);
@@ -757,6 +877,14 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
if (Config->OutputFile.empty())
Config->OutputFile = "a.out";
+ // Fail early if the output file or map file is not writable. If a user has a
+ // long link, e.g. due to a large LTO link, they do not wish to run it and
+ // find that it failed because there was a mistake in their command-line.
+ if (!isFileWritable(Config->OutputFile, "output file"))
+ return;
+ if (!isFileWritable(Config->MapFile, "map file"))
+ return;
+
// Use default entry point name if no name was given via the command
// line nor linker scripts. For some reason, MIPS entry point name is
// different from others.
@@ -792,6 +920,11 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
if (ErrorCount)
return;
+ // Some symbols (such as __ehdr_start) are defined lazily only when there
+ // are undefined symbols for them, so we add these to trigger that logic.
+ for (StringRef Sym : Script->Opt.ReferencedSymbols)
+ Symtab.addUndefined(Sym);
+
for (auto *Arg : Args.filtered(OPT_wrap))
Symtab.wrap(Arg->getValue());
@@ -799,12 +932,12 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
// Beyond this point, no new files are added.
// Aggregate all input sections into one place.
for (elf::ObjectFile<ELFT> *F : Symtab.getObjectFiles())
- for (InputSectionBase<ELFT> *S : F->getSections())
- if (S && S != &InputSection<ELFT>::Discarded)
- Symtab.Sections.push_back(S);
+ for (InputSectionBase *S : F->getSections())
+ if (S && S != &InputSection::Discarded)
+ InputSections.push_back(S);
for (BinaryFile *F : Symtab.getBinaryFiles())
- for (InputSectionData *S : F->getSections())
- Symtab.Sections.push_back(cast<InputSection<ELFT>>(S));
+ for (InputSectionBase *S : F->getSections())
+ InputSections.push_back(cast<InputSection>(S));
// Do size optimizations: garbage collection and identical code folding.
if (Config->GcSections)
@@ -814,15 +947,15 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
// MergeInputSection::splitIntoPieces needs to be called before
// any call of MergeInputSection::getOffset. Do that.
- forEach(Symtab.Sections.begin(), Symtab.Sections.end(),
- [](InputSectionBase<ELFT> *S) {
- if (!S->Live)
- return;
- if (Decompressor::isCompressedELFSection(S->Flags, S->Name))
- S->uncompress();
- if (auto *MS = dyn_cast<MergeInputSection<ELFT>>(S))
- MS->splitIntoPieces();
- });
+ parallelForEach(InputSections.begin(), InputSections.end(),
+ [](InputSectionBase *S) {
+ if (!S->Live)
+ return;
+ if (Decompressor::isCompressedELFSection(S->Flags, S->Name))
+ S->uncompress();
+ if (auto *MS = dyn_cast<MergeInputSection>(S))
+ MS->splitIntoPieces();
+ });
// Write the result to the file.
writeResult<ELFT>();