[PATCH] D24060: [lld][ELF] Add support for -b binary
Rafael EspĂndola via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 7 09:36:58 PDT 2016
I am taking a look at this.
A version of the patch is attached. So far I have only clang-formated
it and fixed the linux build.
Cheers,
Rafael
On 1 September 2016 at 08:19, Rafael EspĂndola
<rafael.espindola at gmail.com> wrote:
>> I agree with Rui here. I looked at what it would take to do it without
>> creating an ELF file and it ends up touching a lot of code. Using the
>> existing ELF machinery makes this a very isolated change for a special case
>> feature. However if you see a simple way to implement it without ELF I'm
>> fine with doing so.
>
> Let me give it a try. I am opposed to having to invent an ELF file to
> load things in. It would be equivalent to doing that for LTO IMHO.
>
> Having said that, we do have two ELF writers in LLVM (MC and dwp), so
> you can already try replacing both with the writer you wrote.
>
> Cheers,
> Rafael
-------------- next part --------------
diff --git a/ELF/Config.h b/ELF/Config.h
index f06fe1b..0da4115 100644
--- a/ELF/Config.h
+++ b/ELF/Config.h
@@ -1,144 +1,145 @@
//===- Config.h -------------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_ELF_CONFIG_H
#define LLD_ELF_CONFIG_H
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ELF.h"
#include <vector>
namespace lld {
namespace elf {
class InputFile;
struct Symbol;
enum ELFKind {
ELFNoneKind,
ELF32LEKind,
ELF32BEKind,
ELF64LEKind,
ELF64BEKind
};
// For --build-id.
enum class BuildIdKind { None, Fnv1, Md5, Sha1, Hexstring, Uuid };
// For --discard-{all,locals,none}.
enum class DiscardPolicy { Default, All, Locals, None };
// For --strip-{all,debug}.
enum class StripPolicy { None, All, Debug };
// For --unresolved-symbols.
enum class UnresolvedPolicy { NoUndef, ReportError, Warn, Ignore };
struct SymbolVersion {
llvm::StringRef Name;
bool IsExternCpp;
};
// This struct contains symbols version definition that
// can be found in version script if it is used for link.
struct VersionDefinition {
VersionDefinition(llvm::StringRef Name, size_t Id) : Name(Name), Id(Id) {}
llvm::StringRef Name;
size_t Id;
std::vector<SymbolVersion> Globals;
size_t NameOff; // Offset in string table.
};
// This struct contains the global configuration for the linker.
// Most fields are direct mapping from the command line options
// and such fields have the same name as the corresponding options.
// Most fields are initialized by the driver.
struct Configuration {
Symbol *EntrySym = nullptr;
InputFile *FirstElf = nullptr;
llvm::StringRef DynamicLinker;
llvm::StringRef Entry;
llvm::StringRef Emulation;
llvm::StringRef Fini;
llvm::StringRef Init;
llvm::StringRef LtoAAPipeline;
llvm::StringRef LtoNewPmPasses;
llvm::StringRef OutputFile;
llvm::StringRef SoName;
llvm::StringRef Sysroot;
std::string RPath;
std::vector<VersionDefinition> VersionDefinitions;
std::vector<llvm::StringRef> AuxiliaryList;
std::vector<llvm::StringRef> DynamicList;
std::vector<llvm::StringRef> SearchPaths;
std::vector<llvm::StringRef> Undefined;
std::vector<SymbolVersion> VersionScriptGlobals;
std::vector<uint8_t> BuildIdVector;
bool AllowMultipleDefinition;
bool AsNeeded = false;
+ bool Binary = false;
bool Bsymbolic;
bool BsymbolicFunctions;
bool Demangle = true;
bool DisableVerify;
bool EhFrameHdr;
bool EnableNewDtags;
bool ExportDynamic;
bool FatalWarnings;
bool GcSections;
bool GnuHash = false;
bool ICF;
bool Mips64EL = false;
bool NoGnuUnique;
bool NoUndefinedVersion;
bool Nostdlib;
bool OFormatBinary;
bool Pic;
bool Pie;
bool PrintGcSections;
bool Rela;
bool Relocatable;
bool SaveTemps;
bool Shared;
bool Static = false;
bool SysvHash = true;
bool Target1Rel;
bool Threads;
bool Trace;
bool Verbose;
bool WarnCommon;
bool ZCombreloc;
bool ZExecStack;
bool ZNodelete;
bool ZNow;
bool ZOrigin;
bool ZRelro;
DiscardPolicy Discard;
StripPolicy Strip = StripPolicy::None;
UnresolvedPolicy UnresolvedSymbols;
BuildIdKind BuildId = BuildIdKind::None;
ELFKind EKind = ELFNoneKind;
uint16_t DefaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL;
uint16_t EMachine = llvm::ELF::EM_NONE;
uint64_t EntryAddr = -1;
uint64_t ImageBase;
uint64_t ZStackSize = -1;
unsigned LtoJobs;
unsigned LtoO;
unsigned Optimize;
};
// The only instance of Configuration struct.
extern Configuration *Config;
} // namespace elf
} // namespace lld
#endif
diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp
index 45c6b1b..2fed11d 100644
--- a/ELF/Driver.cpp
+++ b/ELF/Driver.cpp
@@ -1,646 +1,667 @@
//===- Driver.cpp ---------------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Driver.h"
#include "Config.h"
#include "Error.h"
#include "ICF.h"
#include "InputFiles.h"
#include "InputSection.h"
#include "LinkerScript.h"
#include "Strings.h"
#include "SymbolListFile.h"
#include "SymbolTable.h"
#include "Target.h"
#include "Writer.h"
#include "lld/Driver/Driver.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"
#include <cstdlib>
#include <utility>
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
using namespace llvm::sys;
using namespace lld;
using namespace lld::elf;
Configuration *elf::Config;
LinkerDriver *elf::Driver;
bool elf::link(ArrayRef<const char *> Args, raw_ostream &Error) {
HasError = false;
ErrorOS = &Error;
Configuration C;
LinkerDriver D;
ScriptConfiguration SC;
Config = &C;
Driver = &D;
ScriptConfig = &SC;
Driver->main(Args);
return !HasError;
}
// Parses a linker -m option.
static std::pair<ELFKind, uint16_t> parseEmulation(StringRef S) {
if (S.endswith("_fbsd"))
S = S.drop_back(5);
std::pair<ELFKind, uint16_t> Ret =
StringSwitch<std::pair<ELFKind, uint16_t>>(S)
.Case("aarch64elf", {ELF64LEKind, EM_AARCH64})
.Case("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("elf32ppc", {ELF32BEKind, EM_PPC})
.Case("elf64btsmip", {ELF64BEKind, EM_MIPS})
.Case("elf64ltsmip", {ELF64LEKind, EM_MIPS})
.Case("elf64ppc", {ELF64BEKind, EM_PPC64})
.Case("elf_i386", {ELF32LEKind, EM_386})
.Case("elf_iamcu", {ELF32LEKind, EM_IAMCU})
.Case("elf_x86_64", {ELF64LEKind, EM_X86_64})
.Default({ELFNoneKind, EM_NONE});
if (Ret.first == ELFNoneKind) {
if (S == "i386pe" || S == "i386pep" || S == "thumb2pe")
error("Windows targets are not supported on the ELF frontend: " + S);
else
error("unknown emulation: " + S);
}
return Ret;
}
// Returns slices of MB by parsing MB as an archive file.
// Each slice consists of a member file in the archive.
std::vector<MemoryBufferRef>
LinkerDriver::getArchiveMembers(MemoryBufferRef MB) {
std::unique_ptr<Archive> File =
check(Archive::create(MB), "failed to parse archive");
std::vector<MemoryBufferRef> V;
Error Err;
for (const ErrorOr<Archive::Child> &COrErr : File->children(Err)) {
Archive::Child C = check(COrErr, "could not get the child of the archive " +
File->getFileName());
MemoryBufferRef MBRef =
check(C.getMemoryBufferRef(),
"could not get the buffer for a child of the archive " +
File->getFileName());
V.push_back(MBRef);
}
if (Err)
Error(Err);
// Take ownership of memory buffers created for members of thin archives.
for (std::unique_ptr<MemoryBuffer> &MB : File->takeThinBuffers())
OwningMBs.push_back(std::move(MB));
return V;
}
// 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 KnownScript) {
using namespace sys::fs;
if (Config->Verbose)
outs() << Path << "\n";
Optional<MemoryBufferRef> Buffer = readFile(Path);
if (!Buffer.hasValue())
return;
MemoryBufferRef MBRef = *Buffer;
+ if (Config->Binary && !KnownScript) {
+ Files.push_back(make_unique<BinaryFile>(MBRef));
+ return;
+ }
+
switch (identify_magic(MBRef.getBuffer())) {
case file_magic::unknown:
readLinkerScript(MBRef);
return;
case file_magic::archive:
if (WholeArchive) {
for (MemoryBufferRef MB : getArchiveMembers(MBRef))
Files.push_back(createObjectFile(MB, Path));
return;
}
Files.push_back(make_unique<ArchiveFile>(MBRef));
return;
case file_magic::elf_shared_object:
if (Config->Relocatable) {
error("attempted static link of dynamic object " + Path);
return;
}
Files.push_back(createSharedFile(MBRef));
return;
default:
if (InLib)
Files.push_back(make_unique<LazyObjectFile>(MBRef));
else
Files.push_back(createObjectFile(MBRef));
}
}
Optional<MemoryBufferRef> LinkerDriver::readFile(StringRef Path) {
auto MBOrErr = MemoryBuffer::getFile(Path);
if (auto EC = MBOrErr.getError()) {
error(EC, "cannot open " + Path);
return None;
}
std::unique_ptr<MemoryBuffer> &MB = *MBOrErr;
MemoryBufferRef MBRef = MB->getMemBufferRef();
OwningMBs.push_back(std::move(MB)); // take MB ownership
if (Cpio)
Cpio->append(relativeToRoot(Path), MBRef.getBuffer());
return MBRef;
}
// Add a given library by searching it from input search paths.
void LinkerDriver::addLibrary(StringRef Name) {
std::string Path = searchLibrary(Name);
if (Path.empty())
error("unable to find library -l" + Name);
else
addFile(Path);
}
// This function is called on startup. We need this for LTO since
// LTO calls LLVM functions to compile bitcode files to native code.
// Technically this can be delayed until we read bitcode files, but
// we don't bother to do lazily because the initialization is fast.
static void initLLVM(opt::InputArgList &Args) {
InitializeAllTargets();
InitializeAllTargetMCs();
InitializeAllAsmPrinters();
InitializeAllAsmParsers();
// This is a flag to discard all but GlobalValue names.
// We want to enable it by default because it saves memory.
// Disable it only when a developer option (-save-temps) is given.
Driver->Context.setDiscardValueNames(!Config->SaveTemps);
Driver->Context.enableDebugTypeODRUniquing();
// Parse and evaluate -mllvm options.
std::vector<const char *> V;
V.push_back("lld (LLVM option parsing)");
for (auto *Arg : Args.filtered(OPT_mllvm))
V.push_back(Arg->getValue());
cl::ParseCommandLineOptions(V.size(), V.data());
}
// Some command line options or some combinations of them are not allowed.
// This function checks for such errors.
static void checkOptions(opt::InputArgList &Args) {
// The MIPS ABI as of 2016 does not support the GNU-style symbol lookup
// table which is a relatively new feature.
if (Config->EMachine == EM_MIPS && Config->GnuHash)
error("the .gnu.hash section is not compatible with the MIPS target.");
if (Config->EMachine == EM_AMDGPU && !Config->Entry.empty())
error("-e option is not valid for AMDGPU.");
if (Config->Pie && Config->Shared)
error("-shared and -pie may not be used together");
if (Config->Relocatable) {
if (Config->Shared)
error("-r and -shared may not be used together");
if (Config->GcSections)
error("-r and --gc-sections may not be used together");
if (Config->ICF)
error("-r and --icf may not be used together");
if (Config->Pie)
error("-r and -pie may not be used together");
}
}
static StringRef
getString(opt::InputArgList &Args, unsigned Key, StringRef Default = "") {
if (auto *Arg = Args.getLastArg(Key))
return Arg->getValue();
return Default;
}
static int getInteger(opt::InputArgList &Args, unsigned Key, int Default) {
int V = Default;
if (auto *Arg = Args.getLastArg(Key)) {
StringRef S = Arg->getValue();
if (S.getAsInteger(10, V))
error(Arg->getSpelling() + ": number expected, but got " + S);
}
return V;
}
static const char *getReproduceOption(opt::InputArgList &Args) {
if (auto *Arg = Args.getLastArg(OPT_reproduce))
return Arg->getValue();
return getenv("LLD_REPRODUCE");
}
static bool hasZOption(opt::InputArgList &Args, StringRef Key) {
for (auto *Arg : Args.filtered(OPT_z))
if (Key == Arg->getValue())
return true;
return false;
}
static Optional<StringRef>
getZOptionValue(opt::InputArgList &Args, StringRef Key) {
for (auto *Arg : Args.filtered(OPT_z)) {
StringRef Value = Arg->getValue();
size_t Pos = Value.find("=");
if (Pos != StringRef::npos && Key == Value.substr(0, Pos))
return Value.substr(Pos + 1);
}
return None;
}
void LinkerDriver::main(ArrayRef<const char *> ArgsArr) {
ELFOptTable Parser;
opt::InputArgList Args = Parser.parse(ArgsArr.slice(1));
if (Args.hasArg(OPT_help)) {
printHelp(ArgsArr[0]);
return;
}
if (Args.hasArg(OPT_version))
outs() << getVersionString();
if (const char *Path = getReproduceOption(Args)) {
// Note that --reproduce is a debug option so you can ignore it
// if you are trying to understand the whole picture of the code.
ErrorOr<CpioFile *> F = CpioFile::create(Path);
if (F) {
Cpio.reset(*F);
Cpio->append("response.txt", createResponseFile(Args));
Cpio->append("version.txt", getVersionString());
} else
error(F.getError(),
Twine("--reproduce: failed to open ") + Path + ".cpio");
}
readConfigs(Args);
initLLVM(Args);
createFiles(Args);
checkOptions(Args);
if (HasError)
return;
switch (Config->EKind) {
case ELF32LEKind:
link<ELF32LE>(Args);
return;
case ELF32BEKind:
link<ELF32BE>(Args);
return;
case ELF64LEKind:
link<ELF64LE>(Args);
return;
case ELF64BEKind:
link<ELF64BE>(Args);
return;
default:
error("-m or at least a .o file required");
}
}
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::ReportError;
error("unknown --unresolved-symbols value: " + S);
}
return UnresolvedPolicy::ReportError;
}
static bool isOutputFormatBinary(opt::InputArgList &Args) {
if (auto *Arg = Args.getLastArg(OPT_oformat)) {
StringRef S = Arg->getValue();
if (S == "binary")
return true;
error("unknown --oformat value: " + S);
}
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) {
auto *Arg =
Args.getLastArg(OPT_discard_all, OPT_discard_locals, OPT_discard_none);
if (!Arg)
return DiscardPolicy::Default;
if (Arg->getOption().getID() == OPT_discard_all)
return DiscardPolicy::All;
if (Arg->getOption().getID() == OPT_discard_locals)
return DiscardPolicy::Locals;
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;
}
// 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) = parseEmulation(S);
Config->Emulation = S;
}
Config->AllowMultipleDefinition = Args.hasArg(OPT_allow_multiple_definition);
Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic);
Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions);
Config->Demangle = getArg(Args, OPT_demangle, OPT_no_demangle, true);
Config->DisableVerify = Args.hasArg(OPT_disable_verify);
Config->Discard = getDiscardOption(Args);
Config->EhFrameHdr = Args.hasArg(OPT_eh_frame_hdr);
Config->EnableNewDtags = !Args.hasArg(OPT_disable_new_dtags);
Config->ExportDynamic = Args.hasArg(OPT_export_dynamic);
Config->FatalWarnings = Args.hasArg(OPT_fatal_warnings);
Config->GcSections = getArg(Args, OPT_gc_sections, OPT_no_gc_sections, false);
Config->ICF = Args.hasArg(OPT_icf);
Config->NoGnuUnique = Args.hasArg(OPT_no_gnu_unique);
Config->NoUndefinedVersion = Args.hasArg(OPT_no_undefined_version);
Config->Nostdlib = Args.hasArg(OPT_nostdlib);
Config->Pie = Args.hasArg(OPT_pie);
Config->PrintGcSections = Args.hasArg(OPT_print_gc_sections);
Config->Relocatable = Args.hasArg(OPT_relocatable);
Config->SaveTemps = Args.hasArg(OPT_save_temps);
Config->Shared = Args.hasArg(OPT_shared);
Config->Target1Rel = getArg(Args, OPT_target1_rel, OPT_target1_abs, false);
Config->Threads = Args.hasArg(OPT_threads);
Config->Trace = Args.hasArg(OPT_trace);
Config->Verbose = Args.hasArg(OPT_verbose);
Config->WarnCommon = Args.hasArg(OPT_warn_common);
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->LtoJobs = getInteger(Args, OPT_lto_jobs, 1);
if (Config->LtoJobs == 0)
error("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");
if (!Config->Relocatable)
Config->Strip = getStripOption(Args);
if (Optional<StringRef> Value = getZOptionValue(Args, "stack-size"))
if (Value->getAsInteger(0, Config->ZStackSize))
error("invalid stack size: " + *Value);
// 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);
}
// Parse --build-id or --build-id=<style>.
if (Args.hasArg(OPT_build_id))
Config->BuildId = BuildIdKind::Fnv1;
if (auto *Arg = Args.getLastArg(OPT_build_id_eq)) {
StringRef S = Arg->getValue();
if (S == "md5") {
Config->BuildId = BuildIdKind::Md5;
} else if (S == "sha1") {
Config->BuildId = BuildIdKind::Sha1;
} else if (S == "uuid") {
Config->BuildId = BuildIdKind::Uuid;
} else if (S == "none") {
Config->BuildId = BuildIdKind::None;
} else if (S.startswith("0x")) {
Config->BuildId = BuildIdKind::Hexstring;
Config->BuildIdVector = parseHex(S.substr(2));
} else {
error("unknown --build-id style: " + S);
}
}
Config->OFormatBinary = isOutputFormatBinary(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());
Config->UnresolvedSymbols = getUnresolvedSymbolOption(Args);
if (auto *Arg = Args.getLastArg(OPT_dynamic_list))
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
parseDynamicList(*Buffer);
for (auto *Arg : Args.filtered(OPT_export_dynamic_symbol))
Config->DynamicList.push_back(Arg->getValue());
if (auto *Arg = Args.getLastArg(OPT_version_script))
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
readVersionScript(*Buffer);
}
void LinkerDriver::createFiles(opt::InputArgList &Args) {
for (auto *Arg : Args) {
switch (Arg->getOption().getID()) {
case OPT_l:
addLibrary(Arg->getValue());
break;
- case OPT_alias_script_T:
case OPT_INPUT:
- case OPT_script:
addFile(Arg->getValue());
break;
+ case OPT_alias_script_T:
+ case OPT_script:
+ addFile(Arg->getValue(), true);
+ break;
case OPT_as_needed:
Config->AsNeeded = true;
break;
+ case OPT_format: {
+ StringRef Val = Arg->getValue();
+ if (Val == "elf" || Val == "default") {
+ Config->Binary = false;
+ break;
+ }
+ if (Val == "binary") {
+ Config->Binary = true;
+ break;
+ }
+ error("unknown " + Arg->getSpelling() + " format: " + Arg->getValue() +
+ " (supported formats: elf, default, binary)");
+ break;
+ }
case OPT_no_as_needed:
Config->AsNeeded = false;
break;
case OPT_Bstatic:
Config->Static = true;
break;
case OPT_Bdynamic:
Config->Static = false;
break;
case OPT_whole_archive:
WholeArchive = true;
break;
case OPT_no_whole_archive:
WholeArchive = false;
break;
case OPT_start_lib:
InLib = true;
break;
case OPT_end_lib:
InLib = false;
break;
}
}
if (Files.empty() && !HasError)
error("no input files.");
// If -m <machine_type> was not given, infer it from object files.
if (Config->EKind == ELFNoneKind) {
for (std::unique_ptr<InputFile> &F : Files) {
if (F->EKind == ELFNoneKind)
continue;
Config->EKind = F->EKind;
Config->EMachine = F->EMachine;
break;
}
}
}
// Do actual linking. Note that when this function is called,
// all linker scripts have already been parsed.
template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
SymbolTable<ELFT> Symtab;
elf::Symtab<ELFT>::X = &Symtab;
std::unique_ptr<TargetInfo> TI(createTarget());
Target = TI.get();
LinkerScript<ELFT> LS;
Script<ELFT>::X = &LS;
Config->Rela = ELFT::Is64Bits || Config->EMachine == EM_X86_64;
Config->Mips64EL =
(Config->EMachine == EM_MIPS && Config->EKind == ELF64LEKind);
// Add entry symbol. Note that AMDGPU binaries have no entry points.
if (Config->Entry.empty() && !Config->Shared && !Config->Relocatable &&
Config->EMachine != EM_AMDGPU)
Config->Entry = (Config->EMachine == EM_MIPS) ? "__start" : "_start";
// Default output filename is "a.out" by the Unix tradition.
if (Config->OutputFile.empty())
Config->OutputFile = "a.out";
// Handle --trace-symbol.
for (auto *Arg : Args.filtered(OPT_trace_symbol))
Symtab.trace(Arg->getValue());
// Set either EntryAddr (if S is a number) or EntrySym (otherwise).
if (!Config->Entry.empty()) {
StringRef S = Config->Entry;
if (S.getAsInteger(0, Config->EntryAddr))
Config->EntrySym = Symtab.addUndefined(S);
}
// Initialize Config->ImageBase.
if (auto *Arg = Args.getLastArg(OPT_image_base)) {
StringRef S = Arg->getValue();
if (S.getAsInteger(0, Config->ImageBase))
error(Arg->getSpelling() + ": number expected, but got " + S);
else if ((Config->ImageBase % Target->PageSize) != 0)
warning(Arg->getSpelling() + ": address isn't multiple of page size");
} else {
Config->ImageBase = Config->Pic ? 0 : Target->DefaultImageBase;
}
for (std::unique_ptr<InputFile> &F : Files)
Symtab.addFile(std::move(F));
if (HasError)
return; // There were duplicate symbols or incompatible files
Symtab.scanUndefinedFlags();
Symtab.scanShlibUndefined();
Symtab.scanDynamicList();
Symtab.scanVersionScript();
Symtab.addCombinedLtoObject();
if (HasError)
return;
for (auto *Arg : Args.filtered(OPT_wrap))
Symtab.wrap(Arg->getValue());
// Write the result to the file.
if (Config->GcSections)
markLive<ELFT>();
if (Config->ICF)
doIcf<ELFT>();
// MergeInputSection::splitIntoPieces needs to be called before
// any call of MergeInputSection::getOffset. Do that.
for (const std::unique_ptr<elf::ObjectFile<ELFT>> &F :
Symtab.getObjectFiles())
for (InputSectionBase<ELFT> *S : F->getSections()) {
if (!S || S == &InputSection<ELFT>::Discarded || !S->Live)
continue;
if (S->Compressed)
S->uncompress();
if (auto *MS = dyn_cast<MergeInputSection<ELFT>>(S))
MS->splitIntoPieces();
}
writeResult<ELFT>();
}
diff --git a/ELF/Driver.h b/ELF/Driver.h
index 9726430..b16244f 100644
--- a/ELF/Driver.h
+++ b/ELF/Driver.h
@@ -1,84 +1,84 @@
//===- Driver.h -------------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_ELF_DRIVER_H
#define LLD_ELF_DRIVER_H
#include "SymbolTable.h"
#include "lld/Core/LLVM.h"
#include "lld/Core/Reproduce.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/raw_ostream.h"
namespace lld {
namespace elf {
extern class LinkerDriver *Driver;
class LinkerDriver {
public:
void main(ArrayRef<const char *> Args);
- void addFile(StringRef Path);
+ void addFile(StringRef Path, bool KnownScript = false);
void addLibrary(StringRef Name);
llvm::LLVMContext Context; // to parse bitcode ifles
std::unique_ptr<CpioFile> Cpio; // for reproduce
private:
std::vector<MemoryBufferRef> getArchiveMembers(MemoryBufferRef MB);
llvm::Optional<MemoryBufferRef> readFile(StringRef Path);
void readConfigs(llvm::opt::InputArgList &Args);
void createFiles(llvm::opt::InputArgList &Args);
template <class ELFT> void link(llvm::opt::InputArgList &Args);
// True if we are in --whole-archive and --no-whole-archive.
bool WholeArchive = false;
// True if we are in --start-lib and --end-lib.
bool InLib = false;
llvm::BumpPtrAllocator Alloc;
std::vector<std::unique_ptr<InputFile>> Files;
std::vector<std::unique_ptr<MemoryBuffer>> OwningMBs;
};
// Parses command line options.
class ELFOptTable : public llvm::opt::OptTable {
public:
ELFOptTable();
llvm::opt::InputArgList parse(ArrayRef<const char *> Argv);
private:
llvm::BumpPtrAllocator Alloc;
};
// Create enum with OPT_xxx values for each option in Options.td
enum {
OPT_INVALID = 0,
#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11) OPT_##ID,
#include "Options.inc"
#undef OPTION
};
void printHelp(const char *Argv0);
std::string getVersionString();
std::vector<uint8_t> parseHexstring(StringRef S);
std::string createResponseFile(const llvm::opt::InputArgList &Args);
std::string findFromSearchPaths(StringRef Path);
std::string searchLibrary(StringRef Path);
std::string buildSysrootedPath(llvm::StringRef Dir, llvm::StringRef File);
} // namespace elf
} // namespace lld
#endif
diff --git a/ELF/InputFiles.cpp b/ELF/InputFiles.cpp
index 4db9907..64212d2 100644
--- a/ELF/InputFiles.cpp
+++ b/ELF/InputFiles.cpp
@@ -1,851 +1,902 @@
//===- InputFiles.cpp -----------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "InputFiles.h"
#include "Driver.h"
#include "Error.h"
#include "InputSection.h"
#include "LinkerScript.h"
+#include "SimpleELFWriter.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/CodeGen/Analysis.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
+#include "llvm/MC/StringTableBuilder.h"
+#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
using namespace llvm::sys::fs;
using namespace lld;
using namespace lld::elf;
// Returns "(internal)", "foo.a(bar.o)" or "baz.o".
std::string elf::getFilename(const InputFile *F) {
if (!F)
return "(internal)";
if (!F->ArchiveName.empty())
return (F->ArchiveName + "(" + F->getName() + ")").str();
return F->getName();
}
template <class ELFT>
static ELFFile<ELFT> createELFObj(MemoryBufferRef MB) {
std::error_code EC;
ELFFile<ELFT> F(MB.getBuffer(), EC);
if (EC)
error(EC, "failed to read " + MB.getBufferIdentifier());
return F;
}
template <class ELFT> static ELFKind getELFKind() {
if (ELFT::TargetEndianness == support::little)
return ELFT::Is64Bits ? ELF64LEKind : ELF32LEKind;
return ELFT::Is64Bits ? ELF64BEKind : ELF32BEKind;
}
template <class ELFT>
ELFFileBase<ELFT>::ELFFileBase(Kind K, MemoryBufferRef MB)
: InputFile(K, MB), ELFObj(createELFObj<ELFT>(MB)) {
EKind = getELFKind<ELFT>();
EMachine = ELFObj.getHeader()->e_machine;
}
template <class ELFT>
typename ELFT::SymRange ELFFileBase<ELFT>::getElfSymbols(bool OnlyGlobals) {
if (!Symtab)
return Elf_Sym_Range(nullptr, nullptr);
Elf_Sym_Range Syms = ELFObj.symbols(Symtab);
uint32_t NumSymbols = std::distance(Syms.begin(), Syms.end());
uint32_t FirstNonLocal = Symtab->sh_info;
if (FirstNonLocal > NumSymbols)
fatal(getFilename(this) + ": invalid sh_info in symbol table");
if (OnlyGlobals)
return makeArrayRef(Syms.begin() + FirstNonLocal, Syms.end());
return makeArrayRef(Syms.begin(), Syms.end());
}
template <class ELFT>
uint32_t ELFFileBase<ELFT>::getSectionIndex(const Elf_Sym &Sym) const {
uint32_t I = Sym.st_shndx;
if (I == ELF::SHN_XINDEX)
return ELFObj.getExtendedSymbolTableIndex(&Sym, Symtab, SymtabSHNDX);
if (I >= ELF::SHN_LORESERVE)
return 0;
return I;
}
template <class ELFT> void ELFFileBase<ELFT>::initStringTable() {
if (!Symtab)
return;
StringTable = check(ELFObj.getStringTableForSymtab(*Symtab));
}
template <class ELFT>
elf::ObjectFile<ELFT>::ObjectFile(MemoryBufferRef M)
: ELFFileBase<ELFT>(Base::ObjectKind, M) {}
template <class ELFT>
ArrayRef<SymbolBody *> elf::ObjectFile<ELFT>::getNonLocalSymbols() {
if (!this->Symtab)
return this->SymbolBodies;
uint32_t FirstNonLocal = this->Symtab->sh_info;
return makeArrayRef(this->SymbolBodies).slice(FirstNonLocal);
}
template <class ELFT>
ArrayRef<SymbolBody *> elf::ObjectFile<ELFT>::getLocalSymbols() {
if (!this->Symtab)
return this->SymbolBodies;
uint32_t FirstNonLocal = this->Symtab->sh_info;
return makeArrayRef(this->SymbolBodies).slice(1, FirstNonLocal - 1);
}
template <class ELFT>
ArrayRef<SymbolBody *> elf::ObjectFile<ELFT>::getSymbols() {
if (!this->Symtab)
return this->SymbolBodies;
return makeArrayRef(this->SymbolBodies).slice(1);
}
template <class ELFT> uint32_t elf::ObjectFile<ELFT>::getMipsGp0() const {
if (ELFT::Is64Bits && MipsOptions && MipsOptions->Reginfo)
return MipsOptions->Reginfo->ri_gp_value;
if (!ELFT::Is64Bits && MipsReginfo && MipsReginfo->Reginfo)
return MipsReginfo->Reginfo->ri_gp_value;
return 0;
}
template <class ELFT>
void elf::ObjectFile<ELFT>::parse(DenseSet<StringRef> &ComdatGroups) {
// Read section and symbol tables.
initializeSections(ComdatGroups);
initializeSymbols();
}
// Sections with SHT_GROUP and comdat bits define comdat section groups.
// They are identified and deduplicated by group name. This function
// returns a group name.
template <class ELFT>
StringRef elf::ObjectFile<ELFT>::getShtGroupSignature(const Elf_Shdr &Sec) {
const ELFFile<ELFT> &Obj = this->ELFObj;
const Elf_Shdr *Symtab = check(Obj.getSection(Sec.sh_link));
const Elf_Sym *Sym = Obj.getSymbol(Symtab, Sec.sh_info);
StringRef Strtab = check(Obj.getStringTableForSymtab(*Symtab));
return check(Sym->getName(Strtab));
}
template <class ELFT>
ArrayRef<typename elf::ObjectFile<ELFT>::Elf_Word>
elf::ObjectFile<ELFT>::getShtGroupEntries(const Elf_Shdr &Sec) {
const ELFFile<ELFT> &Obj = this->ELFObj;
ArrayRef<Elf_Word> Entries =
check(Obj.template getSectionContentsAsArray<Elf_Word>(&Sec));
if (Entries.empty() || Entries[0] != GRP_COMDAT)
fatal(getFilename(this) + ": unsupported SHT_GROUP format");
return Entries.slice(1);
}
template <class ELFT>
bool elf::ObjectFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
// We don't merge sections if -O0 (default is -O1). This makes sometimes
// the linker significantly faster, although the output will be bigger.
if (Config->Optimize == 0)
return false;
// We don't merge if linker script has SECTIONS command. When script
// do layout it can merge several sections with different attributes
// into single output sections. We currently do not support adding
// mergeable input sections to regular output ones as well as adding
// regular input sections to mergeable output.
if (ScriptConfig->HasContents)
return false;
// A mergeable section with size 0 is useless because they don't have
// any data to merge. A mergeable string section with size 0 can be
// argued as invalid because it doesn't end with a null character.
// We'll avoid a mess by handling them as if they were non-mergeable.
if (Sec.sh_size == 0)
return false;
uintX_t Flags = Sec.sh_flags;
if (!(Flags & SHF_MERGE))
return false;
if (Flags & SHF_WRITE)
fatal(getFilename(this) + ": writable SHF_MERGE section is not supported");
uintX_t EntSize = Sec.sh_entsize;
if (!EntSize || Sec.sh_size % EntSize)
fatal(getFilename(this) +
": SHF_MERGE section size must be a multiple of sh_entsize");
// Don't try to merge if the alignment is larger than the sh_entsize and this
// is not SHF_STRINGS.
//
// Since this is not a SHF_STRINGS, we would need to pad after every entity.
// It would be equivalent for the producer of the .o to just set a larger
// sh_entsize.
if (Flags & SHF_STRINGS)
return true;
return Sec.sh_addralign <= EntSize;
}
template <class ELFT>
void elf::ObjectFile<ELFT>::initializeSections(
DenseSet<StringRef> &ComdatGroups) {
uint64_t Size = this->ELFObj.getNumSections();
Sections.resize(Size);
unsigned I = -1;
const ELFFile<ELFT> &Obj = this->ELFObj;
for (const Elf_Shdr &Sec : Obj.sections()) {
++I;
if (Sections[I] == &InputSection<ELFT>::Discarded)
continue;
switch (Sec.sh_type) {
case SHT_GROUP:
Sections[I] = &InputSection<ELFT>::Discarded;
if (ComdatGroups.insert(getShtGroupSignature(Sec)).second)
continue;
for (uint32_t SecIndex : getShtGroupEntries(Sec)) {
if (SecIndex >= Size)
fatal(getFilename(this) + ": invalid section index in group: " +
Twine(SecIndex));
Sections[SecIndex] = &InputSection<ELFT>::Discarded;
}
break;
case SHT_SYMTAB:
this->Symtab = &Sec;
break;
case SHT_SYMTAB_SHNDX:
this->SymtabSHNDX = check(Obj.getSHNDXTable(Sec));
break;
case SHT_STRTAB:
case SHT_NULL:
break;
case SHT_RELA:
case SHT_REL: {
// This section contains relocation information.
// If -r is given, we do not interpret or apply relocation
// but just copy relocation sections to output.
if (Config->Relocatable) {
Sections[I] = new (IAlloc.Allocate()) InputSection<ELFT>(this, &Sec);
break;
}
// Find the relocation target section and associate this
// section with it.
InputSectionBase<ELFT> *Target = getRelocTarget(Sec);
if (!Target)
break;
if (auto *S = dyn_cast<InputSection<ELFT>>(Target)) {
S->RelocSections.push_back(&Sec);
break;
}
if (auto *S = dyn_cast<EhInputSection<ELFT>>(Target)) {
if (S->RelocSection)
fatal(
getFilename(this) +
": multiple relocation sections to .eh_frame are not supported");
S->RelocSection = &Sec;
break;
}
fatal(getFilename(this) +
": relocations pointing to SHF_MERGE are not supported");
}
case SHT_ARM_ATTRIBUTES:
// FIXME: ARM meta-data section. At present attributes are ignored,
// they can be used to reason about object compatibility.
Sections[I] = &InputSection<ELFT>::Discarded;
break;
case SHT_MIPS_REGINFO:
MipsReginfo.reset(new MipsReginfoInputSection<ELFT>(this, &Sec));
Sections[I] = MipsReginfo.get();
break;
case SHT_MIPS_OPTIONS:
MipsOptions.reset(new MipsOptionsInputSection<ELFT>(this, &Sec));
Sections[I] = MipsOptions.get();
break;
case SHT_MIPS_ABIFLAGS:
MipsAbiFlags.reset(new MipsAbiFlagsInputSection<ELFT>(this, &Sec));
Sections[I] = MipsAbiFlags.get();
break;
default:
Sections[I] = createInputSection(Sec);
}
}
}
template <class ELFT>
InputSectionBase<ELFT> *
elf::ObjectFile<ELFT>::getRelocTarget(const Elf_Shdr &Sec) {
uint32_t Idx = Sec.sh_info;
if (Idx >= Sections.size())
fatal(getFilename(this) + ": invalid relocated section index: " +
Twine(Idx));
InputSectionBase<ELFT> *Target = Sections[Idx];
// Strictly speaking, a relocation section must be included in the
// group of the section it relocates. However, LLVM 3.3 and earlier
// would fail to do so, so we gracefully handle that case.
if (Target == &InputSection<ELFT>::Discarded)
return nullptr;
if (!Target)
fatal(getFilename(this) + ": unsupported relocation reference");
return Target;
}
template <class ELFT>
InputSectionBase<ELFT> *
elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
StringRef Name = check(this->ELFObj.getSectionName(&Sec));
// .note.GNU-stack is a marker section to control the presence of
// PT_GNU_STACK segment in outputs. Since the presence of the segment
// is controlled only by the command line option (-z execstack) in LLD,
// .note.GNU-stack is ignored.
if (Name == ".note.GNU-stack")
return &InputSection<ELFT>::Discarded;
if (Name == ".note.GNU-split-stack") {
error("objects using splitstacks are not supported");
return &InputSection<ELFT>::Discarded;
}
if (Config->Strip != StripPolicy::None && Name.startswith(".debug"))
return &InputSection<ELFT>::Discarded;
// The linker merges EH (exception handling) frames and creates a
// .eh_frame_hdr section for runtime. So we handle them with a special
// class. For relocatable outputs, they are just passed through.
if (Name == ".eh_frame" && !Config->Relocatable)
return new (EHAlloc.Allocate()) EhInputSection<ELFT>(this, &Sec);
if (shouldMerge(Sec))
return new (MAlloc.Allocate()) MergeInputSection<ELFT>(this, &Sec);
return new (IAlloc.Allocate()) InputSection<ELFT>(this, &Sec);
}
template <class ELFT> void elf::ObjectFile<ELFT>::initializeSymbols() {
this->initStringTable();
Elf_Sym_Range Syms = this->getElfSymbols(false);
uint32_t NumSymbols = std::distance(Syms.begin(), Syms.end());
SymbolBodies.reserve(NumSymbols);
for (const Elf_Sym &Sym : Syms)
SymbolBodies.push_back(createSymbolBody(&Sym));
}
template <class ELFT>
InputSectionBase<ELFT> *
elf::ObjectFile<ELFT>::getSection(const Elf_Sym &Sym) const {
uint32_t Index = this->getSectionIndex(Sym);
if (Index == 0)
return nullptr;
if (Index >= Sections.size())
fatal(getFilename(this) + ": invalid section index: " + Twine(Index));
InputSectionBase<ELFT> *S = Sections[Index];
// We found that GNU assembler 2.17.50 [FreeBSD] 2007-07-03
// could generate broken objects. STT_SECTION symbols can be
// associated with SHT_REL[A]/SHT_SYMTAB/SHT_STRTAB sections.
// In this case it is fine for section to be null here as we
// do not allocate sections of these types.
if (!S || S == &InputSectionBase<ELFT>::Discarded)
return S;
return S->Repl;
}
template <class ELFT>
SymbolBody *elf::ObjectFile<ELFT>::createSymbolBody(const Elf_Sym *Sym) {
int Binding = Sym->getBinding();
InputSectionBase<ELFT> *Sec = getSection(*Sym);
if (Binding == STB_LOCAL) {
if (Sym->st_shndx == SHN_UNDEF)
return new (this->Alloc)
Undefined(Sym->st_name, Sym->st_other, Sym->getType(), this);
return new (this->Alloc) DefinedRegular<ELFT>(*Sym, Sec);
}
StringRef Name = check(Sym->getName(this->StringTable));
switch (Sym->st_shndx) {
case SHN_UNDEF:
return elf::Symtab<ELFT>::X
->addUndefined(Name, Binding, Sym->st_other, Sym->getType(),
/*CanOmitFromDynSym*/ false, /*HasUnnamedAddr*/ false,
this)
->body();
case SHN_COMMON:
return elf::Symtab<ELFT>::X
->addCommon(Name, Sym->st_size, Sym->st_value, Binding, Sym->st_other,
Sym->getType(), /*HasUnnamedAddr*/ false, this)
->body();
}
switch (Binding) {
default:
fatal(getFilename(this) + ": unexpected binding: " + Twine(Binding));
case STB_GLOBAL:
case STB_WEAK:
case STB_GNU_UNIQUE:
if (Sec == &InputSection<ELFT>::Discarded)
return elf::Symtab<ELFT>::X
->addUndefined(Name, Binding, Sym->st_other, Sym->getType(),
/*CanOmitFromDynSym*/ false,
/*HasUnnamedAddr*/ false, this)
->body();
return elf::Symtab<ELFT>::X->addRegular(Name, *Sym, Sec)->body();
}
}
template <class ELFT> void ArchiveFile::parse() {
File = check(Archive::create(MB), "failed to parse archive");
// Read the symbol table to construct Lazy objects.
for (const Archive::Symbol &Sym : File->symbols())
Symtab<ELFT>::X->addLazyArchive(this, Sym);
}
// Returns a buffer pointing to a member file containing a given symbol.
MemoryBufferRef ArchiveFile::getMember(const Archive::Symbol *Sym) {
Archive::Child C =
check(Sym->getMember(),
"could not get the member for symbol " + Sym->getName());
if (!Seen.insert(C.getChildOffset()).second)
return MemoryBufferRef();
MemoryBufferRef Ret =
check(C.getMemoryBufferRef(),
"could not get the buffer for the member defining symbol " +
Sym->getName());
if (C.getParent()->isThin() && Driver->Cpio)
Driver->Cpio->append(relativeToRoot(check(C.getFullName())),
Ret.getBuffer());
return Ret;
}
template <class ELFT>
SharedFile<ELFT>::SharedFile(MemoryBufferRef M)
: ELFFileBase<ELFT>(Base::SharedKind, M), AsNeeded(Config->AsNeeded) {}
template <class ELFT>
const typename ELFT::Shdr *
SharedFile<ELFT>::getSection(const Elf_Sym &Sym) const {
uint32_t Index = this->getSectionIndex(Sym);
if (Index == 0)
return nullptr;
return check(this->ELFObj.getSection(Index));
}
// Partially parse the shared object file so that we can call
// getSoName on this object.
template <class ELFT> void SharedFile<ELFT>::parseSoName() {
typedef typename ELFT::Dyn Elf_Dyn;
typedef typename ELFT::uint uintX_t;
const Elf_Shdr *DynamicSec = nullptr;
const ELFFile<ELFT> Obj = this->ELFObj;
for (const Elf_Shdr &Sec : Obj.sections()) {
switch (Sec.sh_type) {
default:
continue;
case SHT_DYNSYM:
this->Symtab = &Sec;
break;
case SHT_DYNAMIC:
DynamicSec = &Sec;
break;
case SHT_SYMTAB_SHNDX:
this->SymtabSHNDX = check(Obj.getSHNDXTable(Sec));
break;
case SHT_GNU_versym:
this->VersymSec = &Sec;
break;
case SHT_GNU_verdef:
this->VerdefSec = &Sec;
break;
}
}
this->initStringTable();
SoName = this->getName();
if (!DynamicSec)
return;
auto *Begin =
reinterpret_cast<const Elf_Dyn *>(Obj.base() + DynamicSec->sh_offset);
const Elf_Dyn *End = Begin + DynamicSec->sh_size / sizeof(Elf_Dyn);
for (const Elf_Dyn &Dyn : make_range(Begin, End)) {
if (Dyn.d_tag == DT_SONAME) {
uintX_t Val = Dyn.getVal();
if (Val >= this->StringTable.size())
fatal(getFilename(this) + ": invalid DT_SONAME entry");
SoName = StringRef(this->StringTable.data() + Val);
return;
}
}
}
// Parse the version definitions in the object file if present. Returns a vector
// whose nth element contains a pointer to the Elf_Verdef for version identifier
// n. Version identifiers that are not definitions map to nullptr. The array
// always has at least length 1.
template <class ELFT>
std::vector<const typename ELFT::Verdef *>
SharedFile<ELFT>::parseVerdefs(const Elf_Versym *&Versym) {
std::vector<const Elf_Verdef *> Verdefs(1);
// We only need to process symbol versions for this DSO if it has both a
// versym and a verdef section, which indicates that the DSO contains symbol
// version definitions.
if (!VersymSec || !VerdefSec)
return Verdefs;
// The location of the first global versym entry.
Versym = reinterpret_cast<const Elf_Versym *>(this->ELFObj.base() +
VersymSec->sh_offset) +
this->Symtab->sh_info;
// We cannot determine the largest verdef identifier without inspecting
// every Elf_Verdef, but both bfd and gold assign verdef identifiers
// sequentially starting from 1, so we predict that the largest identifier
// will be VerdefCount.
unsigned VerdefCount = VerdefSec->sh_info;
Verdefs.resize(VerdefCount + 1);
// Build the Verdefs array by following the chain of Elf_Verdef objects
// from the start of the .gnu.version_d section.
const uint8_t *Verdef = this->ELFObj.base() + VerdefSec->sh_offset;
for (unsigned I = 0; I != VerdefCount; ++I) {
auto *CurVerdef = reinterpret_cast<const Elf_Verdef *>(Verdef);
Verdef += CurVerdef->vd_next;
unsigned VerdefIndex = CurVerdef->vd_ndx;
if (Verdefs.size() <= VerdefIndex)
Verdefs.resize(VerdefIndex + 1);
Verdefs[VerdefIndex] = CurVerdef;
}
return Verdefs;
}
// Fully parse the shared object file. This must be called after parseSoName().
template <class ELFT> void SharedFile<ELFT>::parseRest() {
// Create mapping from version identifiers to Elf_Verdef entries.
const Elf_Versym *Versym = nullptr;
std::vector<const Elf_Verdef *> Verdefs = parseVerdefs(Versym);
Elf_Sym_Range Syms = this->getElfSymbols(true);
for (const Elf_Sym &Sym : Syms) {
unsigned VersymIndex = 0;
if (Versym) {
VersymIndex = Versym->vs_index;
++Versym;
}
StringRef Name = check(Sym.getName(this->StringTable));
if (Sym.isUndefined()) {
Undefs.push_back(Name);
continue;
}
if (Versym) {
// Ignore local symbols and non-default versions.
if (VersymIndex == VER_NDX_LOCAL || (VersymIndex & VERSYM_HIDDEN))
continue;
}
const Elf_Verdef *V =
VersymIndex == VER_NDX_GLOBAL ? nullptr : Verdefs[VersymIndex];
elf::Symtab<ELFT>::X->addShared(this, Name, Sym, V);
}
}
static ELFKind getBitcodeELFKind(MemoryBufferRef MB) {
Triple T(getBitcodeTargetTriple(MB, Driver->Context));
if (T.isLittleEndian())
return T.isArch64Bit() ? ELF64LEKind : ELF32LEKind;
return T.isArch64Bit() ? ELF64BEKind : ELF32BEKind;
}
static uint8_t getBitcodeMachineKind(MemoryBufferRef MB) {
Triple T(getBitcodeTargetTriple(MB, Driver->Context));
switch (T.getArch()) {
case Triple::aarch64:
return EM_AARCH64;
case Triple::arm:
return EM_ARM;
case Triple::mips:
case Triple::mipsel:
case Triple::mips64:
case Triple::mips64el:
return EM_MIPS;
case Triple::ppc:
return EM_PPC;
case Triple::ppc64:
return EM_PPC64;
case Triple::x86:
return T.isOSIAMCU() ? EM_IAMCU : EM_386;
case Triple::x86_64:
return EM_X86_64;
default:
fatal(MB.getBufferIdentifier() +
": could not infer e_machine from bitcode target triple " + T.str());
}
}
BitcodeFile::BitcodeFile(MemoryBufferRef MB) : InputFile(BitcodeKind, MB) {
EKind = getBitcodeELFKind(MB);
EMachine = getBitcodeMachineKind(MB);
}
static uint8_t getGvVisibility(const GlobalValue *GV) {
switch (GV->getVisibility()) {
case GlobalValue::DefaultVisibility:
return STV_DEFAULT;
case GlobalValue::HiddenVisibility:
return STV_HIDDEN;
case GlobalValue::ProtectedVisibility:
return STV_PROTECTED;
}
llvm_unreachable("unknown visibility");
}
template <class ELFT>
Symbol *BitcodeFile::createSymbol(const DenseSet<const Comdat *> &KeptComdats,
const IRObjectFile &Obj,
const BasicSymbolRef &Sym) {
const GlobalValue *GV = Obj.getSymbolGV(Sym.getRawDataRefImpl());
SmallString<64> Name;
raw_svector_ostream OS(Name);
Sym.printName(OS);
StringRef NameRef = Saver.save(StringRef(Name));
uint32_t Flags = Sym.getFlags();
uint32_t Binding = (Flags & BasicSymbolRef::SF_Weak) ? STB_WEAK : STB_GLOBAL;
uint8_t Type = STT_NOTYPE;
uint8_t Visibility;
bool CanOmitFromDynSym = false;
bool HasUnnamedAddr = false;
// FIXME: Expose a thread-local flag for module asm symbols.
if (GV) {
if (GV->isThreadLocal())
Type = STT_TLS;
CanOmitFromDynSym = canBeOmittedFromSymbolTable(GV);
Visibility = getGvVisibility(GV);
HasUnnamedAddr =
GV->getUnnamedAddr() == llvm::GlobalValue::UnnamedAddr::Global;
} else {
// FIXME: Set SF_Hidden flag correctly for module asm symbols, and expose
// protected visibility.
Visibility = STV_DEFAULT;
}
if (GV)
if (const Comdat *C = GV->getComdat())
if (!KeptComdats.count(C))
return Symtab<ELFT>::X->addUndefined(NameRef, Binding, Visibility, Type,
CanOmitFromDynSym, HasUnnamedAddr,
this);
const Module &M = Obj.getModule();
if (Flags & BasicSymbolRef::SF_Undefined)
return Symtab<ELFT>::X->addUndefined(NameRef, Binding, Visibility, Type,
CanOmitFromDynSym, HasUnnamedAddr,
this);
if (Flags & BasicSymbolRef::SF_Common) {
// FIXME: Set SF_Common flag correctly for module asm symbols, and expose
// size and alignment.
assert(GV);
const DataLayout &DL = M.getDataLayout();
uint64_t Size = DL.getTypeAllocSize(GV->getValueType());
return Symtab<ELFT>::X->addCommon(NameRef, Size, GV->getAlignment(),
Binding, Visibility, STT_OBJECT,
HasUnnamedAddr, this);
}
return Symtab<ELFT>::X->addBitcode(NameRef, Binding, Visibility, Type,
CanOmitFromDynSym, HasUnnamedAddr, this);
}
bool BitcodeFile::shouldSkip(uint32_t Flags) {
return !(Flags & BasicSymbolRef::SF_Global) ||
(Flags & BasicSymbolRef::SF_FormatSpecific);
}
template <class ELFT>
void BitcodeFile::parse(DenseSet<StringRef> &ComdatGroups) {
Obj = check(IRObjectFile::create(MB, Driver->Context));
const Module &M = Obj->getModule();
DenseSet<const Comdat *> KeptComdats;
for (const auto &P : M.getComdatSymbolTable()) {
StringRef N = Saver.save(P.first());
if (ComdatGroups.insert(N).second)
KeptComdats.insert(&P.second);
}
for (const BasicSymbolRef &Sym : Obj->symbols())
if (!shouldSkip(Sym.getFlags()))
Symbols.push_back(createSymbol<ELFT>(KeptComdats, *Obj, Sym));
}
template <template <class> class T>
static std::unique_ptr<InputFile> createELFFile(MemoryBufferRef MB) {
unsigned char Size;
unsigned char Endian;
std::tie(Size, Endian) = getElfArchType(MB.getBuffer());
if (Endian != ELFDATA2LSB && Endian != ELFDATA2MSB)
fatal("invalid data encoding: " + MB.getBufferIdentifier());
std::unique_ptr<InputFile> Obj;
if (Size == ELFCLASS32 && Endian == ELFDATA2LSB)
Obj.reset(new T<ELF32LE>(MB));
else if (Size == ELFCLASS32 && Endian == ELFDATA2MSB)
Obj.reset(new T<ELF32BE>(MB));
else if (Size == ELFCLASS64 && Endian == ELFDATA2LSB)
Obj.reset(new T<ELF64LE>(MB));
else if (Size == ELFCLASS64 && Endian == ELFDATA2MSB)
Obj.reset(new T<ELF64BE>(MB));
else
fatal("invalid file class: " + MB.getBufferIdentifier());
if (!Config->FirstElf)
Config->FirstElf = Obj.get();
return Obj;
}
+template <class ELFT> std::unique_ptr<InputFile> BinaryFile::createELF() {
+ SimpleELFWriter<ELFT> ELF(ET_REL, Config->EMachine);
+ auto DataSec = ELF.addSection(".data");
+ DataSec.Header->sh_flags = SHF_ALLOC;
+ DataSec.Header->sh_size = MB.getBufferSize();
+ DataSec.Header->sh_type = SHT_PROGBITS;
+ DataSec.Header->sh_addralign = 8;
+
+ std::string Filename = sys::path::filename(MB.getBufferIdentifier()).str();
+ std::transform(Filename.begin(), Filename.end(), Filename.begin(),
+ [](char C) { return isalnum(C) ? C : '_'; });
+ std::string StartSym = "_binary_" + Filename + "_start";
+ std::string EndSym = "_binary_" + Filename + "_end";
+ std::string SizeSym = "_binary_" + Filename + "_size";
+
+ auto SSym = ELF.addSymbol(StartSym);
+ SSym.Sym->setBindingAndType(STB_GLOBAL, STT_OBJECT);
+ SSym.Sym->st_shndx = DataSec.Index;
+
+ auto ESym = ELF.addSymbol(EndSym);
+ ESym.Sym->setBindingAndType(STB_GLOBAL, STT_OBJECT);
+ ESym.Sym->st_shndx = DataSec.Index;
+ ESym.Sym->st_value = MB.getBufferSize();
+
+ auto SZSym = ELF.addSymbol(SizeSym);
+ SZSym.Sym->setBindingAndType(STB_GLOBAL, STT_OBJECT);
+ SZSym.Sym->st_shndx = SHN_ABS;
+ SZSym.Sym->st_value = MB.getBufferSize();
+
+ std::size_t Size = ELF.layout();
+ char *Buff = new char[Size]{};
+ ELFData.reset(Buff);
+
+ ELF.write(Buff);
+
+ // .data
+ std::copy(MB.getBufferStart(), MB.getBufferEnd(),
+ Buff + DataSec.Header->sh_offset);
+
+ return createELFFile<ObjectFile>(
+ MemoryBufferRef(StringRef(Buff, Size), MB.getBufferIdentifier()));
+}
+
static bool isBitcode(MemoryBufferRef MB) {
using namespace sys::fs;
return identify_magic(MB.getBuffer()) == file_magic::bitcode;
}
std::unique_ptr<InputFile> elf::createObjectFile(MemoryBufferRef MB,
StringRef ArchiveName) {
std::unique_ptr<InputFile> F;
if (isBitcode(MB))
F.reset(new BitcodeFile(MB));
else
F = createELFFile<ObjectFile>(MB);
F->ArchiveName = ArchiveName;
return F;
}
std::unique_ptr<InputFile> elf::createSharedFile(MemoryBufferRef MB) {
return createELFFile<SharedFile>(MB);
}
MemoryBufferRef LazyObjectFile::getBuffer() {
if (Seen)
return MemoryBufferRef();
Seen = true;
return MB;
}
template <class ELFT>
void LazyObjectFile::parse() {
for (StringRef Sym : getSymbols())
Symtab<ELFT>::X->addLazyObject(Sym, *this);
}
template <class ELFT> std::vector<StringRef> LazyObjectFile::getElfSymbols() {
typedef typename ELFT::Shdr Elf_Shdr;
typedef typename ELFT::Sym Elf_Sym;
typedef typename ELFT::SymRange Elf_Sym_Range;
const ELFFile<ELFT> Obj = createELFObj<ELFT>(this->MB);
for (const Elf_Shdr &Sec : Obj.sections()) {
if (Sec.sh_type != SHT_SYMTAB)
continue;
Elf_Sym_Range Syms = Obj.symbols(&Sec);
uint32_t FirstNonLocal = Sec.sh_info;
StringRef StringTable = check(Obj.getStringTableForSymtab(Sec));
std::vector<StringRef> V;
for (const Elf_Sym &Sym : Syms.slice(FirstNonLocal))
if (Sym.st_shndx != SHN_UNDEF)
V.push_back(check(Sym.getName(StringTable)));
return V;
}
return {};
}
std::vector<StringRef> LazyObjectFile::getBitcodeSymbols() {
LLVMContext Context;
std::unique_ptr<IRObjectFile> Obj =
check(IRObjectFile::create(this->MB, Context));
std::vector<StringRef> V;
for (const BasicSymbolRef &Sym : Obj->symbols()) {
uint32_t Flags = Sym.getFlags();
if (BitcodeFile::shouldSkip(Flags))
continue;
if (Flags & BasicSymbolRef::SF_Undefined)
continue;
SmallString<64> Name;
raw_svector_ostream OS(Name);
Sym.printName(OS);
V.push_back(Saver.save(StringRef(Name)));
}
return V;
}
// Returns a vector of globally-visible defined symbol names.
std::vector<StringRef> LazyObjectFile::getSymbols() {
if (isBitcode(this->MB))
return getBitcodeSymbols();
unsigned char Size;
unsigned char Endian;
std::tie(Size, Endian) = getElfArchType(this->MB.getBuffer());
if (Size == ELFCLASS32) {
if (Endian == ELFDATA2LSB)
return getElfSymbols<ELF32LE>();
return getElfSymbols<ELF32BE>();
}
if (Endian == ELFDATA2LSB)
return getElfSymbols<ELF64LE>();
return getElfSymbols<ELF64BE>();
}
template void ArchiveFile::parse<ELF32LE>();
template void ArchiveFile::parse<ELF32BE>();
template void ArchiveFile::parse<ELF64LE>();
template void ArchiveFile::parse<ELF64BE>();
template void BitcodeFile::parse<ELF32LE>(DenseSet<StringRef> &);
template void BitcodeFile::parse<ELF32BE>(DenseSet<StringRef> &);
template void BitcodeFile::parse<ELF64LE>(DenseSet<StringRef> &);
template void BitcodeFile::parse<ELF64BE>(DenseSet<StringRef> &);
template void LazyObjectFile::parse<ELF32LE>();
template void LazyObjectFile::parse<ELF32BE>();
template void LazyObjectFile::parse<ELF64LE>();
template void LazyObjectFile::parse<ELF64BE>();
template class elf::ELFFileBase<ELF32LE>;
template class elf::ELFFileBase<ELF32BE>;
template class elf::ELFFileBase<ELF64LE>;
template class elf::ELFFileBase<ELF64BE>;
template class elf::ObjectFile<ELF32LE>;
template class elf::ObjectFile<ELF32BE>;
template class elf::ObjectFile<ELF64LE>;
template class elf::ObjectFile<ELF64BE>;
template class elf::SharedFile<ELF32LE>;
template class elf::SharedFile<ELF32BE>;
template class elf::SharedFile<ELF64LE>;
template class elf::SharedFile<ELF64BE>;
+
+template std::unique_ptr<InputFile> BinaryFile::createELF<ELF32LE>();
+template std::unique_ptr<InputFile> BinaryFile::createELF<ELF32BE>();
+template std::unique_ptr<InputFile> BinaryFile::createELF<ELF64LE>();
+template std::unique_ptr<InputFile> BinaryFile::createELF<ELF64BE>();
diff --git a/ELF/InputFiles.h b/ELF/InputFiles.h
index 5978baf..b8e0911 100644
--- a/ELF/InputFiles.h
+++ b/ELF/InputFiles.h
@@ -1,307 +1,320 @@
//===- InputFiles.h ---------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_ELF_INPUT_FILES_H
#define LLD_ELF_INPUT_FILES_H
#include "Config.h"
#include "InputSection.h"
#include "Error.h"
#include "Symbols.h"
#include "lld/Core/LLVM.h"
#include "lld/Core/Reproduce.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/IR/Comdat.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ELF.h"
#include "llvm/Object/IRObjectFile.h"
#include "llvm/Support/StringSaver.h"
#include <map>
namespace lld {
namespace elf {
using llvm::object::Archive;
class InputFile;
class Lazy;
class SymbolBody;
// The root class of input files.
class InputFile {
public:
enum Kind {
ObjectKind,
SharedKind,
LazyObjectKind,
ArchiveKind,
BitcodeKind,
+ BinaryKind,
};
Kind kind() const { return FileKind; }
StringRef getName() const { return MB.getBufferIdentifier(); }
MemoryBufferRef MB;
// Filename of .a which contained this file. If this file was
// not in an archive file, it is the empty string. We use this
// string for creating error messages.
StringRef ArchiveName;
// If this is an architecture-specific file, the following members
// have ELF type (i.e. ELF{32,64}{LE,BE}) and target machine type.
ELFKind EKind = ELFNoneKind;
uint16_t EMachine = llvm::ELF::EM_NONE;
protected:
InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {}
private:
const Kind FileKind;
};
// Returns "(internal)", "foo.a(bar.o)" or "baz.o".
std::string getFilename(const InputFile *F);
template <typename ELFT> class ELFFileBase : public InputFile {
public:
typedef typename ELFT::Shdr Elf_Shdr;
typedef typename ELFT::Sym Elf_Sym;
typedef typename ELFT::Word Elf_Word;
typedef typename ELFT::SymRange Elf_Sym_Range;
ELFFileBase(Kind K, MemoryBufferRef M);
static bool classof(const InputFile *F) {
Kind K = F->kind();
return K == ObjectKind || K == SharedKind;
}
const llvm::object::ELFFile<ELFT> &getObj() const { return ELFObj; }
llvm::object::ELFFile<ELFT> &getObj() { return ELFObj; }
uint8_t getOSABI() const {
return getObj().getHeader()->e_ident[llvm::ELF::EI_OSABI];
}
StringRef getStringTable() const { return StringTable; }
uint32_t getSectionIndex(const Elf_Sym &Sym) const;
Elf_Sym_Range getElfSymbols(bool OnlyGlobals);
protected:
llvm::object::ELFFile<ELFT> ELFObj;
const Elf_Shdr *Symtab = nullptr;
ArrayRef<Elf_Word> SymtabSHNDX;
StringRef StringTable;
void initStringTable();
};
// .o file.
template <class ELFT> class ObjectFile : public ELFFileBase<ELFT> {
typedef ELFFileBase<ELFT> Base;
typedef typename ELFT::Sym Elf_Sym;
typedef typename ELFT::Shdr Elf_Shdr;
typedef typename ELFT::SymRange Elf_Sym_Range;
typedef typename ELFT::Word Elf_Word;
typedef typename ELFT::uint uintX_t;
StringRef getShtGroupSignature(const Elf_Shdr &Sec);
ArrayRef<Elf_Word> getShtGroupEntries(const Elf_Shdr &Sec);
public:
static bool classof(const InputFile *F) {
return F->kind() == Base::ObjectKind;
}
ArrayRef<SymbolBody *> getSymbols();
ArrayRef<SymbolBody *> getLocalSymbols();
ArrayRef<SymbolBody *> getNonLocalSymbols();
explicit ObjectFile(MemoryBufferRef M);
void parse(llvm::DenseSet<StringRef> &ComdatGroups);
ArrayRef<InputSectionBase<ELFT> *> getSections() const { return Sections; }
InputSectionBase<ELFT> *getSection(const Elf_Sym &Sym) const;
SymbolBody &getSymbolBody(uint32_t SymbolIndex) const {
return *SymbolBodies[SymbolIndex];
}
template <typename RelT> SymbolBody &getRelocTargetSym(const RelT &Rel) const {
uint32_t SymIndex = Rel.getSymbol(Config->Mips64EL);
return getSymbolBody(SymIndex);
}
const Elf_Shdr *getSymbolTable() const { return this->Symtab; };
// Get MIPS GP0 value defined by this file. This value represents the gp value
// used to create the relocatable object and required to support
// R_MIPS_GPREL16 / R_MIPS_GPREL32 relocations.
uint32_t getMipsGp0() const;
// The number is the offset in the string table. It will be used as the
// st_name of the symbol.
std::vector<std::pair<const DefinedRegular<ELFT> *, unsigned>> KeptLocalSyms;
// SymbolBodies and Thunks for sections in this file are allocated
// using this buffer.
llvm::BumpPtrAllocator Alloc;
private:
void initializeSections(llvm::DenseSet<StringRef> &ComdatGroups);
void initializeSymbols();
InputSectionBase<ELFT> *getRelocTarget(const Elf_Shdr &Sec);
InputSectionBase<ELFT> *createInputSection(const Elf_Shdr &Sec);
bool shouldMerge(const Elf_Shdr &Sec);
SymbolBody *createSymbolBody(const Elf_Sym *Sym);
// List of all sections defined by this file.
std::vector<InputSectionBase<ELFT> *> Sections;
// List of all symbols referenced or defined by this file.
std::vector<SymbolBody *> SymbolBodies;
// MIPS .reginfo section defined by this file.
std::unique_ptr<MipsReginfoInputSection<ELFT>> MipsReginfo;
// MIPS .MIPS.options section defined by this file.
std::unique_ptr<MipsOptionsInputSection<ELFT>> MipsOptions;
// MIPS .MIPS.abiflags section defined by this file.
std::unique_ptr<MipsAbiFlagsInputSection<ELFT>> MipsAbiFlags;
llvm::SpecificBumpPtrAllocator<InputSection<ELFT>> IAlloc;
llvm::SpecificBumpPtrAllocator<MergeInputSection<ELFT>> MAlloc;
llvm::SpecificBumpPtrAllocator<EhInputSection<ELFT>> EHAlloc;
};
// LazyObjectFile is analogous to ArchiveFile in the sense that
// the file contains lazy symbols. The difference is that
// LazyObjectFile wraps a single file instead of multiple files.
//
// This class is used for --start-lib and --end-lib options which
// instruct the linker to link object files between them with the
// archive file semantics.
class LazyObjectFile : public InputFile {
public:
explicit LazyObjectFile(MemoryBufferRef M) : InputFile(LazyObjectKind, M) {}
static bool classof(const InputFile *F) {
return F->kind() == LazyObjectKind;
}
template <class ELFT> void parse();
MemoryBufferRef getBuffer();
private:
std::vector<StringRef> getSymbols();
template <class ELFT> std::vector<StringRef> getElfSymbols();
std::vector<StringRef> getBitcodeSymbols();
llvm::BumpPtrAllocator Alloc;
llvm::StringSaver Saver{Alloc};
bool Seen = false;
};
// An ArchiveFile object represents a .a file.
class ArchiveFile : public InputFile {
public:
explicit ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {}
static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; }
template <class ELFT> void parse();
// Returns a memory buffer for a given symbol. An empty memory buffer
// is returned if we have already returned the same memory buffer.
// (So that we don't instantiate same members more than once.)
MemoryBufferRef getMember(const Archive::Symbol *Sym);
private:
std::unique_ptr<Archive> File;
llvm::DenseSet<uint64_t> Seen;
};
class BitcodeFile : public InputFile {
public:
explicit BitcodeFile(MemoryBufferRef M);
static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }
template <class ELFT>
void parse(llvm::DenseSet<StringRef> &ComdatGroups);
ArrayRef<Symbol *> getSymbols() { return Symbols; }
static bool shouldSkip(uint32_t Flags);
std::unique_ptr<llvm::object::IRObjectFile> Obj;
private:
std::vector<Symbol *> Symbols;
llvm::BumpPtrAllocator Alloc;
llvm::StringSaver Saver{Alloc};
template <class ELFT>
Symbol *createSymbol(const llvm::DenseSet<const llvm::Comdat *> &KeptComdats,
const llvm::object::IRObjectFile &Obj,
const llvm::object::BasicSymbolRef &Sym);
};
// .so file.
template <class ELFT> class SharedFile : public ELFFileBase<ELFT> {
typedef ELFFileBase<ELFT> Base;
typedef typename ELFT::Shdr Elf_Shdr;
typedef typename ELFT::Sym Elf_Sym;
typedef typename ELFT::Word Elf_Word;
typedef typename ELFT::SymRange Elf_Sym_Range;
typedef typename ELFT::Versym Elf_Versym;
typedef typename ELFT::Verdef Elf_Verdef;
std::vector<StringRef> Undefs;
StringRef SoName;
const Elf_Shdr *VersymSec = nullptr;
const Elf_Shdr *VerdefSec = nullptr;
public:
StringRef getSoName() const { return SoName; }
const Elf_Shdr *getSection(const Elf_Sym &Sym) const;
llvm::ArrayRef<StringRef> getUndefinedSymbols() { return Undefs; }
static bool classof(const InputFile *F) {
return F->kind() == Base::SharedKind;
}
explicit SharedFile(MemoryBufferRef M);
void parseSoName();
void parseRest();
std::vector<const Elf_Verdef *> parseVerdefs(const Elf_Versym *&Versym);
struct NeededVer {
// The string table offset of the version name in the output file.
size_t StrTab;
// The version identifier for this version name.
uint16_t Index;
};
// Mapping from Elf_Verdef data structures to information about Elf_Vernaux
// data structures in the output file.
std::map<const Elf_Verdef *, NeededVer> VerdefMap;
// Used for --as-needed
bool AsNeeded = false;
bool IsUsed = false;
bool isNeeded() const { return !AsNeeded || IsUsed; }
};
+class BinaryFile : public InputFile {
+public:
+ explicit BinaryFile(MemoryBufferRef M) : InputFile(BinaryKind, M) {}
+
+ static bool classof(const InputFile *F) { return F->kind() == BinaryKind; }
+
+ template <class ELFT> std::unique_ptr<InputFile> createELF();
+
+private:
+ std::unique_ptr<char[]> ELFData;
+};
+
std::unique_ptr<InputFile> createObjectFile(MemoryBufferRef MB,
StringRef ArchiveName = "");
std::unique_ptr<InputFile> createSharedFile(MemoryBufferRef MB);
} // namespace elf
} // namespace lld
#endif
diff --git a/ELF/Options.td b/ELF/Options.td
index fdc1874..85fd1f2 100644
--- a/ELF/Options.td
+++ b/ELF/Options.td
@@ -1,296 +1,300 @@
include "llvm/Option/OptParser.td"
// For options whose names are multiple letters, either one dash or
// two can precede the option name except those that start with 'o'.
class F<string name>: Flag<["--", "-"], name>;
class J<string name>: Joined<["--", "-"], name>;
class S<string name>: Separate<["--", "-"], name>;
class JS<string name>: JoinedOrSeparate<["--", "-"], name>;
def auxiliary: S<"auxiliary">, HelpText<"Set DT_AUXILIARY field to the specified name">;
def Bsymbolic: F<"Bsymbolic">, HelpText<"Bind defined symbols locally">;
def Bsymbolic_functions: F<"Bsymbolic-functions">,
HelpText<"Bind defined function symbols locally">;
def Bdynamic: F<"Bdynamic">, HelpText<"Link against shared libraries">;
def Bstatic: F<"Bstatic">, HelpText<"Do not link against shared libraries">;
def build_id: F<"build-id">, HelpText<"Generate build ID note">;
def build_id_eq: J<"build-id=">, HelpText<"Generate build ID note">;
def L: JoinedOrSeparate<["-"], "L">, MetaVarName<"<dir>">,
HelpText<"Add a directory to the library search path">;
def O: Joined<["-"], "O">, HelpText<"Optimize output file size">;
def allow_multiple_definition: F<"allow-multiple-definition">,
HelpText<"Allow multiple definitions">;
def as_needed: F<"as-needed">,
HelpText<"Only set DT_NEEDED for shared libraries if used">;
def disable_new_dtags: F<"disable-new-dtags">,
HelpText<"Disable new dynamic tags">;
def discard_all: F<"discard-all">, HelpText<"Delete all local symbols">;
def discard_locals: F<"discard-locals">,
HelpText<"Delete temporary local symbols">;
def discard_none: F<"discard-none">,
HelpText<"Keep all symbols in the symbol table">;
def dynamic_linker: S<"dynamic-linker">,
HelpText<"Which dynamic linker to use">;
def dynamic_list: S<"dynamic-list">,
HelpText<"Read a list of dynamic symbols">;
def eh_frame_hdr: F<"eh-frame-hdr">,
HelpText<"Request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header">;
def enable_new_dtags: F<"enable-new-dtags">,
HelpText<"Enable new dynamic tags">;
def end_lib: F<"end-lib">,
HelpText<"End a grouping of objects that should be treated as if they were together in an archive">;
def entry: S<"entry">, MetaVarName<"<entry>">,
HelpText<"Name of entry point symbol">;
def export_dynamic: F<"export-dynamic">,
HelpText<"Put symbols in the dynamic symbol table">;
def export_dynamic_symbol: S<"export-dynamic-symbol">,
HelpText<"Put a symbol in the dynamic symbol table">;
def fatal_warnings: F<"fatal-warnings">,
HelpText<"Treat warnings as errors">;
def fini: S<"fini">, MetaVarName<"<symbol>">,
HelpText<"Specify a finalizer function">;
def gc_sections: F<"gc-sections">,
HelpText<"Enable garbage collection of unused sections">;
+def format: J<"format=">, MetaVarName<"<input-format>">,
+ HelpText<"Change the input format of the inputs following this option">;
+
def hash_style: S<"hash-style">,
HelpText<"Specify hash style (sysv, gnu or both)">;
def help: F<"help">, HelpText<"Print option help">;
def icf: F<"icf=all">, HelpText<"Enable identical code folding">;
def image_base : J<"image-base=">, HelpText<"Set the base address">;
def init: S<"init">, MetaVarName<"<symbol>">,
HelpText<"Specify an initializer function">;
def l: JoinedOrSeparate<["-"], "l">, MetaVarName<"<libName>">,
HelpText<"Root name of library to use">;
def lto_O: J<"lto-O">, MetaVarName<"<opt-level>">,
HelpText<"Optimization level for LTO">;
def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">;
def nostdlib: F<"nostdlib">,
HelpText<"Only search directories specified on the command line">;
def no_as_needed: F<"no-as-needed">,
HelpText<"Always DT_NEEDED for shared libraries">;
def no_demangle: F<"no-demangle">,
HelpText<"Do not demangle symbol names">;
def no_gc_sections: F<"no-gc-sections">,
HelpText<"Disable garbage collection of unused sections">;
def no_gnu_unique: F<"no-gnu-unique">,
HelpText<"Disable STB_GNU_UNIQUE symbol binding">;
def no_whole_archive: F<"no-whole-archive">,
HelpText<"Restores the default behavior of loading archive members">;
def noinhibit_exec: F<"noinhibit-exec">,
HelpText<"Retain the executable output file whenever it is still usable">;
def no_undefined: F<"no-undefined">,
HelpText<"Report unresolved symbols even if the linker is creating a shared library">;
def no_undefined_version: F<"no-undefined-version">,
HelpText<"Report version scripts that refer undefined symbols">;
def o: JoinedOrSeparate<["-"], "o">, MetaVarName<"<path>">,
HelpText<"Path to file to write output">;
def oformat: Separate<["--"], "oformat">, MetaVarName<"<format>">,
HelpText<"Specify the binary format for the output object file">;
def pie: F<"pie">, HelpText<"Create a position independent executable">;
def print_gc_sections: F<"print-gc-sections">,
HelpText<"List removed unused sections">;
def reproduce: S<"reproduce">,
HelpText<"Dump linker invocation and input files for debugging">;
def rpath: S<"rpath">, HelpText<"Add a DT_RUNPATH to the output">;
def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">;
def script: S<"script">, HelpText<"Read linker script">;
def shared: F<"shared">, HelpText<"Build a shared object">;
def soname: J<"soname=">, HelpText<"Set DT_SONAME">;
def start_lib: F<"start-lib">,
HelpText<"Start a grouping of objects that should be treated as if they were together in an archive">;
def strip_all: F<"strip-all">, HelpText<"Strip all symbols">;
def strip_debug: F<"strip-debug">, HelpText<"Strip debugging information">;
def sysroot: J<"sysroot=">, HelpText<"Set the system root">;
def target1_rel: F<"target1-rel">, HelpText<"Interpret R_ARM_TARGET1 as R_ARM_REL32">;
def target1_abs: F<"target1-abs">, HelpText<"Interpret R_ARM_TARGET1 as R_ARM_ABS32">;
def threads: F<"threads">, HelpText<"Enable use of threads">;
def trace: F<"trace">, HelpText<"Print the names of the input files">;
def trace_symbol : J<"trace-symbol=">, HelpText<"Trace references to symbols">;
def undefined: J<"undefined=">,
HelpText<"Force undefined symbol during linking">;
def unresolved_symbols: J<"unresolved-symbols=">,
HelpText<"Determine how to handle unresolved symbols">;
def rsp_quoting: J<"rsp-quoting=">,
HelpText<"Quoting style for response files. Values supported: windows|posix">;
def verbose: F<"verbose">, HelpText<"Verbose mode">;
def version: F<"version">, HelpText<"Display the version number">;
def version_script: S<"version-script">,
HelpText<"Read a version script">;
def warn_common: F<"warn-common">,
HelpText<"Warn about duplicate common symbols">;
def whole_archive: F<"whole-archive">,
HelpText<"Force load of all members in a static library">;
def wrap: S<"wrap">, MetaVarName<"<symbol>">,
HelpText<"Use wrapper functions for symbol">;
def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"<option>">,
HelpText<"Linker option extensions">;
// Aliases
def alias_auxiliary: Separate<["-"], "f">, Alias<auxiliary>;
def alias_Bdynamic_call_shared: F<"call_shared">, Alias<Bdynamic>;
def alias_Bdynamic_dy: F<"dy">, Alias<Bdynamic>;
def alias_Bstatic_dn: F<"dn">, Alias<Bstatic>;
def alias_Bstatic_non_shared: F<"non_shared">, Alias<Bstatic>;
def alias_Bstatic_static: F<"static">, Alias<Bstatic>;
def alias_L__library_path: J<"library-path=">, Alias<L>;
def alias_discard_all_x: Flag<["-"], "x">, Alias<discard_all>;
def alias_discard_locals_X: Flag<["-"], "X">, Alias<discard_locals>;
def alias_dynamic_list: J<"dynamic-list=">, Alias<dynamic_list>;
def alias_entry_e: JoinedOrSeparate<["-"], "e">, Alias<entry>;
def alias_entry_entry: J<"entry=">, Alias<entry>;
def alias_export_dynamic_E: Flag<["-"], "E">, Alias<export_dynamic>;
def alias_export_dynamic_symbol: J<"export-dynamic-symbol=">,
Alias<export_dynamic_symbol>;
def alias_fini_fini: J<"fini=">, Alias<fini>;
+def alias_format_b: S<"b">, Alias<format>;
def alias_hash_style_hash_style: J<"hash-style=">, Alias<hash_style>;
def alias_init_init: J<"init=">, Alias<init>;
def alias_l__library: J<"library=">, Alias<l>;
def alias_o_output: J<"output=">, Alias<o>;
def alias_o_output2 : S<"output">, Alias<o>;
def alias_pie_pic_executable: F<"pic-executable">, Alias<pie>;
def alias_relocatable_r: Flag<["-"], "r">, Alias<relocatable>;
def alias_rpath_R: Joined<["-"], "R">, Alias<rpath>;
def alias_rpath_rpath: J<"rpath=">, Alias<rpath>;
def alias_script_T: JoinedOrSeparate<["-"], "T">, Alias<script>;
def alias_shared_Bshareable: F<"Bshareable">, Alias<shared>;
def alias_soname_h: JoinedOrSeparate<["-"], "h">, Alias<soname>;
def alias_soname_soname: S<"soname">, Alias<soname>;
def alias_strip_all: Flag<["-"], "s">, Alias<strip_all>;
def alias_strip_debug_S: Flag<["-"], "S">, Alias<strip_debug>;
def alias_trace: Flag<["-"], "t">, Alias<trace>;
def alias_trace_symbol_y : JoinedOrSeparate<["-"], "y">, Alias<trace_symbol>;
def alias_undefined_u: JoinedOrSeparate<["-"], "u">, Alias<undefined>;
def alias_version_V: Flag<["-"], "V">, Alias<version>;
def alias_version_v: Flag<["-"], "v">, Alias<version>;
def alias_wrap_wrap: J<"wrap=">, Alias<wrap>;
// Our symbol resolution algorithm handles symbols in archive files differently
// than traditional linkers, so we don't need --start-group and --end-group.
// These options are recongized for compatibility but ignored.
def end_group: F<"end-group">;
def end_group_paren: Flag<["-"], ")">;
def start_group: F<"start-group">;
def start_group_paren: Flag<["-"], "(">;
// Ignore LTO plugin-related options.
// clang -flto passes -plugin and -plugin-opt to the linker. This is required
// for ld.gold and ld.bfd to get LTO working. But it's not for lld which doesn't
// rely on a plugin. Instead of detecting which linker is used on clang side we
// just ignore the option on lld side as it's easier. In fact, the linker could
// be called 'ld' and understanding which linker is used would require parsing of
// --version output.
def plugin: S<"plugin">;
def plugin_eq: J<"plugin=">;
def plugin_opt: S<"plugin-opt">;
def plugin_opt_eq: J<"plugin-opt=">;
// Options listed below are silently ignored for now for compatibility.
def allow_shlib_undefined: F<"allow-shlib-undefined">;
def define_common: F<"define-common">;
def demangle: F<"demangle">;
def detect_odr_violations: F<"detect-odr-violations">;
def g: Flag<["-"], "g">;
def no_add_needed: F<"no-add-needed">;
def no_allow_shlib_undefined: F<"no-allow-shlib-undefined">;
def no_copy_dt_needed_entries: F<"no-copy-dt-needed-entries">,
Alias<no_add_needed>;
def no_dynamic_linker: F<"no-dynamic-linker">;
def no_fatal_warnings: F<"no-fatal-warnings">;
def no_mmap_output_file: F<"no-mmap-output-file">;
def no_warn_common: F<"no-warn-common">;
def no_warn_mismatch: F<"no-warn-mismatch">;
def rpath_link: S<"rpath-link">;
def rpath_link_eq: J<"rpath-link=">;
def sort_common: F<"sort-common">;
def warn_execstack: F<"warn-execstack">;
def warn_shared_textrel: F<"warn-shared-textrel">;
def EB : F<"EB">;
def EL : F<"EL">;
def G: JoinedOrSeparate<["-"], "G">;
// Aliases for ignored options
def alias_define_common_d: Flag<["-"], "d">, Alias<define_common>;
def alias_define_common_dc: F<"dc">, Alias<define_common>;
def alias_define_common_dp: F<"dp">, Alias<define_common>;
def alias_version_script_version_script: J<"version-script=">,
Alias<version_script>;
// LTO-related options.
def lto_jobs: J<"lto-jobs=">, HelpText<"Number of threads to run codegen">;
def lto_aa_pipeline: J<"lto-aa-pipeline=">,
HelpText<"AA pipeline to run during LTO. Used in conjunction with -lto-newpm-passes">;
def lto_newpm_passes: J<"lto-newpm-passes=">,
HelpText<"Passes to run during LTO">;
def disable_verify: F<"disable-verify">;
def mllvm: S<"mllvm">;
def save_temps: F<"save-temps">;
diff --git a/ELF/SimpleELFWriter.h b/ELF/SimpleELFWriter.h
new file mode 100644
index 0000000..b2ebb8f
--- /dev/null
+++ b/ELF/SimpleELFWriter.h
@@ -0,0 +1,150 @@
+//===- SimpleELFWriter.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_ELF_SIMPLE_ELF_WRITER_H
+#define LLD_ELF_SIMPLE_ELF_WRITER_H
+
+#include "lld/Core/LLVM.h"
+
+#include "llvm/MC/StringTableBuilder.h"
+#include "llvm/Object/ELFTypes.h"
+
+using namespace llvm;
+using namespace llvm::ELF;
+
+namespace lld {
+namespace elf {
+
+template <class ELFT> class SimpleELFWriter {
+ typedef typename ELFT::uint uintX_t;
+ typedef typename ELFT::Ehdr Elf_Ehdr;
+ typedef typename ELFT::Shdr Elf_Shdr;
+ typedef typename ELFT::Sym Elf_Sym;
+
+public:
+ SimpleELFWriter(std::uint16_t Type, std::uint16_t Machine) {
+ memcpy(Header.e_ident, "\177ELF", 4);
+ Header.e_ident[EI_CLASS] = ELFT::Is64Bits ? ELFCLASS64 : ELFCLASS32;
+ Header.e_ident[EI_DATA] = ELFT::TargetEndianness == llvm::support::little
+ ? ELFDATA2LSB
+ : ELFDATA2MSB;
+ Header.e_ident[EI_VERSION] = EV_CURRENT;
+ Header.e_ident[EI_OSABI] = 0;
+ Header.e_type = Type;
+ Header.e_machine = Machine;
+ Header.e_version = EV_CURRENT;
+ Header.e_entry = 0;
+ Header.e_phoff = 0;
+ Header.e_flags = 0;
+ Header.e_ehsize = sizeof(Elf_Ehdr);
+ Header.e_phnum = 0;
+ Header.e_shentsize = sizeof(Elf_Shdr);
+ Header.e_shstrndx = 1;
+
+ ShStrTab = addSection(".shstrtab").Header;
+ ShStrTab->sh_type = SHT_STRTAB;
+ ShStrTab->sh_addralign = 1;
+
+ StrTab = addSection(".strtab").Header;
+ StrTab->sh_type = SHT_STRTAB;
+ StrTab->sh_addralign = 1;
+
+ SymTab = addSection(".symtab").Header;
+ SymTab->sh_type = SHT_SYMTAB;
+ SymTab->sh_link = 2;
+ SymTab->sh_info = 1;
+ SymTab->sh_addralign = sizeof(uintX_t);
+ SymTab->sh_entsize = sizeof(Elf_Sym);
+ }
+
+ struct Section {
+ Elf_Shdr *Header;
+ std::size_t Index;
+ };
+
+ struct Symbol {
+ Elf_Sym *Sym;
+ std::size_t Index;
+ };
+
+ Section addSection(StringRef Name) {
+ std::size_t NameOff = SecHdrStrTabBuilder.add(Name);
+ auto Shdr = new (Alloc) Elf_Shdr{};
+ Shdr->sh_name = NameOff;
+ Sections.push_back(Shdr);
+ return {Shdr, Sections.size()};
+ }
+
+ Symbol addSymbol(StringRef Name) {
+ std::size_t NameOff = StrTabBuilder.add(Name);
+ auto Sym = new (Alloc) Elf_Sym{};
+ Sym->st_name = NameOff;
+ StaticSymbols.push_back(Sym);
+ return {Sym, StaticSymbols.size()};
+ }
+
+ std::size_t layout() {
+ SecHdrStrTabBuilder.finalizeInOrder();
+ ShStrTab->sh_size = SecHdrStrTabBuilder.getSize();
+
+ StrTabBuilder.finalizeInOrder();
+ StrTab->sh_size = StrTabBuilder.getSize();
+
+ SymTab->sh_size = (StaticSymbols.size() + 1) * sizeof(Elf_Sym);
+
+ uintX_t Offset = sizeof(Elf_Ehdr);
+ for (Elf_Shdr *Sec : Sections) {
+ Offset = alignTo(Offset, Sec->sh_addralign);
+ Sec->sh_offset = Offset;
+ Offset += Sec->sh_size;
+ }
+
+ Offset = alignTo(Offset, sizeof(uintX_t));
+ Header.e_shoff = Offset;
+ Offset += (Sections.size() + 1) * sizeof(Elf_Shdr);
+ Header.e_shnum = Sections.size() + 1;
+
+ return Offset;
+ }
+
+ void write(char *Out) {
+ std::memcpy(Out, &Header, sizeof(Elf_Ehdr));
+ std::copy(SecHdrStrTabBuilder.data().begin(),
+ SecHdrStrTabBuilder.data().end(), Out + ShStrTab->sh_offset);
+ std::copy(StrTabBuilder.data().begin(), StrTabBuilder.data().end(),
+ Out + StrTab->sh_offset);
+
+ Elf_Sym *Sym = reinterpret_cast<Elf_Sym *>(Out + SymTab->sh_offset);
+ // Skip null.
+ ++Sym;
+ for (Elf_Sym *S : StaticSymbols)
+ *Sym++ = *S;
+
+ Elf_Shdr *Shdr = reinterpret_cast<Elf_Shdr *>(Out + Header.e_shoff);
+ // Skip null.
+ ++Shdr;
+ for (Elf_Shdr *S : Sections)
+ *Shdr++ = *S;
+ }
+
+private:
+ Elf_Ehdr Header;
+ std::vector<Elf_Shdr *> Sections;
+ std::vector<Elf_Sym *> StaticSymbols;
+ llvm::StringTableBuilder SecHdrStrTabBuilder{llvm::StringTableBuilder::ELF};
+ llvm::StringTableBuilder StrTabBuilder{llvm::StringTableBuilder::ELF};
+ llvm::BumpPtrAllocator Alloc;
+ Elf_Shdr *ShStrTab;
+ Elf_Shdr *StrTab;
+ Elf_Shdr *SymTab;
+};
+}
+}
+
+#endif
diff --git a/ELF/SymbolTable.cpp b/ELF/SymbolTable.cpp
index 152881d..eff5851 100644
--- a/ELF/SymbolTable.cpp
+++ b/ELF/SymbolTable.cpp
@@ -1,703 +1,709 @@
//===- SymbolTable.cpp ----------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Symbol table is a bag of all known symbols. We put all symbols of
// all input files to the symbol table. The symbol table is basically
// a hash table with the logic to resolve symbol name conflicts using
// the symbol types.
//
//===----------------------------------------------------------------------===//
#include "SymbolTable.h"
#include "Config.h"
#include "Error.h"
#include "LinkerScript.h"
#include "Strings.h"
#include "SymbolListFile.h"
#include "Symbols.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/Support/StringSaver.h"
using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
// All input object files must be for the same architecture
// (e.g. it does not make sense to link x86 object files with
// MIPS object files.) This function checks for that error.
template <class ELFT> static bool isCompatible(InputFile *F) {
if (!isa<ELFFileBase<ELFT>>(F) && !isa<BitcodeFile>(F))
return true;
if (F->EKind == Config->EKind && F->EMachine == Config->EMachine)
return true;
StringRef A = F->getName();
StringRef B = Config->Emulation;
if (B.empty())
B = Config->FirstElf->getName();
error(A + " is incompatible with " + B);
return false;
}
// Add symbols in File to the symbol table.
template <class ELFT>
void SymbolTable<ELFT>::addFile(std::unique_ptr<InputFile> File) {
InputFile *FileP = File.get();
if (!isCompatible<ELFT>(FileP))
return;
+ // Binary file
+ if (auto *F = dyn_cast<BinaryFile>(FileP)) {
+ addFile(F->createELF<ELFT>());
+ return;
+ }
+
// .a file
if (auto *F = dyn_cast<ArchiveFile>(FileP)) {
ArchiveFiles.emplace_back(cast<ArchiveFile>(File.release()));
F->parse<ELFT>();
return;
}
// Lazy object file
if (auto *F = dyn_cast<LazyObjectFile>(FileP)) {
LazyObjectFiles.emplace_back(cast<LazyObjectFile>(File.release()));
F->parse<ELFT>();
return;
}
if (Config->Trace)
outs() << getFilename(FileP) << "\n";
// .so file
if (auto *F = dyn_cast<SharedFile<ELFT>>(FileP)) {
// DSOs are uniquified not by filename but by soname.
F->parseSoName();
if (!SoNames.insert(F->getSoName()).second)
return;
SharedFiles.emplace_back(cast<SharedFile<ELFT>>(File.release()));
F->parseRest();
return;
}
// LLVM bitcode file
if (auto *F = dyn_cast<BitcodeFile>(FileP)) {
BitcodeFiles.emplace_back(cast<BitcodeFile>(File.release()));
F->parse<ELFT>(ComdatGroups);
return;
}
// Regular object file
auto *F = cast<ObjectFile<ELFT>>(FileP);
ObjectFiles.emplace_back(cast<ObjectFile<ELFT>>(File.release()));
F->parse(ComdatGroups);
}
// This function is where all the optimizations of link-time
// optimization happens. When LTO is in use, some input files are
// not in native object file format but in the LLVM bitcode format.
// This function compiles bitcode files into a few big native files
// using LLVM functions and replaces bitcode symbols with the results.
// Because all bitcode files that consist of a program are passed
// to the compiler at once, it can do whole-program optimization.
template <class ELFT> void SymbolTable<ELFT>::addCombinedLtoObject() {
if (BitcodeFiles.empty())
return;
// Compile bitcode files.
Lto.reset(new BitcodeCompiler);
for (const std::unique_ptr<BitcodeFile> &F : BitcodeFiles)
Lto->add(*F);
std::vector<std::unique_ptr<InputFile>> IFs = Lto->compile();
// Replace bitcode symbols.
for (auto &IF : IFs) {
ObjectFile<ELFT> *Obj = cast<ObjectFile<ELFT>>(IF.release());
DenseSet<StringRef> DummyGroups;
Obj->parse(DummyGroups);
ObjectFiles.emplace_back(Obj);
}
}
template <class ELFT>
DefinedRegular<ELFT> *SymbolTable<ELFT>::addAbsolute(StringRef Name,
uint8_t Visibility) {
return cast<DefinedRegular<ELFT>>(
addRegular(Name, STB_GLOBAL, Visibility)->body());
}
// Add Name as an "ignored" symbol. An ignored symbol is a regular
// linker-synthesized defined symbol, but is only defined if needed.
template <class ELFT>
DefinedRegular<ELFT> *SymbolTable<ELFT>::addIgnored(StringRef Name,
uint8_t Visibility) {
if (!find(Name))
return nullptr;
return addAbsolute(Name, Visibility);
}
// Set a flag for --trace-symbol so that we can print out a log message
// if a new symbol with the same name is inserted into the symbol table.
template <class ELFT> void SymbolTable<ELFT>::trace(StringRef Name) {
Symtab.insert({Name, {-1, true}});
}
// Rename SYM as __wrap_SYM. The original symbol is preserved as __real_SYM.
// Used to implement --wrap.
template <class ELFT> void SymbolTable<ELFT>::wrap(StringRef Name) {
SymbolBody *B = find(Name);
if (!B)
return;
StringSaver Saver(Alloc);
Symbol *Sym = B->symbol();
Symbol *Real = addUndefined(Saver.save("__real_" + Name));
Symbol *Wrap = addUndefined(Saver.save("__wrap_" + Name));
// We rename symbols by replacing the old symbol's SymbolBody with the new
// symbol's SymbolBody. This causes all SymbolBody pointers referring to the
// old symbol to instead refer to the new symbol.
memcpy(Real->Body.buffer, Sym->Body.buffer, sizeof(Sym->Body));
memcpy(Sym->Body.buffer, Wrap->Body.buffer, sizeof(Wrap->Body));
}
static uint8_t getMinVisibility(uint8_t VA, uint8_t VB) {
if (VA == STV_DEFAULT)
return VB;
if (VB == STV_DEFAULT)
return VA;
return std::min(VA, VB);
}
// Parses a symbol in the form of <name>@<version> or <name>@@<version>.
static std::pair<StringRef, uint16_t> getSymbolVersion(StringRef S) {
if (Config->VersionDefinitions.empty())
return {S, Config->DefaultSymbolVersion};
size_t Pos = S.find('@');
if (Pos == 0 || Pos == StringRef::npos)
return {S, Config->DefaultSymbolVersion};
StringRef Name = S.substr(0, Pos);
StringRef Verstr = S.substr(Pos + 1);
if (Verstr.empty())
return {S, Config->DefaultSymbolVersion};
// '@@' in a symbol name means the default version.
// It is usually the most recent one.
bool IsDefault = (Verstr[0] == '@');
if (IsDefault)
Verstr = Verstr.substr(1);
for (VersionDefinition &V : Config->VersionDefinitions) {
if (V.Name == Verstr)
return {Name, IsDefault ? V.Id : (V.Id | VERSYM_HIDDEN)};
}
// It is an error if the specified version was not defined.
error("symbol " + S + " has undefined version " + Verstr);
return {S, Config->DefaultSymbolVersion};
}
// Find an existing symbol or create and insert a new one.
template <class ELFT>
std::pair<Symbol *, bool> SymbolTable<ELFT>::insert(StringRef &Name) {
auto P = Symtab.insert({Name, SymIndex((int)SymVector.size(), false)});
SymIndex &V = P.first->second;
bool IsNew = P.second;
if (V.Idx == -1) {
IsNew = true;
V = SymIndex((int)SymVector.size(), true);
}
Symbol *Sym;
if (IsNew) {
Sym = new (Alloc) Symbol;
Sym->Binding = STB_WEAK;
Sym->Visibility = STV_DEFAULT;
Sym->IsUsedInRegularObj = false;
Sym->HasUnnamedAddr = true;
Sym->ExportDynamic = false;
Sym->Traced = V.Traced;
std::tie(Name, Sym->VersionId) = getSymbolVersion(Name);
SymVector.push_back(Sym);
} else {
Sym = SymVector[V.Idx];
}
return {Sym, IsNew};
}
// Find an existing symbol or create and insert a new one, then apply the given
// attributes.
template <class ELFT>
std::pair<Symbol *, bool>
SymbolTable<ELFT>::insert(StringRef &Name, uint8_t Type, uint8_t Visibility,
bool CanOmitFromDynSym, bool HasUnnamedAddr,
InputFile *File) {
bool IsUsedInRegularObj = !File || File->kind() == InputFile::ObjectKind;
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
// Merge in the new unnamed_addr attribute.
S->HasUnnamedAddr &= HasUnnamedAddr;
// Merge in the new symbol's visibility.
S->Visibility = getMinVisibility(S->Visibility, Visibility);
if (!CanOmitFromDynSym && (Config->Shared || Config->ExportDynamic))
S->ExportDynamic = true;
if (IsUsedInRegularObj)
S->IsUsedInRegularObj = true;
if (!WasInserted && S->body()->Type != SymbolBody::UnknownType &&
((Type == STT_TLS) != S->body()->isTls()))
error("TLS attribute mismatch for symbol: " +
conflictMsg(S->body(), File));
return {S, WasInserted};
}
// Construct a string in the form of "Sym in File1 and File2".
// Used to construct an error message.
template <typename ELFT>
std::string SymbolTable<ELFT>::conflictMsg(SymbolBody *Existing,
InputFile *NewFile) {
std::string Sym = Existing->getName();
if (Config->Demangle)
Sym = demangle(Sym);
return Sym + " in " + getFilename(Existing->File) + " and " +
getFilename(NewFile);
}
template <class ELFT> Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name) {
return addUndefined(Name, STB_GLOBAL, STV_DEFAULT, /*Type*/ 0,
/*CanOmitFromDynSym*/ false, /*HasUnnamedAddr*/ false,
/*File*/ nullptr);
}
template <class ELFT>
Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name, uint8_t Binding,
uint8_t StOther, uint8_t Type,
bool CanOmitFromDynSym,
bool HasUnnamedAddr, InputFile *File) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) =
insert(Name, Type, StOther & 3, CanOmitFromDynSym, HasUnnamedAddr, File);
if (WasInserted) {
S->Binding = Binding;
replaceBody<Undefined>(S, Name, StOther, Type, File);
return S;
}
if (Binding != STB_WEAK) {
if (S->body()->isShared() || S->body()->isLazy())
S->Binding = Binding;
if (auto *SS = dyn_cast<SharedSymbol<ELFT>>(S->body()))
SS->file()->IsUsed = true;
}
if (auto *L = dyn_cast<Lazy>(S->body())) {
// An undefined weak will not fetch archive members, but we have to remember
// its type. See also comment in addLazyArchive.
if (S->isWeak())
L->Type = Type;
else if (auto F = L->fetch())
addFile(std::move(F));
}
return S;
}
// We have a new defined symbol with the specified binding. Return 1 if the new
// symbol should win, -1 if the new symbol should lose, or 0 if both symbols are
// strong defined symbols.
static int compareDefined(Symbol *S, bool WasInserted, uint8_t Binding) {
if (WasInserted)
return 1;
SymbolBody *Body = S->body();
if (Body->isLazy() || Body->isUndefined() || Body->isShared())
return 1;
if (Binding == STB_WEAK)
return -1;
if (S->isWeak())
return 1;
return 0;
}
// We have a new non-common defined symbol with the specified binding. Return 1
// if the new symbol should win, -1 if the new symbol should lose, or 0 if there
// is a conflict. If the new symbol wins, also update the binding.
static int compareDefinedNonCommon(Symbol *S, bool WasInserted,
uint8_t Binding) {
if (int Cmp = compareDefined(S, WasInserted, Binding)) {
if (Cmp > 0)
S->Binding = Binding;
return Cmp;
}
if (isa<DefinedCommon>(S->body())) {
// Non-common symbols take precedence over common symbols.
if (Config->WarnCommon)
warning("common " + S->body()->getName() + " is overridden");
return 1;
}
return 0;
}
template <class ELFT>
Symbol *SymbolTable<ELFT>::addCommon(StringRef N, uint64_t Size,
uint64_t Alignment, uint8_t Binding,
uint8_t StOther, uint8_t Type,
bool HasUnnamedAddr, InputFile *File) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(
N, Type, StOther & 3, /*CanOmitFromDynSym*/ false, HasUnnamedAddr, File);
int Cmp = compareDefined(S, WasInserted, Binding);
if (Cmp > 0) {
S->Binding = Binding;
replaceBody<DefinedCommon>(S, N, Size, Alignment, StOther, Type, File);
} else if (Cmp == 0) {
auto *C = dyn_cast<DefinedCommon>(S->body());
if (!C) {
// Non-common symbols take precedence over common symbols.
if (Config->WarnCommon)
warning("common " + S->body()->getName() + " is overridden");
return S;
}
if (Config->WarnCommon)
warning("multiple common of " + S->body()->getName());
Alignment = C->Alignment = std::max(C->Alignment, Alignment);
if (Size > C->Size)
replaceBody<DefinedCommon>(S, N, Size, Alignment, StOther, Type, File);
}
return S;
}
template <class ELFT>
void SymbolTable<ELFT>::reportDuplicate(SymbolBody *Existing,
InputFile *NewFile) {
std::string Msg = "duplicate symbol: " + conflictMsg(Existing, NewFile);
if (Config->AllowMultipleDefinition)
warning(Msg);
else
error(Msg);
}
template <typename ELFT>
Symbol *SymbolTable<ELFT>::addRegular(StringRef Name, const Elf_Sym &Sym,
InputSectionBase<ELFT> *Section) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) =
insert(Name, Sym.getType(), Sym.getVisibility(),
/*CanOmitFromDynSym*/ false, /*HasUnnamedAddr*/ false,
Section ? Section->getFile() : nullptr);
int Cmp = compareDefinedNonCommon(S, WasInserted, Sym.getBinding());
if (Cmp > 0)
replaceBody<DefinedRegular<ELFT>>(S, Name, Sym, Section);
else if (Cmp == 0)
reportDuplicate(S->body(), Section->getFile());
return S;
}
template <typename ELFT>
Symbol *SymbolTable<ELFT>::addRegular(StringRef Name, uint8_t Binding,
uint8_t StOther) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) =
insert(Name, STT_NOTYPE, StOther & 3, /*CanOmitFromDynSym*/ false,
/*HasUnnamedAddr*/ false, nullptr);
int Cmp = compareDefinedNonCommon(S, WasInserted, Binding);
if (Cmp > 0)
replaceBody<DefinedRegular<ELFT>>(S, Name, StOther);
else if (Cmp == 0)
reportDuplicate(S->body(), nullptr);
return S;
}
template <typename ELFT>
Symbol *SymbolTable<ELFT>::addSynthetic(StringRef N,
OutputSectionBase<ELFT> *Section,
uintX_t Value, uint8_t StOther) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(N, STT_NOTYPE, /*Visibility*/ StOther & 0x3,
/*CanOmitFromDynSym*/ false,
/*HasUnnamedAddr*/ false, nullptr);
int Cmp = compareDefinedNonCommon(S, WasInserted, STB_GLOBAL);
if (Cmp > 0)
replaceBody<DefinedSynthetic<ELFT>>(S, N, Value, Section);
else if (Cmp == 0)
reportDuplicate(S->body(), nullptr);
return S;
}
template <typename ELFT>
void SymbolTable<ELFT>::addShared(SharedFile<ELFT> *F, StringRef Name,
const Elf_Sym &Sym,
const typename ELFT::Verdef *Verdef) {
// DSO symbols do not affect visibility in the output, so we pass STV_DEFAULT
// as the visibility, which will leave the visibility in the symbol table
// unchanged.
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) =
insert(Name, Sym.getType(), STV_DEFAULT, /*CanOmitFromDynSym*/ true,
/*HasUnnamedAddr*/ false, F);
// Make sure we preempt DSO symbols with default visibility.
if (Sym.getVisibility() == STV_DEFAULT)
S->ExportDynamic = true;
if (WasInserted || isa<Undefined>(S->body())) {
replaceBody<SharedSymbol<ELFT>>(S, F, Name, Sym, Verdef);
if (!S->isWeak())
F->IsUsed = true;
}
}
template <class ELFT>
Symbol *SymbolTable<ELFT>::addBitcode(StringRef Name, uint8_t Binding,
uint8_t StOther, uint8_t Type,
bool CanOmitFromDynSym,
bool HasUnnamedAddr, BitcodeFile *F) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) =
insert(Name, Type, StOther & 3, CanOmitFromDynSym, HasUnnamedAddr, F);
int Cmp = compareDefinedNonCommon(S, WasInserted, Binding);
if (Cmp > 0)
replaceBody<DefinedRegular<ELFT>>(S, Name, StOther, Type, F);
else if (Cmp == 0)
reportDuplicate(S->body(), F);
return S;
}
template <class ELFT> SymbolBody *SymbolTable<ELFT>::find(StringRef Name) {
auto It = Symtab.find(Name);
if (It == Symtab.end())
return nullptr;
SymIndex V = It->second;
if (V.Idx == -1)
return nullptr;
return SymVector[V.Idx]->body();
}
// Returns a list of defined symbols that match with a given regex.
template <class ELFT>
std::vector<SymbolBody *> SymbolTable<ELFT>::findAll(const Regex &Re) {
std::vector<SymbolBody *> Res;
for (Symbol *Sym : SymVector) {
SymbolBody *B = Sym->body();
StringRef Name = B->getName();
if (!B->isUndefined() && const_cast<Regex &>(Re).match(Name))
Res.push_back(B);
}
return Res;
}
template <class ELFT>
void SymbolTable<ELFT>::addLazyArchive(ArchiveFile *F,
const object::Archive::Symbol Sym) {
Symbol *S;
bool WasInserted;
StringRef Name = Sym.getName();
std::tie(S, WasInserted) = insert(Name);
if (WasInserted) {
replaceBody<LazyArchive>(S, *F, Sym, SymbolBody::UnknownType);
return;
}
if (!S->body()->isUndefined())
return;
// Weak undefined symbols should not fetch members from archives. If we were
// to keep old symbol we would not know that an archive member was available
// if a strong undefined symbol shows up afterwards in the link. If a strong
// undefined symbol never shows up, this lazy symbol will get to the end of
// the link and must be treated as the weak undefined one. We already marked
// this symbol as used when we added it to the symbol table, but we also need
// to preserve its type. FIXME: Move the Type field to Symbol.
if (S->isWeak()) {
replaceBody<LazyArchive>(S, *F, Sym, S->body()->Type);
return;
}
MemoryBufferRef MBRef = F->getMember(&Sym);
if (!MBRef.getBuffer().empty())
addFile(createObjectFile(MBRef, F->getName()));
}
template <class ELFT>
void SymbolTable<ELFT>::addLazyObject(StringRef Name, LazyObjectFile &Obj) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
if (WasInserted) {
replaceBody<LazyObject>(S, Name, Obj, SymbolBody::UnknownType);
return;
}
if (!S->body()->isUndefined())
return;
// See comment for addLazyArchive above.
if (S->isWeak()) {
replaceBody<LazyObject>(S, Name, Obj, S->body()->Type);
} else {
MemoryBufferRef MBRef = Obj.getBuffer();
if (!MBRef.getBuffer().empty())
addFile(createObjectFile(MBRef));
}
}
// Process undefined (-u) flags by loading lazy symbols named by those flags.
template <class ELFT> void SymbolTable<ELFT>::scanUndefinedFlags() {
for (StringRef S : Config->Undefined)
if (auto *L = dyn_cast_or_null<Lazy>(find(S)))
if (std::unique_ptr<InputFile> File = L->fetch())
addFile(std::move(File));
}
// This function takes care of the case in which shared libraries depend on
// the user program (not the other way, which is usual). Shared libraries
// may have undefined symbols, expecting that the user program provides
// the definitions for them. An example is BSD's __progname symbol.
// We need to put such symbols to the main program's .dynsym so that
// shared libraries can find them.
// Except this, we ignore undefined symbols in DSOs.
template <class ELFT> void SymbolTable<ELFT>::scanShlibUndefined() {
for (std::unique_ptr<SharedFile<ELFT>> &File : SharedFiles)
for (StringRef U : File->getUndefinedSymbols())
if (SymbolBody *Sym = find(U))
if (Sym->isDefined())
Sym->symbol()->ExportDynamic = true;
}
// This function processes --export-dynamic-symbol and --dynamic-list.
template <class ELFT> void SymbolTable<ELFT>::scanDynamicList() {
for (StringRef S : Config->DynamicList)
if (SymbolBody *B = find(S))
B->symbol()->ExportDynamic = true;
}
static void setVersionId(SymbolBody *Body, StringRef VersionName,
StringRef Name, uint16_t Version) {
if (!Body || Body->isUndefined()) {
if (Config->NoUndefinedVersion)
error("version script assignment of " + VersionName + " to symbol " +
Name + " failed: symbol not defined");
return;
}
Symbol *Sym = Body->symbol();
if (Sym->VersionId != Config->DefaultSymbolVersion)
warning("duplicate symbol " + Name + " in version script");
Sym->VersionId = Version;
}
template <class ELFT>
std::map<std::string, SymbolBody *> SymbolTable<ELFT>::getDemangledSyms() {
std::map<std::string, SymbolBody *> Result;
for (Symbol *Sym : SymVector) {
SymbolBody *B = Sym->body();
Result[demangle(B->getName())] = B;
}
return Result;
}
static bool hasExternCpp() {
for (VersionDefinition &V : Config->VersionDefinitions)
for (SymbolVersion Sym : V.Globals)
if (Sym.IsExternCpp)
return true;
return false;
}
static SymbolBody *findDemangled(const std::map<std::string, SymbolBody *> &D,
StringRef Name) {
auto I = D.find(Name);
if (I != D.end())
return I->second;
return nullptr;
}
static std::vector<SymbolBody *>
findAllDemangled(const std::map<std::string, SymbolBody *> &D,
const Regex &Re) {
std::vector<SymbolBody *> Res;
for (auto &P : D) {
SymbolBody *Body = P.second;
if (!Body->isUndefined() && const_cast<Regex &>(Re).match(P.first))
Res.push_back(Body);
}
return Res;
}
// This function processes version scripts by updating VersionId
// member of symbols.
template <class ELFT> void SymbolTable<ELFT>::scanVersionScript() {
// If there's only one anonymous version definition in a version
// script file, the script does not actullay define any symbol version,
// but just specifies symbols visibilities. We assume that the script was
// in the form of { global: foo; bar; local *; }. So, local is default.
// Here, we make specified symbols global.
if (!Config->VersionScriptGlobals.empty()) {
for (SymbolVersion &Sym : Config->VersionScriptGlobals)
if (SymbolBody *B = find(Sym.Name))
B->symbol()->VersionId = VER_NDX_GLOBAL;
return;
}
if (Config->VersionDefinitions.empty())
return;
// Now we have version definitions, so we need to set version ids to symbols.
// Each version definition has a glob pattern, and all symbols that match
// with the pattern get that version.
// Users can use "extern C++ {}" directive to match against demangled
// C++ symbols. For example, you can write a pattern such as
// "llvm::*::foo(int, ?)". Obviously, there's no way to handle this
// other than trying to match a regexp against all demangled symbols.
// So, if "extern C++" feature is used, we demangle all known symbols.
std::map<std::string, SymbolBody *> Demangled;
if (hasExternCpp())
Demangled = getDemangledSyms();
// First, we assign versions to exact matching symbols,
// i.e. version definitions not containing any glob meta-characters.
for (VersionDefinition &V : Config->VersionDefinitions) {
for (SymbolVersion Sym : V.Globals) {
if (hasWildcard(Sym.Name))
continue;
StringRef N = Sym.Name;
SymbolBody *B = Sym.IsExternCpp ? findDemangled(Demangled, N) : find(N);
setVersionId(B, V.Name, N, V.Id);
}
}
// Next, we assign versions to fuzzy matching symbols,
// i.e. version definitions containing glob meta-characters.
// Note that because the last match takes precedence over previous matches,
// we iterate over the definitions in the reverse order.
for (size_t I = Config->VersionDefinitions.size() - 1; I != (size_t)-1; --I) {
VersionDefinition &V = Config->VersionDefinitions[I];
for (SymbolVersion &Sym : V.Globals) {
if (!hasWildcard(Sym.Name))
continue;
Regex Re = compileGlobPatterns({Sym.Name});
std::vector<SymbolBody *> Syms =
Sym.IsExternCpp ? findAllDemangled(Demangled, Re) : findAll(Re);
// Exact matching takes precendence over fuzzy matching,
// so we set a version to a symbol only if no version has been assigned
// to the symbol. This behavior is compatible with GNU.
for (SymbolBody *B : Syms)
if (B->symbol()->VersionId == Config->DefaultSymbolVersion)
B->symbol()->VersionId = V.Id;
}
}
}
template class elf::SymbolTable<ELF32LE>;
template class elf::SymbolTable<ELF32BE>;
template class elf::SymbolTable<ELF64LE>;
template class elf::SymbolTable<ELF64BE>;
diff --git a/test/ELF/format-binary.test b/test/ELF/format-binary.test
new file mode 100644
index 0000000..493893d
--- /dev/null
+++ b/test/ELF/format-binary.test
@@ -0,0 +1,52 @@
+# REQUIRES: x86
+
+# RUN: echo -n "Fluffle Puff" > %t.binary
+# RUN: ld.lld -m elf_x86_64 -r -b binary %t.binary -o %t.out
+# RUN: llvm-readobj %t.out -sections -section-data -symbols | FileCheck %s
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld %t.o -b binary %t.binary -b default %t.o -shared -o %t.out
+
+# CHECK: Name: .data
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x0
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size: 12
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment:
+# CHECK-NEXT: EntrySize: 0
+# CHECK-NEXT: SectionData (
+# CHECK-NEXT: 0000: 466C7566 666C6520 50756666 |Fluffle Puff|
+# CHECK-NEXT: )
+# CHECK-NEXT: }
+
+# CHECK: Name: _binary_format_binary_test_tmp_binary_start
+# CHECK-NEXT: Value: 0x0
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: Object
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .data
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: _binary_format_binary_test_tmp_binary_end
+# CHECK-NEXT: Value: 0xC
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: Object
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .data
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: _binary_format_binary_test_tmp_binary_size
+# CHECK-NEXT: Value: 0xC
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: Object
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: Absolute
+# CHECK-NEXT: }
More information about the llvm-commits
mailing list