summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--COFF/Driver.cpp8
-rw-r--r--COFF/DriverUtils.cpp2
-rw-r--r--ELF/Config.h1
-rw-r--r--ELF/Driver.cpp13
-rw-r--r--ELF/LinkerScript.cpp50
-rw-r--r--ELF/LinkerScript.h1
-rw-r--r--ELF/Options.td3
-rw-r--r--ELF/OutputSections.cpp42
-rw-r--r--ELF/OutputSections.h5
-rw-r--r--ELF/Writer.cpp47
-rw-r--r--test/COFF/msvclto.ll3
-rw-r--r--test/ELF/compress-debug-sections.s32
-rw-r--r--test/ELF/tls-offset.s16
13 files changed, 177 insertions, 46 deletions
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<std::string> &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<std::string> Temps;
for (auto *Arg : Args) {
@@ -528,14 +528,14 @@ void LinkerDriver::invokeMSVC(opt::InputArgList &Args) {
case OPT_INPUT: {
if (Optional<StringRef> Path = doFindFile(Arg->getValue())) {
if (Optional<std::string> 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<StringRef> Objects) {
std::vector<TemporaryFile> 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<StringRef> 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<BaseCommand *> Commands;
+
+ // Define start address
+ uint64_t StartAddr = Config->ImageBase;
+ if (AllocateHeader)
+ StartAddr += elf::getHeaderSize();
+
+ // The Sections with -T<section> 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<SymbolAssignment>(".", [=] { 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<SymbolAssignment>(".", [=] { return I->second; }, ""));
+
+ auto *OSCmd = make<OutputSectionCommand>(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<InputSectionDescription>("");
+ 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 @@ public:
bool isDefined(StringRef S);
std::vector<OutputSection *> *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<"<dir>">,
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 <class ELFT> 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<Elf_Chdr *>(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<uint8_t> Buf(Size);
+ writeTo<ELFT>(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 <class ELFT> 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 <class ELFT> 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<ELF32BE>();
template void OutputSection::finalize<ELF64LE>();
template void OutputSection::finalize<ELF64BE>();
+template void OutputSection::maybeCompress<ELF32LE>();
+template void OutputSection::maybeCompress<ELF32BE>();
+template void OutputSection::maybeCompress<ELF64LE>();
+template void OutputSection::maybeCompress<ELF64BE>();
+
template void OutputSection::writeTo<ELF32LE>(uint8_t *Buf);
template void OutputSection::writeTo<ELF32BE>(uint8_t *Buf);
template void OutputSection::writeTo<ELF64LE>(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 @@ public:
uint32_t getFiller();
template <class ELFT> void writeTo(uint8_t *Buf);
template <class ELFT> void finalize();
+ template <class ELFT> void maybeCompress();
void assignOffsets();
std::vector<InputSection *> Sections;
+ // Used for implementation of --compress-debug-sections option.
+ std::vector<uint8_t> ZDebugHeader;
+ llvm::SmallVector<char, 1> 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 @@ private:
std::vector<PhdrEntry> createPhdrs();
void removeEmptyPTLoad();
void addPtArmExid(std::vector<PhdrEntry> &Phdrs);
- void assignAddresses();
void assignFileOffsets();
void assignFileOffsetsBinary();
void setPhdrs();
@@ -250,13 +250,11 @@ template <class ELFT> void Writer<ELFT>::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 <class ELFT> void Writer<ELFT>::finalizeSections() {
for (OutputSection *Sec : OutputSections)
Sec->finalize<ELFT>();
+ // 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<ELFT>(); });
+
// createThunks may have added local symbols to the static symbol table
applySynthetic({In<ELFT>::SymTab, In<ELFT>::ShStrTab, In<ELFT>::StrTab},
[](SyntheticSection *SS) { SS->postThunkContents(); });
@@ -1502,37 +1506,6 @@ template <class ELFT> void Writer<ELFT>::fixHeaders() {
AllocateHeader = allocateHeaders(Phdrs, OutputSections, Min);
}
-// Assign VAs (addresses at run-time) to output sections.
-template <class ELFT> void Writer<ELFT>::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<uint32_t>(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