diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp index 3e7f10bf8d11..daddfb86d4cf 100644 --- a/COFF/Driver.cpp +++ b/COFF/Driver.cpp @@ -509,7 +509,7 @@ filterBitcodeFiles(StringRef Path, std::vector &TemporaryFiles) { // Create response file contents and invoke the MSVC linker. void LinkerDriver::invokeMSVC(opt::InputArgList &Args) { - std::string Rsp = "/nologo "; + std::string Rsp = "/nologo\n"; std::vector Temps; for (auto *Arg : Args) { @@ -528,14 +528,14 @@ void LinkerDriver::invokeMSVC(opt::InputArgList &Args) { case OPT_INPUT: { if (Optional Path = doFindFile(Arg->getValue())) { if (Optional S = filterBitcodeFiles(*Path, Temps)) - Rsp += quote(*S) + " "; + Rsp += quote(*S) + "\n"; continue; } - Rsp += quote(Arg->getValue()) + " "; + Rsp += quote(Arg->getValue()) + "\n"; break; } default: - Rsp += toString(Arg) + " "; + Rsp += toString(Arg) + "\n"; } } diff --git a/COFF/DriverUtils.cpp b/COFF/DriverUtils.cpp index a9c1c9d5593e..2c9ba797f73b 100644 --- a/COFF/DriverUtils.cpp +++ b/COFF/DriverUtils.cpp @@ -634,7 +634,7 @@ void runMSVCLinker(std::string Rsp, ArrayRef Objects) { std::vector Temps; for (StringRef S : Objects) { Temps.emplace_back("lto", "obj", S); - Rsp += quote(Temps.back().Path) + " "; + Rsp += quote(Temps.back().Path) + "\n"; } log("link.exe " + Rsp); diff --git a/ELF/Config.h b/ELF/Config.h index c8eecec7439c..d25c63c3c0d2 100644 --- a/ELF/Config.h +++ b/ELF/Config.h @@ -104,6 +104,7 @@ struct Configuration { bool Bsymbolic; bool BsymbolicFunctions; bool ColorDiagnostics = false; + bool CompressDebugSections; bool DefineCommon; bool Demangle = true; bool DisableVerify; diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp index 47ecd607a48f..93924e4554c9 100644 --- a/ELF/Driver.cpp +++ b/ELF/Driver.cpp @@ -45,6 +45,7 @@ #include "llvm/ADT/StringSwitch.h" #include "llvm/Object/Decompressor.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/Path.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/TargetSelect.h" @@ -564,12 +565,24 @@ static std::vector getLines(MemoryBufferRef MB) { return Ret; } +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; +} + // Initializes Config members by the command line options. void LinkerDriver::readConfigs(opt::InputArgList &Args) { Config->AllowMultipleDefinition = Args.hasArg(OPT_allow_multiple_definition); Config->AuxiliaryList = getArgs(Args, OPT_auxiliary); Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic); Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions); + Config->CompressDebugSections = getCompressDebugSections(Args); Config->DefineCommon = getArg(Args, OPT_define_common, OPT_no_define_common, !Args.hasArg(OPT_relocatable)); Config->Demangle = getArg(Args, OPT_demangle, OPT_no_demangle, true); diff --git a/ELF/LinkerScript.cpp b/ELF/LinkerScript.cpp index ab2ca22e9e17..63eb90456e17 100644 --- a/ELF/LinkerScript.cpp +++ b/ELF/LinkerScript.cpp @@ -413,6 +413,56 @@ void LinkerScript::processCommands(OutputSectionFactory &Factory) { CurOutSec = nullptr; } +void LinkerScript::fabricateDefaultCommands(bool AllocateHeader) { + std::vector Commands; + + // Define start address + uint64_t StartAddr = Config->ImageBase; + if (AllocateHeader) + StartAddr += elf::getHeaderSize(); + + // The Sections with -T
are sorted in order of ascending address + // we must use this if it is lower than StartAddr as calls to setDot() must + // be monotonically increasing + if (!Config->SectionStartMap.empty()) { + uint64_t LowestSecStart = Config->SectionStartMap.begin()->second; + StartAddr = std::min(StartAddr, LowestSecStart); + } + Commands.push_back( + make(".", [=] { return StartAddr; }, "")); + + // For each OutputSection that needs a VA fabricate an OutputSectionCommand + // with an InputSectionDescription describing the InputSections + for (OutputSection *Sec : *OutputSections) { + if (!(Sec->Flags & SHF_ALLOC)) + continue; + + auto I = Config->SectionStartMap.find(Sec->Name); + if (I != Config->SectionStartMap.end()) + Commands.push_back( + make(".", [=] { return I->second; }, "")); + + auto *OSCmd = make(Sec->Name); + OSCmd->Sec = Sec; + if (Sec->PageAlign) + OSCmd->AddrExpr = [=] { + return alignTo(Script->getDot(), Config->MaxPageSize); + }; + Commands.push_back(OSCmd); + if (Sec->Sections.size()) { + auto *ISD = make(""); + OSCmd->Commands.push_back(ISD); + for (InputSection *ISec : Sec->Sections) { + ISD->Sections.push_back(ISec); + ISec->Assigned = true; + } + } + } + // SECTIONS commands run before other non SECTIONS commands + Commands.insert(Commands.end(), Opt.Commands.begin(), Opt.Commands.end()); + Opt.Commands = std::move(Commands); +} + // Add sections that didn't match any sections command. void LinkerScript::addOrphanSections(OutputSectionFactory &Factory) { for (InputSectionBase *S : InputSections) diff --git a/ELF/LinkerScript.h b/ELF/LinkerScript.h index 04a388efb4e9..61942b2db357 100644 --- a/ELF/LinkerScript.h +++ b/ELF/LinkerScript.h @@ -256,6 +256,7 @@ class LinkerScript { bool isDefined(StringRef S); std::vector *OutputSections; + void fabricateDefaultCommands(bool AllocateHeader); void addOrphanSections(OutputSectionFactory &Factory); void removeEmptyCommands(); void adjustSectionsBeforeSorting(); diff --git a/ELF/Options.td b/ELF/Options.td index 7ed8dfb090bd..4cf14c9011c3 100644 --- a/ELF/Options.td +++ b/ELF/Options.td @@ -22,6 +22,9 @@ def build_id: F<"build-id">, HelpText<"Generate build ID note">; def build_id_eq: J<"build-id=">, HelpText<"Generate build ID note">; +def compress_debug_sections : J<"compress-debug-sections=">, + HelpText<"Compress DWARF debug sections">; + def L: JoinedOrSeparate<["-"], "L">, MetaVarName<"">, HelpText<"Add a directory to the library search path">; diff --git a/ELF/OutputSections.cpp b/ELF/OutputSections.cpp index 93f83100a745..a40818d2d301 100644 --- a/ELF/OutputSections.cpp +++ b/ELF/OutputSections.cpp @@ -16,6 +16,7 @@ #include "SyntheticSections.h" #include "Target.h" #include "Threads.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/Dwarf.h" #include "llvm/Support/MD5.h" #include "llvm/Support/MathExtras.h" @@ -83,6 +84,33 @@ static bool compareByFilePosition(InputSection *A, InputSection *B) { return LA->OutSecOff < LB->OutSecOff; } +// Compress section contents if this section contains debug info. +template void OutputSection::maybeCompress() { + typedef typename ELFT::Chdr Elf_Chdr; + + // Compress only DWARF debug sections. + if (!Config->CompressDebugSections || (Flags & SHF_ALLOC) || + !Name.startswith(".debug_")) + return; + + // Create a section header. + ZDebugHeader.resize(sizeof(Elf_Chdr)); + auto *Hdr = reinterpret_cast(ZDebugHeader.data()); + Hdr->ch_type = ELFCOMPRESS_ZLIB; + Hdr->ch_size = Size; + Hdr->ch_addralign = Alignment; + + // Write section contents to a temporary buffer and compress it. + std::vector Buf(Size); + writeTo(Buf.data()); + if (Error E = zlib::compress(toStringRef(Buf), CompressedData)) + fatal("compress failed: " + llvm::toString(std::move(E))); + + // Update section headers. + Size = sizeof(Elf_Chdr) + CompressedData.size(); + Flags |= SHF_COMPRESSED; +} + template void OutputSection::finalize() { if ((this->Flags & SHF_LINK_ORDER) && !this->Sections.empty()) { std::sort(Sections.begin(), Sections.end(), compareByFilePosition); @@ -245,6 +273,15 @@ uint32_t OutputSection::getFiller() { template void OutputSection::writeTo(uint8_t *Buf) { Loc = Buf; + // We may have already rendered compressed content when using + // -compress-debug-sections option. Write it together with header. + if (!CompressedData.empty()) { + memcpy(Buf, ZDebugHeader.data(), ZDebugHeader.size()); + memcpy(Buf + ZDebugHeader.size(), CompressedData.data(), + CompressedData.size()); + return; + } + // Write leading padding. uint32_t Filler = getFiller(); if (Filler) @@ -422,6 +459,11 @@ template void OutputSection::finalize(); template void OutputSection::finalize(); template void OutputSection::finalize(); +template void OutputSection::maybeCompress(); +template void OutputSection::maybeCompress(); +template void OutputSection::maybeCompress(); +template void OutputSection::maybeCompress(); + template void OutputSection::writeTo(uint8_t *Buf); template void OutputSection::writeTo(uint8_t *Buf); template void OutputSection::writeTo(uint8_t *Buf); diff --git a/ELF/OutputSections.h b/ELF/OutputSections.h index 0ae3df5f7859..bcda77d1a26d 100644 --- a/ELF/OutputSections.h +++ b/ELF/OutputSections.h @@ -84,9 +84,14 @@ class OutputSection final : public SectionBase { uint32_t getFiller(); template void writeTo(uint8_t *Buf); template void finalize(); + template void maybeCompress(); void assignOffsets(); std::vector Sections; + // Used for implementation of --compress-debug-sections option. + std::vector ZDebugHeader; + llvm::SmallVector CompressedData; + // Location in the output buffer. uint8_t *Loc = nullptr; }; diff --git a/ELF/Writer.cpp b/ELF/Writer.cpp index 3ded0c675b80..e8718c258c77 100644 --- a/ELF/Writer.cpp +++ b/ELF/Writer.cpp @@ -19,6 +19,7 @@ #include "SymbolTable.h" #include "SyntheticSections.h" #include "Target.h" +#include "Threads.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/FileOutputBuffer.h" @@ -58,7 +59,6 @@ template class Writer { std::vector createPhdrs(); void removeEmptyPTLoad(); void addPtArmExid(std::vector &Phdrs); - void assignAddresses(); void assignFileOffsets(); void assignFileOffsetsBinary(); void setPhdrs(); @@ -250,13 +250,11 @@ template void Writer::run() { if (Config->Relocatable) { assignFileOffsets(); } else { - if (Script->Opt.HasSections) { - Script->assignAddresses(Phdrs); - } else { + if (!Script->Opt.HasSections) { fixSectionAlignments(); - assignAddresses(); - Script->processNonSectionCommands(); + Script->fabricateDefaultCommands(Config->MaxPageSize); } + Script->assignAddresses(Phdrs); // Remove empty PT_LOAD to avoid causing the dynamic linker to try to mmap a // 0 sized region. This has to be done late since only after assignAddresses @@ -1216,6 +1214,12 @@ template void Writer::finalizeSections() { for (OutputSection *Sec : OutputSections) Sec->finalize(); + // If -compressed-debug-sections is specified, we need to compress + // .debug_* sections. Do it right now because it changes the size of + // output sections. + parallelForEach(OutputSections.begin(), OutputSections.end(), + [](OutputSection *S) { S->maybeCompress(); }); + // createThunks may have added local symbols to the static symbol table applySynthetic({In::SymTab, In::ShStrTab, In::StrTab}, [](SyntheticSection *SS) { SS->postThunkContents(); }); @@ -1502,37 +1506,6 @@ template void Writer::fixHeaders() { AllocateHeader = allocateHeaders(Phdrs, OutputSections, Min); } -// Assign VAs (addresses at run-time) to output sections. -template void Writer::assignAddresses() { - uint64_t VA = Config->ImageBase; - uint64_t ThreadBssOffset = 0; - - if (AllocateHeader) - VA += getHeaderSize(); - - for (OutputSection *Sec : OutputSections) { - uint32_t Alignment = Sec->Alignment; - if (Sec->PageAlign) - Alignment = std::max(Alignment, Config->MaxPageSize); - - auto I = Config->SectionStartMap.find(Sec->Name); - if (I != Config->SectionStartMap.end()) - VA = I->second; - - // We only assign VAs to allocated sections. - if (needsPtLoad(Sec)) { - VA = alignTo(VA, Alignment); - Sec->Addr = VA; - VA += Sec->Size; - } else if (Sec->Flags & SHF_TLS && Sec->Type == SHT_NOBITS) { - uint64_t TVA = VA + ThreadBssOffset; - TVA = alignTo(TVA, Alignment); - Sec->Addr = TVA; - ThreadBssOffset = TVA - VA + Sec->Size; - } - } -} - // Adjusts the file alignment for a given output section and returns // its new file offset. The file offset must be the same with its // virtual address (modulo the page size) so that the loader can load diff --git a/test/COFF/msvclto.ll b/test/COFF/msvclto.ll index e8a6b5c49ce9..7fa9c54711b4 100644 --- a/test/COFF/msvclto.ll +++ b/test/COFF/msvclto.ll @@ -5,7 +5,8 @@ ; RUN: /entry:main /verbose > %t.log || true ; RUN: FileCheck %s < %t.log -; CHECK: /opt:icf /entry:main /verbose +; CHECK: /opt:icf /entry:main +; CHECK: /verbose target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-pc-windows-msvc" diff --git a/test/ELF/compress-debug-sections.s b/test/ELF/compress-debug-sections.s new file mode 100644 index 000000000000..5fb7ee515dcb --- /dev/null +++ b/test/ELF/compress-debug-sections.s @@ -0,0 +1,32 @@ +# REQUIRES: x86, zlib + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: ld.lld %t.o -o %t1 --compress-debug-sections=zlib + +# RUN: llvm-objdump -s %t1 | FileCheck %s --check-prefix=ZLIBCONTENT +# ZLIBCONTENT: Contents of section .debug_str: +# ZLIBCONTENT-NOT: AAAAAAAAA + +# RUN: llvm-readobj -s %t1 | FileCheck %s --check-prefix=ZLIBFLAGS +# ZLIBFLAGS: Section { +# ZLIBFLAGS: Index: +# ZLIBFLAGS: Name: .debug_str +# ZLIBFLAGS-NEXT: Type: SHT_PROGBITS +# ZLIBFLAGS-NEXT: Flags [ +# ZLIBFLAGS-NEXT: SHF_COMPRESSED + +# RUN: llvm-dwarfdump %t1 -debug-dump=str | \ +# RUN: FileCheck %s --check-prefix=DEBUGSTR +# DEBUGSTR: .debug_str contents: +# DEBUGSTR-NEXT: AAAAAAAAAAAAAAAAAAAAAAAAAAA +# DEBUGSTR-NEXT: BBBBBBBBBBBBBBBBBBBBBBBBBBB + +# RUN: not ld.lld %t.o -o %t1 --compress-debug-sections=zlib-gabi 2>&1 | \ +# RUN: FileCheck -check-prefix=ERR %s +# ERR: unknown --compress-debug-sections value: zlib-gabi + +.section .debug_str,"MS",@progbits,1 +.Linfo_string0: + .asciz "AAAAAAAAAAAAAAAAAAAAAAAAAAA" +.Linfo_string1: + .asciz "BBBBBBBBBBBBBBBBBBBBBBBBBBB" diff --git a/test/ELF/tls-offset.s b/test/ELF/tls-offset.s index ad921af8eff6..062def4e14fc 100644 --- a/test/ELF/tls-offset.s +++ b/test/ELF/tls-offset.s @@ -2,7 +2,17 @@ // RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t // RUN: ld.lld %t -o %tout // RUN: llvm-readobj -s %tout | FileCheck %s - +// RUN: echo "SECTIONS { \ +// RUN: . = 0x201000; \ +// RUN: .text : { *(.text) } \ +// RUN: . = 0x202000; \ +// RUN: .tdata : { *(.tdata) } \ +// RUN: .tbss : { *(.tbss) } \ +// RUN: .data.rel.ro : { *(.data.rel.ro) } \ +// RUN: }" > %t.script + // RUN: ld.lld -T %t.script %t -o %tout2 +// RUN: echo SCRIPT +// RUN: llvm-readobj -s %tout2 | FileCheck %s .global _start _start: retq @@ -51,6 +61,6 @@ _start: // CHECK-NEXT: SHF_ALLOC // CHECK-NEXT: SHF_WRITE // CHECK-NEXT: ] -// CHECK-NEXT: Address: 0x202004 -// CHECK-NEXT: Offset: 0x2004 +// CHECK-NEXT: Address: 0x202010 +// CHECK-NEXT: Offset: 0x2010 // CHECK-NEXT: Size: 4