diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2017-05-02 18:31:09 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2017-05-02 18:31:09 +0000 |
commit | 274c9ff5404582ff22769d9599ab10ed216ceec3 (patch) | |
tree | 8eccb2d5ca32dbec2ab6a1a3b8ac6a593f0e1b6f | |
parent | e06a19b85dfce9ea18be97247d4ca315963edc5c (diff) | |
download | src-test2-274c9ff5404582ff22769d9599ab10ed216ceec3.tar.gz src-test2-274c9ff5404582ff22769d9599ab10ed216ceec3.zip |
Notes
39 files changed, 696 insertions, 418 deletions
diff --git a/COFF/Chunks.cpp b/COFF/Chunks.cpp index 2e49f417a206..791d96ee92a5 100644 --- a/COFF/Chunks.cpp +++ b/COFF/Chunks.cpp @@ -326,41 +326,38 @@ void SEHTableChunk::writeTo(uint8_t *Buf) const { // usually loaded to that address. However, if there's already another // DLL that overlaps, the loader has to relocate it. To do that, DLLs // contain .reloc sections which contain offsets that need to be fixed -// up at runtime. If the loader find that a DLL cannot be loaded to its +// up at runtime. If the loader finds that a DLL cannot be loaded to its // desired base address, it loads it to somewhere else, and add <actual // base address> - <desired base address> to each offset that is -// specified by .reloc section. +// specified by the .reloc section. In ELF terms, .reloc sections +// contain relative relocations in REL format (as opposed to RELA.) // -// In ELF terms, .reloc sections contain arrays of relocation offsets. -// All these offsets in the section are implicitly R_*_RELATIVE, and -// addends are read from section contents (so it is REL as opposed to -// RELA). +// This already significantly reduces the size of relocations compared +// to ELF .rel.dyn, but Windows does more to reduce it (probably because +// it was invented for PCs in the late '80s or early '90s.) Offsets in +// .reloc are grouped by page where the page size is 12 bits, and +// offsets sharing the same page address are stored consecutively to +// represent them with less space. This is very similar to the page +// table which is grouped by (multiple stages of) pages. // -// This already reduce the size of relocations to 1/3 compared to ELF -// .dynrel, but Windows does more to reduce it (probably because it was -// invented for PCs in the late '80s or early '90s.) Offsets in .reloc -// are grouped by page where page size is 16 bits, and offsets sharing -// the same page address are stored consecutively to represent them with -// less space. This is a very similar to the page table which is grouped -// by (multiple stages of) pages. -// -// For example, let's say we have 0x00030, 0x00500, 0x01000, 0x01100, -// 0x20004, and 0x20008 in a .reloc section. In the section, they are -// represented like this: +// For example, let's say we have 0x00030, 0x00500, 0x00700, 0x00A00, +// 0x20004, and 0x20008 in a .reloc section for x64. The uppermost 4 +// bits have a type IMAGE_REL_BASED_DIR64 or 0xA. In the section, they +// are represented like this: // // 0x00000 -- page address (4 bytes) // 16 -- size of this block (4 bytes) -// 0x0030 -- entries (2 bytes each) -// 0x0500 -// 0x1000 -// 0x1100 +// 0xA030 -- entries (2 bytes each) +// 0xA500 +// 0xA700 +// 0xAA00 // 0x20000 -- page address (4 bytes) // 12 -- size of this block (4 bytes) -// 0x0004 -- entries (2 bytes each) -// 0x0008 +// 0xA004 -- entries (2 bytes each) +// 0xA008 // -// Usually we have a lot of relocatinos for each page, so the number of -// bytes for one .reloc entry is close to 2 bytes. +// Usually we have a lot of relocations for each page, so the number of +// bytes for one .reloc entry is close to 2 bytes on average. BaserelChunk::BaserelChunk(uint32_t Page, Baserel *Begin, Baserel *End) { // Block header consists of 4 byte page RVA and 4 byte block size. // Each entry is 2 byte. Last entry may be padding. diff --git a/COFF/Error.cpp b/COFF/Error.cpp index b2c7c89bd36c..166b1971e77f 100644 --- a/COFF/Error.cpp +++ b/COFF/Error.cpp @@ -59,6 +59,7 @@ void log(const Twine &Msg) { if (Config->Verbose) { std::lock_guard<std::mutex> Lock(Mu); outs() << Argv0 << ": " << Msg << "\n"; + outs().flush(); } } diff --git a/COFF/ICF.cpp b/COFF/ICF.cpp index 19468c0fac5e..fe59de6efa54 100644 --- a/COFF/ICF.cpp +++ b/COFF/ICF.cpp @@ -71,10 +71,18 @@ uint32_t ICF::getHash(SectionChunk *C) { } // Returns true if section S is subject of ICF. +// +// Microsoft's documentation +// (https://msdn.microsoft.com/en-us/library/bxwfs976.aspx; visited April +// 2017) says that /opt:icf folds both functions and read-only data. +// Despite that, the MSVC linker folds only functions. We found +// a few instances of programs that are not safe for data merging. +// Therefore, we merge only functions just like the MSVC tool. bool ICF::isEligible(SectionChunk *C) { bool Global = C->Sym && C->Sym->isExternal(); + bool Executable = C->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE; bool Writable = C->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_WRITE; - return C->isCOMDAT() && C->isLive() && Global && !Writable; + return C->isCOMDAT() && C->isLive() && Global && Executable && !Writable; } // Split a range into smaller ranges by recoloring sections diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp index cb56e13014db..df3b6a032cf8 100644 --- a/COFF/InputFiles.cpp +++ b/COFF/InputFiles.cpp @@ -327,6 +327,9 @@ void ImportFile::parse() { ImpSym = cast<DefinedImportData>( Symtab->addImportData(ImpName, this)->body()); + if (Hdr->getType() == llvm::COFF::IMPORT_CONST) + ConstSym = + cast<DefinedImportData>(Symtab->addImportData(Name, this)->body()); // If type is function, we need to create a thunk which jump to an // address pointed by the __imp_ symbol. (This allows you to call diff --git a/COFF/InputFiles.h b/COFF/InputFiles.h index 3078de687525..a2fd3f59ad70 100644 --- a/COFF/InputFiles.h +++ b/COFF/InputFiles.h @@ -167,6 +167,7 @@ public: static bool classof(const InputFile *F) { return F->kind() == ImportKind; } DefinedImportData *ImpSym = nullptr; + DefinedImportData *ConstSym = nullptr; DefinedImportThunk *ThunkSym = nullptr; std::string DLLName; diff --git a/COFF/MapFile.cpp b/COFF/MapFile.cpp index 43dd8dc35810..4e596e602fee 100644 --- a/COFF/MapFile.cpp +++ b/COFF/MapFile.cpp @@ -11,21 +11,21 @@ // hierarchically the output sections, input sections, input files and // symbol: // -// Address Size Align Out In File Symbol -// ================================================================= -// 00201000 00000015 4 .text -// 00201000 0000000e 4 .text -// 00201000 0000000e 4 test.o -// 0020100e 00000000 0 local -// 00201005 00000000 0 f(int) +// Address Size Align Out File Symbol +// 00201000 00000015 4 .text +// 00201000 0000000e 4 test.o:(.text) +// 0020100e 00000000 0 local +// 00201005 00000000 0 f(int) // //===----------------------------------------------------------------------===// #include "MapFile.h" #include "Error.h" +#include "SymbolTable.h" #include "Symbols.h" #include "Writer.h" +#include "lld/Core/Parallel.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; @@ -34,72 +34,58 @@ using namespace llvm::object; using namespace lld; using namespace lld::coff; -static void writeOutSecLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size, - uint64_t Align, StringRef Name) { - OS << format("%08llx %08llx %5lld ", Address, Size, Align) - << left_justify(Name, 7); -} +typedef DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>> + SymbolMapTy; -static void writeInSecLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size, - uint64_t Align, StringRef Name) { - // Pass an empty name to align the text to the correct column. - writeOutSecLine(OS, Address, Size, Align, ""); - OS << ' ' << left_justify(Name, 7); +// Print out the first three columns of a line. +static void writeHeader(raw_ostream &OS, uint64_t Addr, uint64_t Size, + uint64_t Align) { + OS << format("%08llx %08llx %5lld ", Addr, Size, Align); } -static void writeFileLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size, - uint64_t Align, StringRef Name) { - // Pass an empty name to align the text to the correct column. - writeInSecLine(OS, Address, Size, Align, ""); - OS << ' ' << left_justify(Name, 7); -} +static std::string indent(int Depth) { return std::string(Depth * 8, ' '); } -static void writeSymbolLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size, - StringRef Name) { - // Pass an empty name to align the text to the correct column. - writeFileLine(OS, Address, Size, 0, ""); - OS << ' ' << left_justify(Name, 7); +// Returns a list of all symbols that we want to print out. +static std::vector<DefinedRegular *> getSymbols() { + std::vector<DefinedRegular *> V; + for (coff::ObjectFile *File : Symtab->ObjectFiles) + for (SymbolBody *B : File->getSymbols()) + if (auto *Sym = dyn_cast<DefinedRegular>(B)) + if (Sym && !Sym->getCOFFSymbol().isSectionDefinition()) + V.push_back(Sym); + return V; } -static void writeSectionChunk(raw_fd_ostream &OS, const SectionChunk *SC, - StringRef &PrevName) { - StringRef Name = SC->getSectionName(); - if (Name != PrevName) { - writeInSecLine(OS, SC->getRVA(), SC->getSize(), SC->getAlign(), Name); - OS << '\n'; - PrevName = Name; - } - coff::ObjectFile *File = SC->File; - if (!File) - return; - writeFileLine(OS, SC->getRVA(), SC->getSize(), SC->getAlign(), - toString(File)); - OS << '\n'; - ArrayRef<SymbolBody *> Syms = File->getSymbols(); - for (SymbolBody *Sym : Syms) { - auto *DR = dyn_cast<DefinedRegular>(Sym); - if (!DR || DR->getChunk() != SC || - DR->getCOFFSymbol().isSectionDefinition()) - continue; - writeSymbolLine(OS, DR->getRVA(), SC->getSize(), toString(*Sym)); - OS << '\n'; +// Returns a map from sections to their symbols. +static SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> Syms) { + SymbolMapTy Ret; + for (DefinedRegular *S : Syms) + Ret[S->getChunk()].push_back(S); + + // Sort symbols by address. + for (auto &It : Ret) { + SmallVectorImpl<DefinedRegular *> &V = It.second; + std::sort(V.begin(), V.end(), [](DefinedRegular *A, DefinedRegular *B) { + return A->getRVA() < B->getRVA(); + }); } + return Ret; } -static void writeMapFile2(raw_fd_ostream &OS, - ArrayRef<OutputSection *> OutputSections) { - OS << "Address Size Align Out In File Symbol\n"; +// Construct a map from symbols to their stringified representations. +static DenseMap<DefinedRegular *, std::string> +getSymbolStrings(ArrayRef<DefinedRegular *> Syms) { + std::vector<std::string> Str(Syms.size()); + parallel_for((size_t)0, Syms.size(), [&](size_t I) { + raw_string_ostream OS(Str[I]); + writeHeader(OS, Syms[I]->getRVA(), 0, 0); + OS << indent(2) << toString(*Syms[I]); + }); - for (OutputSection *Sec : OutputSections) { - uint32_t VA = Sec->getRVA(); - writeOutSecLine(OS, VA, Sec->getVirtualSize(), /*Align=*/PageSize, - Sec->getName()); - OS << '\n'; - StringRef PrevName = ""; - for (Chunk *C : Sec->getChunks()) - if (const auto *SC = dyn_cast<SectionChunk>(C)) - writeSectionChunk(OS, SC, PrevName); - } + DenseMap<DefinedRegular *, std::string> Ret; + for (size_t I = 0, E = Syms.size(); I < E; ++I) + Ret[Syms[I]] = std::move(Str[I]); + return Ret; } void coff::writeMapFile(ArrayRef<OutputSection *> OutputSections) { @@ -110,5 +96,30 @@ void coff::writeMapFile(ArrayRef<OutputSection *> OutputSections) { raw_fd_ostream OS(Config->MapFile, EC, sys::fs::F_None); if (EC) fatal("cannot open " + Config->MapFile + ": " + EC.message()); - writeMapFile2(OS, OutputSections); + + // Collect symbol info that we want to print out. + std::vector<DefinedRegular *> Syms = getSymbols(); + SymbolMapTy SectionSyms = getSectionSyms(Syms); + DenseMap<DefinedRegular *, std::string> SymStr = getSymbolStrings(Syms); + + // Print out the header line. + OS << "Address Size Align Out In Symbol\n"; + + // Print out file contents. + for (OutputSection *Sec : OutputSections) { + writeHeader(OS, Sec->getRVA(), Sec->getVirtualSize(), /*Align=*/PageSize); + OS << Sec->getName() << '\n'; + + for (Chunk *C : Sec->getChunks()) { + auto *SC = dyn_cast<SectionChunk>(C); + if (!SC) + continue; + + writeHeader(OS, SC->getRVA(), SC->getSize(), SC->getAlign()); + OS << indent(1) << SC->File->getName() << ":(" << SC->getSectionName() + << ")\n"; + for (DefinedRegular *Sym : SectionSyms[SC]) + OS << SymStr[Sym] << '\n'; + } + } } diff --git a/ELF/Config.h b/ELF/Config.h index d25c63c3c0d2..1ace4aa26fdb 100644 --- a/ELF/Config.h +++ b/ELF/Config.h @@ -89,7 +89,7 @@ struct Configuration { llvm::StringRef SoName; llvm::StringRef Sysroot; llvm::StringRef ThinLTOCacheDir; - std::string RPath; + std::string Rpath; std::vector<VersionDefinition> VersionDefinitions; std::vector<llvm::StringRef> AuxiliaryList; std::vector<llvm::StringRef> SearchPaths; diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp index 68eb5616a5c6..6a71eb3ee490 100644 --- a/ELF/Driver.cpp +++ b/ELF/Driver.cpp @@ -124,7 +124,7 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) { // 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) { +static getArchiveMembers(MemoryBufferRef MB) { std::unique_ptr<Archive> File = check(Archive::create(MB), MB.getBufferIdentifier() + ": failed to parse archive"); @@ -242,6 +242,9 @@ static void checkOptions(opt::InputArgList &Args) { if (Config->Pie && Config->Shared) error("-shared and -pie may not be used together"); + if (!Config->Shared && !Config->AuxiliaryList.empty()) + error("-f may not be used without -shared"); + if (Config->Relocatable) { if (Config->Shared) error("-r and -shared may not be used together"); @@ -396,7 +399,7 @@ static std::vector<StringRef> getArgs(opt::InputArgList &Args, int Id) { return V; } -static std::string getRPath(opt::InputArgList &Args) { +static std::string getRpath(opt::InputArgList &Args) { std::vector<StringRef> V = getArgs(Args, OPT_rpath); return llvm::join(V.begin(), V.end(), ":"); } @@ -444,16 +447,14 @@ static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &Args) { } static Target2Policy getTarget2(opt::InputArgList &Args) { - if (auto *Arg = Args.getLastArg(OPT_target2)) { - StringRef S = Arg->getValue(); - if (S == "rel") - return Target2Policy::Rel; - if (S == "abs") - return Target2Policy::Abs; - if (S == "got-rel") - return Target2Policy::GotRel; - error("unknown --target2 option: " + S); - } + StringRef S = getString(Args, OPT_target2, "got-rel"); + if (S == "rel") + return Target2Policy::Rel; + if (S == "abs") + return Target2Policy::Abs; + if (S == "got-rel") + return Target2Policy::GotRel; + error("unknown --target2 option: " + S); return Target2Policy::GotRel; } @@ -550,6 +551,29 @@ static std::pair<bool, bool> getHashStyle(opt::InputArgList &Args) { return {true, true}; } +// Parse --build-id or --build-id=<style>. We handle "tree" as a +// synonym for "sha1" because all our hash functions including +// -build-id=sha1 are actually tree hashes for performance reasons. +static std::pair<BuildIdKind, std::vector<uint8_t>> +getBuildId(opt::InputArgList &Args) { + if (Args.hasArg(OPT_build_id)) + return {BuildIdKind::Fast, {}}; + + StringRef S = getString(Args, OPT_build_id_eq, "none"); + if (S == "md5") + return {BuildIdKind::Md5, {}}; + if (S == "sha1" || S == "tree") + return {BuildIdKind::Sha1, {}}; + if (S == "uuid") + return {BuildIdKind::Uuid, {}}; + if (S.startswith("0x")) + return {BuildIdKind::Hexstring, parseHex(S.substr(2))}; + + if (S != "none") + error("unknown --build-id style: " + S); + return {BuildIdKind::None, {}}; +} + static std::vector<StringRef> getLines(MemoryBufferRef MB) { SmallVector<StringRef, 0> Arr; MB.getBuffer().split(Arr, '\n'); @@ -564,14 +588,14 @@ static std::vector<StringRef> getLines(MemoryBufferRef MB) { } static bool getCompressDebugSections(opt::InputArgList &Args) { - if (auto *Arg = Args.getLastArg(OPT_compress_debug_sections)) { - StringRef S = Arg->getValue(); - if (S == "zlib") - return zlib::isAvailable(); - if (S != "none") - error("unknown --compress-debug-sections value: " + S); - } - return false; + StringRef S = getString(Args, OPT_compress_debug_sections, "none"); + if (S == "none") + return false; + if (S != "zlib") + error("unknown --compress-debug-sections value: " + S); + if (!zlib::isAvailable()) + error("--compress-debug-sections: zlib is not available"); + return true; } // Initializes Config members by the command line options. @@ -616,7 +640,7 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) { Config->OutputFile = getString(Args, OPT_o); Config->Pie = getArg(Args, OPT_pie, OPT_nopie, false); Config->PrintGcSections = Args.hasArg(OPT_print_gc_sections); - Config->RPath = getRPath(Args); + Config->Rpath = getRpath(Args); Config->Relocatable = Args.hasArg(OPT_relocatable); Config->SaveTemps = Args.hasArg(OPT_save_temps); Config->SearchPaths = getArgs(Args, OPT_L); @@ -679,32 +703,7 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) { Config->ZRelro = false; std::tie(Config->SysvHash, Config->GnuHash) = getHashStyle(Args); - - // Parse --build-id or --build-id=<style>. We handle "tree" as a - // synonym for "sha1" because all of our hash functions including - // -build-id=sha1 are tree hashes for performance reasons. - if (Args.hasArg(OPT_build_id)) - Config->BuildId = BuildIdKind::Fast; - if (auto *Arg = Args.getLastArg(OPT_build_id_eq)) { - StringRef S = Arg->getValue(); - if (S == "md5") { - Config->BuildId = BuildIdKind::Md5; - } else if (S == "sha1" || S == "tree") { - 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); - } - } - - if (!Config->Shared && !Config->AuxiliaryList.empty()) - error("-f may not be used without -shared"); + std::tie(Config->BuildId, Config->BuildIdVector) = getBuildId(Args); if (auto *Arg = Args.getLastArg(OPT_symbol_ordering_file)) if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue())) diff --git a/ELF/Driver.h b/ELF/Driver.h index 6a75a8942ca0..58bd2ab52195 100644 --- a/ELF/Driver.h +++ b/ELF/Driver.h @@ -31,7 +31,6 @@ public: void addLibrary(StringRef Name); private: - std::vector<MemoryBufferRef> getArchiveMembers(MemoryBufferRef MB); void readConfigs(llvm::opt::InputArgList &Args); void createFiles(llvm::opt::InputArgList &Args); void inferMachineType(); diff --git a/ELF/Error.cpp b/ELF/Error.cpp index 2c61b58dfed5..7a58668bdcc0 100644 --- a/ELF/Error.cpp +++ b/ELF/Error.cpp @@ -60,6 +60,7 @@ void elf::log(const Twine &Msg) { if (Config->Verbose) { std::lock_guard<std::mutex> Lock(Mu); outs() << Argv0 << ": " << Msg << "\n"; + outs().flush(); } } diff --git a/ELF/InputFiles.cpp b/ELF/InputFiles.cpp index d99f71eb4aae..12867bbd071c 100644 --- a/ELF/InputFiles.cpp +++ b/ELF/InputFiles.cpp @@ -123,10 +123,10 @@ std::string elf::ObjectFile<ELFT>::getLineInfo(InputSectionBase *S, return ""; } -// Returns "(internal)", "foo.a(bar.o)" or "baz.o". +// Returns "<internal>", "foo.a(bar.o)" or "baz.o". std::string lld::toString(const InputFile *F) { if (!F) - return "(internal)"; + return "<internal>"; if (F->ToStringCache.empty()) { if (F->ArchiveName.empty()) @@ -137,15 +137,13 @@ std::string lld::toString(const InputFile *F) { return F->ToStringCache; } -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) { - EKind = getELFKind<ELFT>(); + if (ELFT::TargetEndianness == support::little) + EKind = ELFT::Is64Bits ? ELF64LEKind : ELF32LEKind; + else + EKind = ELFT::Is64Bits ? ELF64BEKind : ELF32BEKind; + EMachine = getObj().getHeader()->e_machine; OSABI = getObj().getHeader()->e_ident[llvm::ELF::EI_OSABI]; } @@ -174,8 +172,10 @@ void ELFFileBase<ELFT>::initSymtab(ArrayRef<Elf_Shdr> Sections, } template <class ELFT> -elf::ObjectFile<ELFT>::ObjectFile(MemoryBufferRef M) - : ELFFileBase<ELFT>(Base::ObjectKind, M) {} +elf::ObjectFile<ELFT>::ObjectFile(MemoryBufferRef M, StringRef ArchiveName) + : ELFFileBase<ELFT>(Base::ObjectKind, M) { + this->ArchiveName = ArchiveName; +} template <class ELFT> ArrayRef<SymbolBody *> elf::ObjectFile<ELFT>::getLocalSymbols() { @@ -361,6 +361,15 @@ InputSectionBase *elf::ObjectFile<ELFT>::getRelocTarget(const Elf_Shdr &Sec) { return Target; } +// Create a regular InputSection class that has the same contents +// as a given section. +InputSectionBase *toRegularSection(MergeInputSection *Sec) { + auto *Ret = make<InputSection>(Sec->Flags, Sec->Type, Sec->Alignment, + Sec->Data, Sec->Name); + Ret->File = Sec->File; + return Ret; +} + template <class ELFT> InputSectionBase * elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec, @@ -398,9 +407,18 @@ elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec, if (Target->FirstRelocation) fatal(toString(this) + ": multiple relocation sections to one section are not supported"); - if (isa<MergeInputSection>(Target)) - fatal(toString(this) + - ": relocations pointing to SHF_MERGE are not supported"); + + // Mergeable sections with relocations are tricky because relocations + // need to be taken into account when comparing section contents for + // merging. It doesn't worth supporting such mergeable sections because + // they are rare and it'd complicates the internal design (we usually + // have to determine if two sections are mergeable early in the link + // process much before applying relocations). We simply handle mergeable + // sections with relocations as non-mergeable. + if (auto *MS = dyn_cast<MergeInputSection>(Target)) { + Target = toRegularSection(MS); + this->Sections[Sec.sh_info] = Target; + } size_t NumRelocations; if (Sec.sh_type == SHT_RELA) { @@ -461,6 +479,15 @@ elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec, if (Config->Strip != StripPolicy::None && Name.startswith(".debug")) return &InputSection::Discarded; + // If -gdb-index is given, LLD creates .gdb_index section, and that + // section serves the same purpose as .debug_gnu_pub{names,types} sections. + // If that's the case, we want to eliminate .debug_gnu_pub{names,types} + // because they are redundant and can waste large amount of disk space + // (for example, they are about 400 MiB in total for a clang debug build.) + if (Config->GdbIndex && + (Name == ".debug_gnu_pubnames" || Name == ".debug_gnu_pubtypes")) + return &InputSection::Discarded; + // The linkonce feature is a sort of proto-comdat. Some glibc i386 object // files contain definitions of symbol "__x86.get_pc_thunk.bx" in linkonce // sections. Drop those sections to avoid duplicate symbol errors. @@ -665,7 +692,7 @@ template <class ELFT> void SharedFile<ELFT>::parseSoName() { uint64_t Val = Dyn.getVal(); if (Val >= this->StringTable.size()) fatal(toString(this) + ": invalid DT_SONAME entry"); - SoName = StringRef(this->StringTable.data() + Val); + SoName = this->StringTable.data() + Val; return; } } @@ -748,7 +775,7 @@ template <class ELFT> void SharedFile<ELFT>::parseRest() { // with explicit versions. if (V) { StringRef VerName = this->StringTable.data() + V->getAux()->vda_name; - Name = Saver.save(Twine(Name) + "@" + VerName); + Name = Saver.save(Name + "@" + VerName); elf::Symtab<ELFT>::X->addShared(this, Name, Sym, V); } } @@ -862,76 +889,50 @@ void BitcodeFile::parse(DenseSet<CachedHashStringRef> &ComdatGroups) { Symbols.push_back(createBitcodeSymbol<ELFT>(KeptComdats, ObjSym, this)); } -// Small bit of template meta programming to handle the SharedFile constructor -// being the only one with a DefaultSoName parameter. -template <template <class> class T, class E> -typename std::enable_if<std::is_same<T<E>, SharedFile<E>>::value, - InputFile *>::type -createELFAux(MemoryBufferRef MB, StringRef DefaultSoName) { - return make<T<E>>(MB, DefaultSoName); -} -template <template <class> class T, class E> -typename std::enable_if<!std::is_same<T<E>, SharedFile<E>>::value, - InputFile *>::type -createELFAux(MemoryBufferRef MB, StringRef DefaultSoName) { - return make<T<E>>(MB); -} - -template <template <class> class T> -static InputFile *createELFFile(MemoryBufferRef MB, StringRef DefaultSoName) { +static ELFKind getELFKind(MemoryBufferRef MB) { unsigned char Size; unsigned char Endian; std::tie(Size, Endian) = getElfArchType(MB.getBuffer()); + if (Endian != ELFDATA2LSB && Endian != ELFDATA2MSB) fatal(MB.getBufferIdentifier() + ": invalid data encoding"); + if (Size != ELFCLASS32 && Size != ELFCLASS64) + fatal(MB.getBufferIdentifier() + ": invalid file class"); size_t BufSize = MB.getBuffer().size(); if ((Size == ELFCLASS32 && BufSize < sizeof(Elf32_Ehdr)) || (Size == ELFCLASS64 && BufSize < sizeof(Elf64_Ehdr))) fatal(MB.getBufferIdentifier() + ": file is too short"); - InputFile *Obj; - if (Size == ELFCLASS32 && Endian == ELFDATA2LSB) - Obj = createELFAux<T, ELF32LE>(MB, DefaultSoName); - else if (Size == ELFCLASS32 && Endian == ELFDATA2MSB) - Obj = createELFAux<T, ELF32BE>(MB, DefaultSoName); - else if (Size == ELFCLASS64 && Endian == ELFDATA2LSB) - Obj = createELFAux<T, ELF64LE>(MB, DefaultSoName); - else if (Size == ELFCLASS64 && Endian == ELFDATA2MSB) - Obj = createELFAux<T, ELF64BE>(MB, DefaultSoName); - else - fatal(MB.getBufferIdentifier() + ": invalid file class"); - - if (!Config->FirstElf) - Config->FirstElf = Obj; - return Obj; + if (Size == ELFCLASS32) + return (Endian == ELFDATA2LSB) ? ELF32LEKind : ELF32BEKind; + return (Endian == ELFDATA2LSB) ? ELF64LEKind : ELF64BEKind; } template <class ELFT> void BinaryFile::parse() { - StringRef Buf = MB.getBuffer(); - ArrayRef<uint8_t> Data = - makeArrayRef<uint8_t>((const uint8_t *)Buf.data(), Buf.size()); - - std::string Filename = MB.getBufferIdentifier(); - std::transform(Filename.begin(), Filename.end(), Filename.begin(), - [](char C) { return isalnum(C) ? C : '_'; }); - Filename = "_binary_" + Filename; - StringRef StartName = Saver.save(Twine(Filename) + "_start"); - StringRef EndName = Saver.save(Twine(Filename) + "_end"); - StringRef SizeName = Saver.save(Twine(Filename) + "_size"); - + ArrayRef<uint8_t> Data = toArrayRef(MB.getBuffer()); auto *Section = make<InputSection>(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, 8, Data, ".data"); Sections.push_back(Section); - elf::Symtab<ELFT>::X->addRegular(StartName, STV_DEFAULT, STT_OBJECT, 0, 0, - STB_GLOBAL, Section, nullptr); - elf::Symtab<ELFT>::X->addRegular(EndName, STV_DEFAULT, STT_OBJECT, - Data.size(), 0, STB_GLOBAL, Section, - nullptr); - elf::Symtab<ELFT>::X->addRegular(SizeName, STV_DEFAULT, STT_OBJECT, - Data.size(), 0, STB_GLOBAL, nullptr, + // For each input file foo that is embedded to a result as a binary + // blob, we define _binary_foo_{start,end,size} symbols, so that + // user programs can access blobs by name. Non-alphanumeric + // characters in a filename are replaced with underscore. + std::string S = "_binary_" + MB.getBufferIdentifier().str(); + for (size_t I = 0; I < S.size(); ++I) + if (!isalnum(S[I])) + S[I] = '_'; + + elf::Symtab<ELFT>::X->addRegular(Saver.save(S + "_start"), STV_DEFAULT, + STT_OBJECT, 0, 0, STB_GLOBAL, Section, nullptr); + elf::Symtab<ELFT>::X->addRegular(Saver.save(S + "_end"), STV_DEFAULT, + STT_OBJECT, Data.size(), 0, STB_GLOBAL, + Section, nullptr); + elf::Symtab<ELFT>::X->addRegular(Saver.save(S + "_size"), STV_DEFAULT, + STT_OBJECT, Data.size(), 0, STB_GLOBAL, + nullptr, nullptr); } static bool isBitcode(MemoryBufferRef MB) { @@ -941,15 +942,36 @@ static bool isBitcode(MemoryBufferRef MB) { InputFile *elf::createObjectFile(MemoryBufferRef MB, StringRef ArchiveName, uint64_t OffsetInArchive) { - InputFile *F = isBitcode(MB) - ? make<BitcodeFile>(MB, ArchiveName, OffsetInArchive) - : createELFFile<ObjectFile>(MB, ""); - F->ArchiveName = ArchiveName; - return F; + if (isBitcode(MB)) + return make<BitcodeFile>(MB, ArchiveName, OffsetInArchive); + + switch (getELFKind(MB)) { + case ELF32LEKind: + return make<ObjectFile<ELF32LE>>(MB, ArchiveName); + case ELF32BEKind: + return make<ObjectFile<ELF32BE>>(MB, ArchiveName); + case ELF64LEKind: + return make<ObjectFile<ELF64LE>>(MB, ArchiveName); + case ELF64BEKind: + return make<ObjectFile<ELF64BE>>(MB, ArchiveName); + default: + llvm_unreachable("getELFKind"); + } } InputFile *elf::createSharedFile(MemoryBufferRef MB, StringRef DefaultSoName) { - return createELFFile<SharedFile>(MB, DefaultSoName); + switch (getELFKind(MB)) { + case ELF32LEKind: + return make<SharedFile<ELF32LE>>(MB, DefaultSoName); + case ELF32BEKind: + return make<SharedFile<ELF32BE>>(MB, DefaultSoName); + case ELF64LEKind: + return make<SharedFile<ELF64LE>>(MB, DefaultSoName); + case ELF64BEKind: + return make<SharedFile<ELF64BE>>(MB, DefaultSoName); + default: + llvm_unreachable("getELFKind"); + } } MemoryBufferRef LazyObjectFile::getBuffer() { @@ -1004,17 +1026,18 @@ 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>(); + switch (getELFKind(this->MB)) { + case ELF32LEKind: + return getElfSymbols<ELF32LE>(); + case ELF32BEKind: return getElfSymbols<ELF32BE>(); - } - if (Endian == ELFDATA2LSB) + case ELF64LEKind: return getElfSymbols<ELF64LE>(); - return getElfSymbols<ELF64BE>(); + case ELF64BEKind: + return getElfSymbols<ELF64BE>(); + default: + llvm_unreachable("getELFKind"); + } } template void ArchiveFile::parse<ELF32LE>(); diff --git a/ELF/InputFiles.h b/ELF/InputFiles.h index 4552270316d9..d0a45a4a98cf 100644 --- a/ELF/InputFiles.h +++ b/ELF/InputFiles.h @@ -156,7 +156,7 @@ public: ArrayRef<SymbolBody *> getSymbols(); ArrayRef<SymbolBody *> getLocalSymbols(); - explicit ObjectFile(MemoryBufferRef M); + ObjectFile(MemoryBufferRef M, StringRef ArchiveName); void parse(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups); InputSectionBase *getSection(const Elf_Sym &Sym) const; diff --git a/ELF/InputSection.cpp b/ELF/InputSection.cpp index aff57551a8b3..c082f128a9bc 100644 --- a/ELF/InputSection.cpp +++ b/ELF/InputSection.cpp @@ -39,9 +39,7 @@ std::vector<InputSectionBase *> elf::InputSections; // Returns a string to construct an error message. std::string lld::toString(const InputSectionBase *Sec) { - // File can be absent if section is synthetic. - std::string FileName = Sec->File ? Sec->File->getName() : "<internal>"; - return (FileName + ":(" + Sec->Name + ")").str(); + return (toString(Sec->File) + ":(" + Sec->Name + ")").str(); } template <class ELFT> diff --git a/ELF/LTO.cpp b/ELF/LTO.cpp index dd435173101a..de0d45bea1c4 100644 --- a/ELF/LTO.cpp +++ b/ELF/LTO.cpp @@ -105,6 +105,11 @@ BitcodeCompiler::~BitcodeCompiler() = default; static void undefine(Symbol *S) { replaceBody<Undefined>(S, S->body()->getName(), /*IsLocal=*/false, STV_DEFAULT, S->body()->Type, nullptr); + // It shouldn't normally matter what the binding is, but if a bug in the LTO + // implementation causes it to fail to provide a definition for a symbol, + // setting the binding to STB_GLOBAL will cause the linker to report an + // undefined symbol error, even if the definition was weak. + S->Binding = STB_GLOBAL; } void BitcodeCompiler::add(BitcodeFile &F) { diff --git a/ELF/LinkerScript.cpp b/ELF/LinkerScript.cpp index 63eb90456e17..22a5b639469b 100644 --- a/ELF/LinkerScript.cpp +++ b/ELF/LinkerScript.cpp @@ -406,8 +406,15 @@ void LinkerScript::processCommands(OutputSectionFactory &Factory) { } // Add input sections to an output section. - for (InputSectionBase *S : V) - Factory.addInputSec(S, Cmd->Name); + unsigned Pos = 0; + for (InputSectionBase *S : V) { + // The actual offset will be computed during + // assignAddresses. For now, use the index as a very crude + // approximation so that it is at least easy for other code to + // know the section order. + cast<InputSection>(S)->OutSecOff = Pos++; + Factory.addInputSec(S, Cmd->Name, Cmd->Sec); + } } } CurOutSec = nullptr; @@ -465,9 +472,26 @@ void LinkerScript::fabricateDefaultCommands(bool AllocateHeader) { // Add sections that didn't match any sections command. void LinkerScript::addOrphanSections(OutputSectionFactory &Factory) { - for (InputSectionBase *S : InputSections) - if (S->Live && !S->OutSec) - Factory.addInputSec(S, getOutputSectionName(S->Name)); + for (InputSectionBase *S : InputSections) { + if (!S->Live || S->OutSec) + continue; + StringRef Name = getOutputSectionName(S->Name); + auto I = std::find_if( + Opt.Commands.begin(), Opt.Commands.end(), [&](BaseCommand *Base) { + if (auto *Cmd = dyn_cast<OutputSectionCommand>(Base)) + return Cmd->Name == Name; + return false; + }); + if (I == Opt.Commands.end()) { + Factory.addInputSec(S, Name); + } else { + auto *Cmd = cast<OutputSectionCommand>(*I); + Factory.addInputSec(S, Name, Cmd->Sec); + auto *ISD = make<InputSectionDescription>(""); + ISD->Sections.push_back(S); + Cmd->Commands.push_back(ISD); + } + } } static bool isTbss(OutputSection *Sec) { @@ -475,8 +499,6 @@ static bool isTbss(OutputSection *Sec) { } void LinkerScript::output(InputSection *S) { - if (!AlreadyOutputIS.insert(S).second) - return; bool IsTbss = isTbss(CurOutSec); uint64_t Pos = IsTbss ? Dot + ThreadBssOffset : Dot; @@ -508,19 +530,9 @@ void LinkerScript::output(InputSection *S) { Dot = Pos; } -void LinkerScript::flush() { - assert(CurOutSec); - if (!AlreadyOutputOS.insert(CurOutSec).second) - return; - for (InputSection *I : CurOutSec->Sections) - output(I); -} - void LinkerScript::switchTo(OutputSection *Sec) { if (CurOutSec == Sec) return; - if (AlreadyOutputOS.count(Sec)) - return; CurOutSec = Sec; @@ -571,19 +583,11 @@ void LinkerScript::process(BaseCommand &Base) { if (!Sec->Live) continue; - assert(CurOutSec == Sec->OutSec || AlreadyOutputOS.count(Sec->OutSec)); + assert(CurOutSec == Sec->OutSec); output(cast<InputSection>(Sec)); } } -static OutputSection * -findSection(StringRef Name, const std::vector<OutputSection *> &Sections) { - for (OutputSection *Sec : Sections) - if (Sec->Name == Name) - return Sec; - return nullptr; -} - // This function searches for a memory region to place the given output // section in. If found, a pointer to the appropriate memory region is // returned. Otherwise, a nullptr is returned. @@ -638,19 +642,8 @@ void LinkerScript::assignOffsets(OutputSectionCommand *Cmd) { Dot = CurMemRegion->Offset; switchTo(Sec); - // flush() may add orphan sections, so the order of flush() and - // symbol assignments is important. We want to call flush() first so - // that symbols pointing the end of the current section points to - // the location after orphan sections. - auto Mid = - std::find_if(Cmd->Commands.rbegin(), Cmd->Commands.rend(), - [](BaseCommand *Cmd) { return !isa<SymbolAssignment>(Cmd); }) - .base(); - for (auto I = Cmd->Commands.begin(); I != Mid; ++I) - process(**I); - flush(); - for (auto I = Mid, E = Cmd->Commands.end(); I != E; ++I) - process(**I); + for (BaseCommand *C : Cmd->Commands) + process(*C); } void LinkerScript::removeEmptyCommands() { @@ -663,7 +656,8 @@ void LinkerScript::removeEmptyCommands() { auto Pos = std::remove_if( Opt.Commands.begin(), Opt.Commands.end(), [&](BaseCommand *Base) { if (auto *Cmd = dyn_cast<OutputSectionCommand>(Base)) - return !Cmd->Sec; + return std::find(OutputSections->begin(), OutputSections->end(), + Cmd->Sec) == OutputSections->end(); return false; }); Opt.Commands.erase(Pos, Opt.Commands.end()); @@ -687,8 +681,7 @@ void LinkerScript::adjustSectionsBeforeSorting() { auto *Cmd = dyn_cast<OutputSectionCommand>(Base); if (!Cmd) continue; - if (OutputSection *Sec = findSection(Cmd->Name, *OutputSections)) { - Cmd->Sec = Sec; + if (OutputSection *Sec = Cmd->Sec) { Flags = Sec->Flags; Type = Sec->Type; continue; @@ -820,15 +813,24 @@ void LinkerScript::placeOrphanSections() { ++CmdIndex; } + // If there is no command corresponding to this output section, + // create one and put a InputSectionDescription in it so that both + // representations agree on which input sections to use. auto Pos = std::find_if(CmdIter, E, [&](BaseCommand *Base) { auto *Cmd = dyn_cast<OutputSectionCommand>(Base); return Cmd && Cmd->Name == Name; }); if (Pos == E) { auto *Cmd = make<OutputSectionCommand>(Name); - Cmd->Sec = Sec; Opt.Commands.insert(CmdIter, Cmd); ++CmdIndex; + + Cmd->Sec = Sec; + auto *ISD = make<InputSectionDescription>(""); + for (InputSection *IS : Sec->Sections) + ISD->Sections.push_back(IS); + Cmd->Commands.push_back(ISD); + continue; } @@ -846,6 +848,51 @@ void LinkerScript::processNonSectionCommands() { } } +// Do a last effort at synchronizing the linker script "AST" and the section +// list. This is needed to account for last minute changes, like adding a +// .ARM.exidx terminator and sorting SHF_LINK_ORDER sections. +// +// FIXME: We should instead create the "AST" earlier and the above changes would +// be done directly in the "AST". +// +// This can only handle new sections being added and sections being reordered. +void LinkerScript::synchronize() { + for (BaseCommand *Base : Opt.Commands) { + auto *Cmd = dyn_cast<OutputSectionCommand>(Base); + if (!Cmd) + continue; + ArrayRef<InputSection *> Sections = Cmd->Sec->Sections; + std::vector<InputSectionBase **> ScriptSections; + DenseSet<InputSectionBase *> ScriptSectionsSet; + for (BaseCommand *Base : Cmd->Commands) { + auto *ISD = dyn_cast<InputSectionDescription>(Base); + if (!ISD) + continue; + for (InputSectionBase *&IS : ISD->Sections) { + if (IS->Live) { + ScriptSections.push_back(&IS); + ScriptSectionsSet.insert(IS); + } + } + } + std::vector<InputSectionBase *> Missing; + for (InputSection *IS : Sections) + if (!ScriptSectionsSet.count(IS)) + Missing.push_back(IS); + if (!Missing.empty()) { + auto ISD = make<InputSectionDescription>(""); + ISD->Sections = Missing; + Cmd->Commands.push_back(ISD); + for (InputSectionBase *&IS : ISD->Sections) + if (IS->Live) + ScriptSections.push_back(&IS); + } + assert(ScriptSections.size() == Sections.size()); + for (int I = 0, N = Sections.size(); I < N; ++I) + *ScriptSections[I] = Sections[I]; + } +} + void LinkerScript::assignAddresses(std::vector<PhdrEntry> &Phdrs) { // Assign addresses as instructed by linker script SECTIONS sub-commands. Dot = 0; diff --git a/ELF/LinkerScript.h b/ELF/LinkerScript.h index 61942b2db357..dd96d335a660 100644 --- a/ELF/LinkerScript.h +++ b/ELF/LinkerScript.h @@ -228,7 +228,6 @@ protected: MemoryRegion *findMemoryRegion(OutputSectionCommand *Cmd); void switchTo(OutputSection *Sec); - void flush(); void output(InputSection *Sec); void process(BaseCommand &Base); @@ -242,9 +241,6 @@ protected: OutputSection *CurOutSec = nullptr; MemoryRegion *CurMemRegion = nullptr; - llvm::DenseSet<OutputSection *> AlreadyOutputOS; - llvm::DenseSet<InputSectionBase *> AlreadyOutputIS; - public: bool hasPhdrsCommands() { return !Opt.PhdrsCommands.empty(); } uint64_t getDot() { return Dot; } @@ -271,6 +267,7 @@ public: void assignOffsets(OutputSectionCommand *Cmd); void placeOrphanSections(); void processNonSectionCommands(); + void synchronize(); void assignAddresses(std::vector<PhdrEntry> &Phdrs); int getSectionIndex(StringRef Name); diff --git a/ELF/MapFile.cpp b/ELF/MapFile.cpp index 31c8091bb6a1..af5bc3c2c813 100644 --- a/ELF/MapFile.cpp +++ b/ELF/MapFile.cpp @@ -11,19 +11,19 @@ // hierarchically the output sections, input sections, input files and // symbol: // -// Address Size Align Out In File Symbol -// ================================================================= -// 00201000 00000015 4 .text -// 00201000 0000000e 4 .text -// 00201000 0000000e 4 test.o -// 0020100e 00000000 0 local -// 00201005 00000000 0 f(int) +// Address Size Align Out In Symbol +// 00201000 00000015 4 .text +// 00201000 0000000e 4 test.o:(.text) +// 0020100e 00000000 0 local +// 00201005 00000000 0 f(int) // //===----------------------------------------------------------------------===// #include "MapFile.h" #include "InputFiles.h" #include "Strings.h" +#include "SymbolTable.h" +#include "Threads.h" #include "llvm/Support/raw_ostream.h" @@ -33,83 +33,68 @@ using namespace llvm::object; using namespace lld; using namespace lld::elf; -static void writeOutSecLine(raw_fd_ostream &OS, int Width, uint64_t Address, - uint64_t Size, uint64_t Align, StringRef Name) { - OS << format("%0*llx %0*llx %5lld ", Width, Address, Width, Size, Align) - << left_justify(Name, 7); -} - -static void writeInSecLine(raw_fd_ostream &OS, int Width, uint64_t Address, - uint64_t Size, uint64_t Align, StringRef Name) { - // Pass an empty name to align the text to the correct column. - writeOutSecLine(OS, Width, Address, Size, Align, ""); - OS << ' ' << left_justify(Name, 7); -} +typedef DenseMap<const SectionBase *, SmallVector<DefinedRegular *, 4>> + SymbolMapTy; -static void writeFileLine(raw_fd_ostream &OS, int Width, uint64_t Address, - uint64_t Size, uint64_t Align, StringRef Name) { - // Pass an empty name to align the text to the correct column. - writeInSecLine(OS, Width, Address, Size, Align, ""); - OS << ' ' << left_justify(Name, 7); +// Print out the first three columns of a line. +template <class ELFT> +static void writeHeader(raw_ostream &OS, uint64_t Addr, uint64_t Size, + uint64_t Align) { + int W = ELFT::Is64Bits ? 16 : 8; + OS << format("%0*llx %0*llx %5lld ", W, Addr, W, Size, Align); } -static void writeSymbolLine(raw_fd_ostream &OS, int Width, uint64_t Address, - uint64_t Size, StringRef Name) { - // Pass an empty name to align the text to the correct column. - writeFileLine(OS, Width, Address, Size, 0, ""); - OS << ' ' << left_justify(Name, 7); +static std::string indent(int Depth) { return std::string(Depth * 8, ' '); } + +// Returns a list of all symbols that we want to print out. +template <class ELFT> std::vector<DefinedRegular *> getSymbols() { + std::vector<DefinedRegular *> V; + for (elf::ObjectFile<ELFT> *File : Symtab<ELFT>::X->getObjectFiles()) + for (SymbolBody *B : File->getSymbols()) + if (B->File == File && !B->isSection()) + if (auto *Sym = dyn_cast<DefinedRegular>(B)) + if (Sym->Section) + V.push_back(Sym); + return V; } +// Returns a map from sections to their symbols. template <class ELFT> -static void writeInputSection(raw_fd_ostream &OS, const InputSection *IS, - StringRef &PrevName) { - int Width = ELFT::Is64Bits ? 16 : 8; - StringRef Name = IS->Name; - if (Name != PrevName) { - writeInSecLine(OS, Width, IS->OutSec->Addr + IS->OutSecOff, IS->getSize(), - IS->Alignment, Name); - OS << '\n'; - PrevName = Name; - } - - elf::ObjectFile<ELFT> *File = IS->template getFile<ELFT>(); - if (!File) - return; - writeFileLine(OS, Width, IS->OutSec->Addr + IS->OutSecOff, IS->getSize(), - IS->Alignment, toString(File)); - OS << '\n'; - - for (SymbolBody *Sym : File->getSymbols()) { - auto *DR = dyn_cast<DefinedRegular>(Sym); - if (!DR) - continue; - if (DR->Section != IS) - continue; - if (DR->isSection()) - continue; - writeSymbolLine(OS, Width, Sym->getVA(), Sym->getSize<ELFT>(), - toString(*Sym)); - OS << '\n'; +SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> Syms) { + SymbolMapTy Ret; + for (DefinedRegular *S : Syms) + Ret[S->Section].push_back(S); + + // Sort symbols by address. We want to print out symbols in the + // order in the output file rather than the order they appeared + // in the input files. + for (auto &It : Ret) { + SmallVectorImpl<DefinedRegular *> &V = It.second; + std::sort(V.begin(), V.end(), [](DefinedRegular *A, DefinedRegular *B) { + return A->getVA() < B->getVA(); + }); } + return Ret; } +// Construct a map from symbols to their stringified representations. +// Demangling symbols (which is what toString() does) is slow, so +// we do that in batch using parallel-for. template <class ELFT> -static void writeMapFile2(raw_fd_ostream &OS, - ArrayRef<OutputSection *> OutputSections) { - int Width = ELFT::Is64Bits ? 16 : 8; - - OS << left_justify("Address", Width) << ' ' << left_justify("Size", Width) - << " Align Out In File Symbol\n"; - - for (OutputSection *Sec : OutputSections) { - writeOutSecLine(OS, Width, Sec->Addr, Sec->Size, Sec->Alignment, Sec->Name); - OS << '\n'; - - StringRef PrevName = ""; - for (InputSection *IS : Sec->Sections) { - writeInputSection<ELFT>(OS, IS, PrevName); - } - } +DenseMap<DefinedRegular *, std::string> +getSymbolStrings(ArrayRef<DefinedRegular *> Syms) { + std::vector<std::string> Str(Syms.size()); + parallelFor(0, Syms.size(), [&](size_t I) { + raw_string_ostream OS(Str[I]); + writeHeader<ELFT>(OS, Syms[I]->getVA(), Syms[I]->template getSize<ELFT>(), + 0); + OS << indent(2) << toString(*Syms[I]); + }); + + DenseMap<DefinedRegular *, std::string> Ret; + for (size_t I = 0, E = Syms.size(); I < E; ++I) + Ret[Syms[I]] = std::move(Str[I]); + return Ret; } template <class ELFT> @@ -117,12 +102,38 @@ void elf::writeMapFile(ArrayRef<OutputSection *> OutputSections) { if (Config->MapFile.empty()) return; + // Open a map file for writing. std::error_code EC; raw_fd_ostream OS(Config->MapFile, EC, sys::fs::F_None); - if (EC) + if (EC) { error("cannot open " + Config->MapFile + ": " + EC.message()); - else - writeMapFile2<ELFT>(OS, OutputSections); + return; + } + + // Collect symbol info that we want to print out. + std::vector<DefinedRegular *> Syms = getSymbols<ELFT>(); + SymbolMapTy SectionSyms = getSectionSyms<ELFT>(Syms); + DenseMap<DefinedRegular *, std::string> SymStr = getSymbolStrings<ELFT>(Syms); + + // Print out the header line. + int W = ELFT::Is64Bits ? 16 : 8; + OS << left_justify("Address", W) << ' ' << left_justify("Size", W) + << " Align Out In Symbol\n"; + + // Print out file contents. + for (OutputSection *OSec : OutputSections) { + writeHeader<ELFT>(OS, OSec->Addr, OSec->Size, OSec->Alignment); + OS << OSec->Name << '\n'; + + // Dump symbols for each input section. + for (InputSection *IS : OSec->Sections) { + writeHeader<ELFT>(OS, OSec->Addr + IS->OutSecOff, IS->getSize(), + IS->Alignment); + OS << indent(1) << toString(IS) << '\n'; + for (DefinedRegular *Sym : SectionSyms[IS]) + OS << SymStr[Sym] << '\n'; + } + } } template void elf::writeMapFile<ELF32LE>(ArrayRef<OutputSection *>); diff --git a/ELF/Options.td b/ELF/Options.td index fda675449956..8863912c179c 100644 --- a/ELF/Options.td +++ b/ELF/Options.td @@ -344,6 +344,26 @@ def end_group_paren: Flag<["-"], ")">; def start_group: F<"start-group">; def start_group_paren: Flag<["-"], "(">; +// LTO-related options. +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 lto_partitions: J<"lto-partitions=">, + HelpText<"Number of LTO codegen partitions">; +def disable_verify: F<"disable-verify">; +def mllvm: S<"mllvm">; +def opt_remarks_filename: Separate<["--"], "opt-remarks-filename">, + HelpText<"YAML output file for optimization remarks">; +def opt_remarks_with_hotness: Flag<["--"], "opt-remarks-with-hotness">, + HelpText<"Include hotness informations in the optimization remarks file">; +def save_temps: F<"save-temps">; +def thinlto_cache_dir: J<"thinlto-cache-dir=">, + HelpText<"Path to ThinLTO cached object file directory">; +def thinlto_cache_policy: S<"thinlto-cache-policy">, + HelpText<"Pruning policy for the ThinLTO cache">; +def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">; + // 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 @@ -365,6 +385,7 @@ 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_keep_memory: F<"no-keep-memory">; 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">; @@ -382,23 +403,3 @@ def Qy : F<"Qy">; // Aliases for ignored options def alias_version_script_version_script: J<"version-script=">, Alias<version_script>; - -// LTO-related options. -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 lto_partitions: J<"lto-partitions=">, - HelpText<"Number of LTO codegen partitions">; -def disable_verify: F<"disable-verify">; -def mllvm: S<"mllvm">; -def opt_remarks_filename: Separate<["--"], "opt-remarks-filename">, - HelpText<"YAML output file for optimization remarks">; -def opt_remarks_with_hotness: Flag<["--"], "opt-remarks-with-hotness">, - HelpText<"Include hotness informations in the optimization remarks file">; -def save_temps: F<"save-temps">; -def thinlto_cache_dir: J<"thinlto-cache-dir=">, - HelpText<"Path to ThinLTO cached object file directory">; -def thinlto_cache_policy: S<"thinlto-cache-policy">, - HelpText<"Pruning policy for the ThinLTO cache">; -def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">; diff --git a/ELF/OutputSections.cpp b/ELF/OutputSections.cpp index 71fc00ac35e7..839f68f2da55 100644 --- a/ELF/OutputSections.cpp +++ b/ELF/OutputSections.cpp @@ -395,14 +395,20 @@ static void reportDiscarded(InputSectionBase *IS) { void OutputSectionFactory::addInputSec(InputSectionBase *IS, StringRef OutsecName) { + SectionKey Key = createKey(IS, OutsecName); + OutputSection *&Sec = Map[Key]; + return addInputSec(IS, OutsecName, Sec); +} + +void OutputSectionFactory::addInputSec(InputSectionBase *IS, + StringRef OutsecName, + OutputSection *&Sec) { if (!IS->Live) { reportDiscarded(IS); return; } - SectionKey Key = createKey(IS, OutsecName); uint64_t Flags = getOutFlags(IS); - OutputSection *&Sec = Map[Key]; if (Sec) { if (getIncompatibleFlags(Sec->Flags) != getIncompatibleFlags(IS->Flags)) error("incompatible section flags for " + Sec->Name + @@ -418,7 +424,7 @@ void OutputSectionFactory::addInputSec(InputSectionBase *IS, } Sec->Flags |= Flags; } else { - Sec = make<OutputSection>(Key.Name, IS->Type, Flags); + Sec = make<OutputSection>(OutsecName, IS->Type, Flags); OutputSections.push_back(Sec); } diff --git a/ELF/OutputSections.h b/ELF/OutputSections.h index bcda77d1a26d..6405fb38c6d6 100644 --- a/ELF/OutputSections.h +++ b/ELF/OutputSections.h @@ -141,6 +141,8 @@ public: ~OutputSectionFactory(); void addInputSec(InputSectionBase *IS, StringRef OutsecName); + void addInputSec(InputSectionBase *IS, StringRef OutsecName, + OutputSection *&Sec); private: llvm::SmallDenseMap<SectionKey, OutputSection *> Map; diff --git a/ELF/Strings.h b/ELF/Strings.h index 934b6427105f..bcfa28144989 100644 --- a/ELF/Strings.h +++ b/ELF/Strings.h @@ -76,6 +76,10 @@ llvm::Optional<std::string> demangle(StringRef Name); inline StringRef toStringRef(ArrayRef<uint8_t> Arr) { return {(const char *)Arr.data(), Arr.size()}; } + +inline ArrayRef<uint8_t> toArrayRef(StringRef S) { + return {(const uint8_t *)S.data(), S.size()}; +} } } diff --git a/ELF/SymbolTable.cpp b/ELF/SymbolTable.cpp index e55b8bf52c10..541fc1bf6479 100644 --- a/ELF/SymbolTable.cpp +++ b/ELF/SymbolTable.cpp @@ -52,6 +52,9 @@ template <class ELFT> static bool isCompatible(InputFile *F) { // Add symbols in File to the symbol table. template <class ELFT> void SymbolTable<ELFT>::addFile(InputFile *File) { + if (!Config->FirstElf && isa<ELFFileBase<ELFT>>(File)) + Config->FirstElf = File; + if (!isCompatible<ELFT>(File)) return; diff --git a/ELF/SyntheticSections.cpp b/ELF/SyntheticSections.cpp index e1f81940bb59..a271d31048f5 100644 --- a/ELF/SyntheticSections.cpp +++ b/ELF/SyntheticSections.cpp @@ -97,7 +97,7 @@ static ArrayRef<uint8_t> getVersion() { // Creates a .comment section containing LLD version info. // With this feature, you can identify LLD-generated binaries easily -// by "objdump -s -j .comment <file>". +// by "readelf --string-dump .comment <file>". // The returned object is a mergeable string section. template <class ELFT> MergeInputSection *elf::createCommentSection() { typename ELFT::Shdr Hdr = {}; @@ -541,6 +541,13 @@ template <class ELFT> void EhFrameSection<ELFT>::finalizeContents() { Off += alignTo(Fde->size(), Config->Wordsize); } } + + // The LSB standard does not allow a .eh_frame section with zero + // Call Frame Information records. Therefore add a CIE record length + // 0 as a terminator if this .eh_frame section is empty. + if (Off == 0) + Off = 4; + this->Size = Off; } @@ -1022,9 +1029,9 @@ template <class ELFT> void DynamicSection<ELFT>::addEntries() { // fixed early. for (StringRef S : Config->AuxiliaryList) add({DT_AUXILIARY, In<ELFT>::DynStrTab->addString(S)}); - if (!Config->RPath.empty()) + if (!Config->Rpath.empty()) add({Config->EnableNewDtags ? DT_RUNPATH : DT_RPATH, - In<ELFT>::DynStrTab->addString(Config->RPath)}); + In<ELFT>::DynStrTab->addString(Config->Rpath)}); for (SharedFile<ELFT> *F : Symtab<ELFT>::X->getSharedFiles()) if (F->isNeeded()) add({DT_NEEDED, In<ELFT>::DynStrTab->addString(F->SoName)}); diff --git a/ELF/Writer.cpp b/ELF/Writer.cpp index 989a55af1675..e2ab48433a52 100644 --- a/ELF/Writer.cpp +++ b/ELF/Writer.cpp @@ -101,7 +101,7 @@ StringRef elf::getOutputSectionName(StringRef Name) { for (StringRef V : {".rel.", ".rela."}) { if (Name.startswith(V)) { StringRef Inner = getOutputSectionName(Name.substr(V.size() - 1)); - return Saver.save(Twine(V.drop_back()) + Inner); + return Saver.save(V.drop_back() + Inner); } } } @@ -123,7 +123,7 @@ StringRef elf::getOutputSectionName(StringRef Name) { // ".zdebug_" is a prefix for ZLIB-compressed sections. // Because we decompressed input sections, we want to remove 'z'. if (Name.startswith(".zdebug_")) - return Saver.save(Twine(".") + Name.substr(2)); + return Saver.save("." + Name.substr(2)); return Name; } @@ -254,6 +254,7 @@ template <class ELFT> void Writer<ELFT>::run() { fixSectionAlignments(); Script->fabricateDefaultCommands(Config->MaxPageSize); } + Script->synchronize(); Script->assignAddresses(Phdrs); // Remove empty PT_LOAD to avoid causing the dynamic linker to try to mmap a @@ -1080,6 +1081,7 @@ static void removeUnusedSyntheticSections(std::vector<OutputSection *> &V) { SS->OutSec->Sections.erase(std::find(SS->OutSec->Sections.begin(), SS->OutSec->Sections.end(), SS)); + SS->Live = false; // If there are no other sections in the output section, remove it from the // output. if (SS->OutSec->Sections.empty()) diff --git a/docs/index.rst b/docs/index.rst index 6d9780458d10..fbdade7daf27 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -135,7 +135,7 @@ LDFLAGS when building your programs. LLD leaves its name and version number to a ``.comment`` section in an output. If you are in doubt whether you are successfully using LLD or -not, run ``objdump -s -j .comment <output-file>`` and examine the +not, run ``readelf --string-dump .comment <output-file>`` and examine the output. If the string "Linker: LLD" is included in the output, you are using LLD. diff --git a/test/COFF/Inputs/constant-import.s b/test/COFF/Inputs/constant-import.s new file mode 100644 index 000000000000..4249d74c9cb7 --- /dev/null +++ b/test/COFF/Inputs/constant-import.s @@ -0,0 +1,21 @@ + + .def __DllMainCRTStartup@12 + .type 32 + .scl 2 + .endef + .global __DllMainCRTStartup@12 +__DllMainCRTStartup@12: + ret + + .data + .def _Data + .type 0 + .scl 2 + .endef + .global _Data +_Data: + .long ___CFConstantStringClassReference + + .section .drectve + .ascii " -export:_Data" + diff --git a/test/COFF/constant.test b/test/COFF/constant.test new file mode 100644 index 000000000000..3c8956beac9c --- /dev/null +++ b/test/COFF/constant.test @@ -0,0 +1,5 @@ +RUN: mkdir -p %t +RUN: llvm-mc -triple i686-unknown-windows-msvc -filetype obj -o %t/import.o %S/Inputs/constant-import.s +RUN: llc -mtriple i686-unknown-windows-msvc -filetype obj -o %t/export.o %S/Inputs/constant-export.ll +RUN: lld-link -machine:x86 -dll -out:%t/export.dll %t/export.o -entry:__CFConstantStringClassReference +RUN: lld-link -machine:x86 -dll -out:%t/import.dll %t/import.o %t/export.lib diff --git a/test/COFF/icf-data.test b/test/COFF/icf-data.test new file mode 100644 index 000000000000..20bdecaf3cfe --- /dev/null +++ b/test/COFF/icf-data.test @@ -0,0 +1,61 @@ +# RUN: yaml2obj < %s > %t.obj +# RUN: lld-link /entry:foo /out:%t.exe /subsystem:console /include:bar \ +# RUN: /verbose %t.obj > %t.log 2>&1 +# RUN: FileCheck %s < %t.log + +# CHECK-NOT: Removed foo +# CHECK-NOT: Removed bar + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [] +sections: + - Name: '.text$mn' + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: 4883EC28E8000000004883C428C3 + - Name: '.text$mn' + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: 4883EC28E8000000004883C428C3 +symbols: + - Name: '.text$mn' + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 14 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 1682752513 + Number: 0 + Selection: IMAGE_COMDAT_SELECT_NODUPLICATES + - Name: '.text$mn' + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 14 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 1682752513 + Number: 0 + Selection: IMAGE_COMDAT_SELECT_NODUPLICATES + - Name: foo + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: bar + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/COFF/lldmap.test b/test/COFF/lldmap.test index 69080fd639ab..d705a16c6c2a 100644 --- a/test/COFF/lldmap.test +++ b/test/COFF/lldmap.test @@ -1,11 +1,10 @@ # RUN: yaml2obj < %p/Inputs/ret42.yaml > %t.obj # RUN: lld-link /out:%t.exe /entry:main /lldmap:%T/foo.map %t.obj -# RUN: FileCheck %s < %T/foo.map +# RUN: FileCheck -strict-whitespace %s < %T/foo.map # RUN: lld-link /out:%T/bar.exe /entry:main /lldmap %t.obj -# RUN: FileCheck %s < %T/bar.map +# RUN: FileCheck -strict-whitespace %s < %T/bar.map -# CHECK: Address Size Align Out In File Symbol +# CHECK: Address Size Align Out In Symbol # CHECK-NEXT: 00001000 00000006 4096 .text -# CHECK-NEXT: 00001000 00000006 16 .text$mn -# CHECK-NEXT: 00001000 00000006 16 {{.*}}lldmap.test.tmp.obj -# CHECK-NEXT: 00001000 00000006 0 main +# CHECK-NEXT: 00001000 00000006 16 {{.*}}lldmap.test.tmp.obj:(.text$mn) +# CHECK-NEXT: 00001000 00000000 0 main diff --git a/test/ELF/Inputs/eh-frame-end.s b/test/ELF/Inputs/eh-frame-end.s new file mode 100644 index 000000000000..b7334d8ffbbf --- /dev/null +++ b/test/ELF/Inputs/eh-frame-end.s @@ -0,0 +1,2 @@ + .section ".eh_frame", "a", @progbits + .long 0 diff --git a/test/ELF/abs-conflict.s b/test/ELF/abs-conflict.s index 17a530200033..4662c48a4e40 100644 --- a/test/ELF/abs-conflict.s +++ b/test/ELF/abs-conflict.s @@ -15,4 +15,4 @@ foo = 0x123 // DUP: duplicate symbol: foo // DUP-NEXT: >>> defined in {{.*}}.o -// DUP-NEXT: >>> defined in (internal) +// DUP-NEXT: >>> defined in <internal> diff --git a/test/ELF/debug-gnu-pubnames.s b/test/ELF/debug-gnu-pubnames.s new file mode 100644 index 000000000000..0a8693c97eb8 --- /dev/null +++ b/test/ELF/debug-gnu-pubnames.s @@ -0,0 +1,10 @@ +# REQUIRES: x86 +# RUN: ld.lld -e main %p/Inputs/gdb-index-a.elf %p/Inputs/gdb-index-b.elf -o %t1.exe +# RUN: llvm-readobj -sections %t1.exe | FileCheck -check-prefix=CHECK1 %s +# CHECK1: Name: .debug_gnu_pubnames +# CHECK1: Name: .debug_gnu_pubtypes + +# RUN: ld.lld -gdb-index -e main %p/Inputs/gdb-index-a.elf %p/Inputs/gdb-index-b.elf -o %t2.exe +# RUN: llvm-readobj -sections %t2.exe | FileCheck -check-prefix=CHECK2 %s +# CHECK2-NOT: Name: .debug_gnu_pubnames +# CHECK2-NOT: Name: .debug_gnu_pubtypes diff --git a/test/ELF/driver-access.test b/test/ELF/driver-access.test index 3c976a66d0f4..5209483ae480 100644 --- a/test/ELF/driver-access.test +++ b/test/ELF/driver-access.test @@ -8,6 +8,7 @@ # RUN: chmod 100 %t.dir # RUN: cd %t.dir # RUN: ld.lld %t.o -o %t.exe +# RUN: chmod 755 %t.dir .globl _start _start: diff --git a/test/ELF/eh-frame-begin-end.s b/test/ELF/eh-frame-begin-end.s new file mode 100644 index 000000000000..a7f0bb7d3efd --- /dev/null +++ b/test/ELF/eh-frame-begin-end.s @@ -0,0 +1,17 @@ +// REQUIRES: x86 +// RUN: llvm-mc -filetype=obj -triple=amd64-unknown-openbsd %s -o %t.o +// RUN: llvm-mc -filetype=obj -triple=amd64-unknown-openbsd %p/Inputs/eh-frame-end.s -o %t2.o +// RUN: ld.lld %t.o %t2.o -o %t +// RUN: llvm-readobj -sections %t | FileCheck %s + +// CHECK: Name: .eh_frame +// CHECK-NEXT: Type: SHT_PROGBITS +// CHECK-NEXT: Flags [ +// CHECK-NEXT: SHF_ALLOC +// CHECK-NEXT: ] +// CHECK-NEXT: Address: 0x200120 +// CHECK-NEXT: Offset: 0x120 +// CHECK-NEXT: Size: 4 + + .section ".eh_frame", "a", @progbits +__EH_FRAME_BEGIN__: diff --git a/test/ELF/gdb-index.s b/test/ELF/gdb-index.s index 4ad5c13d3158..b7d8a708ace5 100644 --- a/test/ELF/gdb-index.s +++ b/test/ELF/gdb-index.s @@ -34,8 +34,8 @@ # CHECK-NEXT: 0: Offset = 0x0, Length = 0x34 # CHECK-NEXT: 1: Offset = 0x34, Length = 0x34 # CHECK: Address area offset = 0x38, has 2 entries: -# CHECK-NEXT: Low address = 0x201000, High address = 0x20100b, CU index = 0 -# CHECK-NEXT: Low address = 0x20100b, High address = 0x201016, CU index = 1 +# CHECK-NEXT: Low/High address = [0x201000, 0x20100b) (Size: 0xb), CU id = 0 +# CHECK-NEXT: Low/High address = [0x20100b, 0x201016) (Size: 0xb), CU id = 1 # CHECK: Symbol table offset = 0x60, size = 1024, filled slots: # CHECK-NEXT: 489: Name offset = 0x1d, CU vector offset = 0x0 # CHECK-NEXT: String name: main, CU vector index: 0 diff --git a/test/ELF/linkerscript/section-metadata.s b/test/ELF/linkerscript/section-metadata.s new file mode 100644 index 000000000000..f447240ac3a9 --- /dev/null +++ b/test/ELF/linkerscript/section-metadata.s @@ -0,0 +1,33 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o + +# RUN: echo "SECTIONS { .text : { *(.text.bar) *(.text.foo) } }" > %t.script +# RUN: ld.lld -o %t --script %t.script %t.o +# RUN: llvm-objdump -s %t | FileCheck %s + +# RUN: echo "SECTIONS { .text : { *(.text.foo) *(.text.bar) } }" > %t.script +# RUN: ld.lld -o %t --script %t.script %t.o +# RUN: llvm-objdump -s %t | FileCheck --check-prefix=INV %s + + +# CHECK: Contents of section .text: +# CHECK-NEXT: 02000000 00000000 01000000 00000000 +# CHECK: Contents of section .rodata: +# CHECK-NEXT: 02000000 00000000 01000000 00000000 + +# INV: Contents of section .text: +# INV-NEXT: 01000000 00000000 02000000 00000000 +# INV: Contents of section .rodata: +# INV-NEXT: 01000000 00000000 02000000 00000000 + +.global _start +_start: + +.section .text.bar,"a",@progbits +.quad 2 +.section .text.foo,"a",@progbits +.quad 1 +.section .rodata.foo,"ao",@progbits,.text.foo +.quad 1 +.section .rodata.bar,"ao",@progbits,.text.bar +.quad 2 diff --git a/test/ELF/linkerscript/sections.s b/test/ELF/linkerscript/sections.s index b7d396fdec9f..69c6f19d078d 100644 --- a/test/ELF/linkerscript/sections.s +++ b/test/ELF/linkerscript/sections.s @@ -86,7 +86,8 @@ # Idx Name Size # SEC-MULTI: 1 .text 0000000e {{[0-9a-f]*}} TEXT DATA -# SEC-MULTI-NEXT: .data 00000023 {{[0-9a-f]*}} DATA +# SEC-MULTI-NEXT: .data 00000020 {{[0-9a-f]*}} DATA +# SEC-MULTI-NEXT: .data 00000003 {{[0-9a-f]*}} DATA # SEC-MULTI-NEXT: .bss 00000002 {{[0-9a-f]*}} BSS # SEC-MULTI-NEXT: .comment 00000008 {{[0-9a-f]*}} # SEC-MULTI-NEXT: .symtab 00000030 {{[0-9a-f]*}} diff --git a/test/ELF/map-file.s b/test/ELF/map-file.s index e74c91645cb9..9dbbda0ff0e9 100644 --- a/test/ELF/map-file.s +++ b/test/ELF/map-file.s @@ -6,10 +6,10 @@ // RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/map-file4.s -o %t4.o // RUN: rm -f %t4.a // RUN: llvm-ar rc %t4.a %t4.o -// RUN: ld.lld %t1.o %t2.o %t3.o %t4.a -o %t -M | FileCheck %s -// RUN: ld.lld %t1.o %t2.o %t3.o %t4.a -o %t -print-map | FileCheck %s +// RUN: ld.lld %t1.o %t2.o %t3.o %t4.a -o %t -M | FileCheck -strict-whitespace %s +// RUN: ld.lld %t1.o %t2.o %t3.o %t4.a -o %t -print-map | FileCheck -strict-whitespace %s // RUN: ld.lld %t1.o %t2.o %t3.o %t4.a -o %t -Map=%t.map -// RUN: FileCheck %s < %t.map +// RUN: FileCheck -strict-whitespace %s < %t.map .global _start _start: @@ -26,36 +26,33 @@ bar: local: .comm common,4,16 -// CHECK: Address Size Align Out In File Symbol +// CHECK: Address Size Align Out In Symbol // CHECK-NEXT: 0000000000200158 0000000000000030 8 .eh_frame -// CHECK-NEXT: 0000000000200158 0000000000000030 8 .eh_frame +// CHECK-NEXT: 0000000000200158 0000000000000030 8 <internal>:(.eh_frame) // CHECK-NEXT: 0000000000201000 0000000000000015 4 .text -// CHECK-NEXT: 0000000000201000 000000000000000e 4 .text -// CHECK-NEXT: 0000000000201000 000000000000000e 4 {{.*}}{{/|\\}}map-file.s.tmp1.o -// CHECK-NEXT: 000000000020100e 0000000000000000 0 local -// CHECK-NEXT: 0000000000201005 0000000000000000 0 f(int) -// CHECK-NEXT: 0000000000201000 0000000000000000 0 _start -// CHECK-NEXT: 0000000000201010 0000000000000002 4 {{.*}}{{/|\\}}map-file.s.tmp2.o -// CHECK-NEXT: 0000000000201010 0000000000000000 0 foo -// CHECK-NEXT: 0000000000201011 0000000000000000 0 bar -// CHECK-NEXT: 0000000000201012 0000000000000000 1 .text.zed -// CHECK-NEXT: 0000000000201012 0000000000000000 1 {{.*}}{{/|\\}}map-file.s.tmp2.o -// CHECK-NEXT: 0000000000201012 0000000000000000 0 zed -// CHECK-NEXT: 0000000000201014 0000000000000000 4 .text -// CHECK-NEXT: 0000000000201014 0000000000000000 4 {{.*}}{{/|\\}}map-file.s.tmp3.o -// CHECK-NEXT: 0000000000201014 0000000000000000 0 bah -// CHECK-NEXT: 0000000000201014 0000000000000001 4 {{.*}}{{/|\\}}map-file.s.tmp4.a(map-file.s.tmp4.o) -// CHECK-NEXT: 0000000000201014 0000000000000000 0 baz +// CHECK-NEXT: 0000000000201000 000000000000000e 4 {{.*}}{{/|\\}}map-file.s.tmp1.o:(.text) +// CHECK-NEXT: 0000000000201000 0000000000000000 0 _start +// CHECK-NEXT: 0000000000201005 0000000000000000 0 f(int) +// CHECK-NEXT: 000000000020100e 0000000000000000 0 local +// CHECK-NEXT: 0000000000201010 0000000000000002 4 {{.*}}{{/|\\}}map-file.s.tmp2.o:(.text) +// CHECK-NEXT: 0000000000201010 0000000000000000 0 foo +// CHECK-NEXT: 0000000000201011 0000000000000000 0 bar +// CHECK-NEXT: 0000000000201012 0000000000000000 1 {{.*}}{{/|\\}}map-file.s.tmp2.o:(.text.zed) +// CHECK-NEXT: 0000000000201012 0000000000000000 0 zed +// CHECK-NEXT: 0000000000201014 0000000000000000 4 {{.*}}{{/|\\}}map-file.s.tmp3.o:(.text) +// CHECK-NEXT: 0000000000201014 0000000000000000 0 bah +// CHECK-NEXT: 0000000000201014 0000000000000001 4 {{.*}}{{/|\\}}map-file.s.tmp4.a(map-file.s.tmp4.o):(.text) +// CHECK-NEXT: 0000000000201014 0000000000000000 0 baz // CHECK-NEXT: 0000000000202000 0000000000000004 16 .bss -// CHECK-NEXT: 0000000000202000 0000000000000004 16 COMMON +// CHECK-NEXT: 0000000000202000 0000000000000004 16 <internal>:(COMMON) // CHECK-NEXT: 0000000000000000 0000000000000008 1 .comment -// CHECK-NEXT: 0000000000000000 0000000000000008 1 .comment +// CHECK-NEXT: 0000000000000000 0000000000000008 1 <internal>:(.comment) // CHECK-NEXT: 0000000000000000 00000000000000f0 8 .symtab -// CHECK-NEXT: 0000000000000000 00000000000000f0 8 .symtab +// CHECK-NEXT: 0000000000000000 00000000000000f0 8 <internal>:(.symtab) // CHECK-NEXT: 0000000000000000 0000000000000039 1 .shstrtab -// CHECK-NEXT: 0000000000000000 0000000000000039 1 .shstrtab +// CHECK-NEXT: 0000000000000000 0000000000000039 1 <internal>:(.shstrtab) // CHECK-NEXT: 0000000000000000 000000000000002f 1 .strtab -// CHECK-NEXT: 0000000000000000 000000000000002f 1 .strtab +// CHECK-NEXT: 0000000000000000 000000000000002f 1 <internal>:(.strtab) // RUN: not ld.lld %t1.o %t2.o %t3.o %t4.a -o %t -Map=/ 2>&1 \ // RUN: | FileCheck -check-prefix=FAIL %s diff --git a/test/ELF/relocation-in-merge.s b/test/ELF/relocation-in-merge.s index 9ce2e4f79150..b37eff3ab892 100644 --- a/test/ELF/relocation-in-merge.s +++ b/test/ELF/relocation-in-merge.s @@ -1,7 +1,12 @@ // REQUIRES: x86 // RUN: llvm-mc %s -o %t.o -filetype=obj -triple=x86_64-pc-linux -// RUN: not ld.lld %t.o -o %t 2>&1 | FileCheck %s -// CHECK: relocations pointing to SHF_MERGE are not supported +// RUN: ld.lld %t.o -o %t -shared +// RUN: llvm-objdump -section-headers %t | FileCheck %s - .section .foo,"aM",@progbits,4 - .long bar +// Test that we accept this by just not merging the section. +// CHECK: .foo 00000008 + +bar: + .section .foo,"aM",@progbits,8 + .long bar - . + .long bar - . |