186 changes: 145 additions & 41 deletions lld/COFF/Chunks.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "InputFiles.h"
#include "lld/Common/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/MC/StringTableBuilder.h"
Expand Down Expand Up @@ -55,14 +56,16 @@ class Chunk {
public:
enum Kind : uint8_t { SectionKind, OtherKind };
Kind kind() const { return ChunkKind; }
virtual ~Chunk() = default;

// Returns the size of this chunk (even if this is a common or BSS.)
virtual size_t getSize() const = 0;
size_t getSize() const;

// Returns chunk alignment in power of two form. Value values are powers of
// two from 1 to 8192.
uint32_t getAlignment() const { return 1U << P2Align; }

// Update the chunk section alignment measured in bytes. Internally alignment
// is stored in log2.
void setAlignment(uint32_t Align) {
// Treat zero byte alignment as 1 byte alignment.
Align = Align ? Align : 1;
Expand All @@ -76,7 +79,7 @@ class Chunk {
// beginning of the file. Because this function may use RVA values
// of other chunks for relocations, you need to set them properly
// before calling this function.
virtual void writeTo(uint8_t *Buf) const {}
void writeTo(uint8_t *Buf) const;

// The writer sets and uses the addresses. In practice, PE images cannot be
// larger than 2GB. Chunks are always laid as part of the image, so Chunk RVAs
Expand All @@ -90,16 +93,14 @@ class Chunk {
// Returns true if this has non-zero data. BSS chunks return
// false. If false is returned, the space occupied by this chunk
// will be filled with zeros.
virtual bool hasData() const { return true; }
bool hasData() const { return HasData; }

// Returns readable/writable/executable bits.
virtual uint32_t getOutputCharacteristics() const { return 0; }
uint32_t getOutputCharacteristics() const;

// Returns the section name if this is a section chunk.
// It is illegal to call this function on non-section chunks.
virtual StringRef getSectionName() const {
llvm_unreachable("unimplemented getSectionName");
}
StringRef getSectionName() const;

// An output section has pointers to chunks in the section, and each
// chunk has a back pointer to an output section.
Expand All @@ -109,22 +110,29 @@ class Chunk {

// Windows-specific.
// Collect all locations that contain absolute addresses for base relocations.
virtual void getBaserels(std::vector<Baserel> *Res) {}
void getBaserels(std::vector<Baserel> *Res);

// Returns a human-readable name of this chunk. Chunks are unnamed chunks of
// bytes, so this is used only for logging or debugging.
virtual StringRef getDebugName() { return ""; }
StringRef getDebugName() const;

virtual bool isHotPatchable() const { return false; }
// Return true if this file has the hotpatch flag set to true in the
// S_COMPILE3 record in codeview debug info. Also returns true for some thunks
// synthesized by the linker.
bool isHotPatchable() const;

protected:
Chunk(Kind K = OtherKind) : ChunkKind(K) {}
Chunk(Kind K = OtherKind) : ChunkKind(K), HasData(true), P2Align(0) {}

const Kind ChunkKind;

// True if the section has data. Corresponds to the
// IMAGE_SCN_CNT_UNINITIALIZED_DATA section characteristic bit.
uint8_t HasData : 1;

// The alignment of this chunk, stored in log2 form. The writer uses the
// value.
uint8_t P2Align = 0;
uint8_t P2Align : 7;

// The output section index for this chunk. The first valid section number is
// one.
Expand All @@ -134,6 +142,46 @@ class Chunk {
uint32_t RVA = 0;
};

class NonSectionChunk : public Chunk {
public:
virtual ~NonSectionChunk() = default;

// Returns the size of this chunk (even if this is a common or BSS.)
virtual size_t getSize() const = 0;

virtual uint32_t getOutputCharacteristics() const { return 0; }

// Write this chunk to a mmap'ed file, assuming Buf is pointing to
// beginning of the file. Because this function may use RVA values
// of other chunks for relocations, you need to set them properly
// before calling this function.
virtual void writeTo(uint8_t *Buf) const {}

// Returns the section name if this is a section chunk.
// It is illegal to call this function on non-section chunks.
virtual StringRef getSectionName() const {
llvm_unreachable("unimplemented getSectionName");
}

// Windows-specific.
// Collect all locations that contain absolute addresses for base relocations.
virtual void getBaserels(std::vector<Baserel> *Res) {}

// Return true if this file has the hotpatch flag set to true in the
// S_COMPILE3 record in codeview debug info. Also returns true for some thunks
// synthesized by the linker.
virtual bool isHotPatchable() const { return false; }

// Returns a human-readable name of this chunk. Chunks are unnamed chunks of
// bytes, so this is used only for logging or debugging.
virtual StringRef getDebugName() const { return ""; }

static bool classof(const Chunk *C) { return C->kind() == OtherKind; }

protected:
NonSectionChunk() : Chunk(OtherKind) {}
};

// A chunk corresponding a section of an input file.
class SectionChunk final : public Chunk {
// Identical COMDAT Folding feature accesses section internal data.
Expand All @@ -158,15 +206,17 @@ class SectionChunk final : public Chunk {

SectionChunk(ObjFile *File, const coff_section *Header);
static bool classof(const Chunk *C) { return C->kind() == SectionKind; }
size_t getSize() const override { return Header->SizeOfRawData; }
size_t getSize() const { return Header->SizeOfRawData; }
ArrayRef<uint8_t> getContents() const;
void writeTo(uint8_t *Buf) const override;
bool hasData() const override;
uint32_t getOutputCharacteristics() const override;
StringRef getSectionName() const override {
void writeTo(uint8_t *Buf) const;

uint32_t getOutputCharacteristics() const {
return Header->Characteristics & (PermMask | TypeMask);
}
StringRef getSectionName() const {
return StringRef(SectionNameData, SectionNameSize);
}
void getBaserels(std::vector<Baserel> *Res) override;
void getBaserels(std::vector<Baserel> *Res);
bool isCOMDAT() const;
void applyRelX64(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S,
uint64_t P) const;
Expand All @@ -187,7 +237,7 @@ class SectionChunk final : public Chunk {
// and its children are treated as a group by the garbage collector.
void addAssociative(SectionChunk *Child);

StringRef getDebugName() override;
StringRef getDebugName() const;

// True if this is a codeview debug info chunk. These will not be laid out in
// the image. Instead they will end up in the PDB, if one is requested.
Expand All @@ -200,6 +250,8 @@ class SectionChunk final : public Chunk {
return getSectionName().startswith(".debug_") || getSectionName() == ".eh_frame";
}

bool isHotPatchable() const { return File->HotPatchable; }

// Allow iteration over the bodies of this chunk's relocated symbols.
llvm::iterator_range<symbol_iterator> symbols() const {
return llvm::make_range(symbol_iterator(File, RelocsData),
Expand Down Expand Up @@ -257,8 +309,6 @@ class SectionChunk final : public Chunk {
static SectionChunk *findByName(ArrayRef<SectionChunk *> Sections,
StringRef Name);

bool isHotPatchable() const override { return File->HotPatchable; }

// The file that this chunk was created from.
ObjFile *File;

Expand Down Expand Up @@ -305,6 +355,58 @@ class SectionChunk final : public Chunk {
uint32_t SectionNameSize = 0;
};

// Inline methods to implement faux-virtual dispatch for SectionChunk.

inline size_t Chunk::getSize() const {
if (isa<SectionChunk>(this))
return static_cast<const SectionChunk *>(this)->getSize();
else
return static_cast<const NonSectionChunk *>(this)->getSize();
}

inline uint32_t Chunk::getOutputCharacteristics() const {
if (isa<SectionChunk>(this))
return static_cast<const SectionChunk *>(this)->getOutputCharacteristics();
else
return static_cast<const NonSectionChunk *>(this)
->getOutputCharacteristics();
}

inline void Chunk::writeTo(uint8_t *Buf) const {
if (isa<SectionChunk>(this))
static_cast<const SectionChunk *>(this)->writeTo(Buf);
else
static_cast<const NonSectionChunk *>(this)->writeTo(Buf);
}

inline bool Chunk::isHotPatchable() const {
if (isa<SectionChunk>(this))
return static_cast<const SectionChunk *>(this)->isHotPatchable();
else
return static_cast<const NonSectionChunk *>(this)->isHotPatchable();
}

inline StringRef Chunk::getSectionName() const {
if (isa<SectionChunk>(this))
return static_cast<const SectionChunk *>(this)->getSectionName();
else
return static_cast<const NonSectionChunk *>(this)->getSectionName();
}

inline void Chunk::getBaserels(std::vector<Baserel> *Res) {
if (isa<SectionChunk>(this))
static_cast<SectionChunk *>(this)->getBaserels(Res);
else
static_cast<NonSectionChunk *>(this)->getBaserels(Res);
}

inline StringRef Chunk::getDebugName() const {
if (isa<SectionChunk>(this))
return static_cast<const SectionChunk *>(this)->getDebugName();
else
return static_cast<const NonSectionChunk *>(this)->getDebugName();
}

// This class is used to implement an lld-specific feature (not implemented in
// MSVC) that minimizes the output size by finding string literals sharing tail
// parts and merging them.
Expand All @@ -314,7 +416,7 @@ class SectionChunk final : public Chunk {
// The MergeChunk then tail merges the strings using the StringTableBuilder
// class and assigns RVAs and section offsets to each of the member chunks based
// on the offsets assigned by the StringTableBuilder.
class MergeChunk : public Chunk {
class MergeChunk : public NonSectionChunk {
public:
MergeChunk(uint32_t Alignment);
static void addSection(SectionChunk *C);
Expand All @@ -335,11 +437,10 @@ class MergeChunk : public Chunk {
};

// A chunk for common symbols. Common chunks don't have actual data.
class CommonChunk : public Chunk {
class CommonChunk : public NonSectionChunk {
public:
CommonChunk(const COFFSymbolRef Sym);
size_t getSize() const override { return Sym.getValue(); }
bool hasData() const override { return false; }
uint32_t getOutputCharacteristics() const override;
StringRef getSectionName() const override { return ".bss"; }

Expand All @@ -348,7 +449,7 @@ class CommonChunk : public Chunk {
};

// A chunk for linker-created strings.
class StringChunk : public Chunk {
class StringChunk : public NonSectionChunk {
public:
explicit StringChunk(StringRef S) : Str(S) {}
size_t getSize() const override { return Str.size() + 1; }
Expand Down Expand Up @@ -377,7 +478,7 @@ static const uint8_t ImportThunkARM64[] = {
// Windows-specific.
// A chunk for DLL import jump table entry. In a final output, its
// contents will be a JMP instruction to some __imp_ symbol.
class ImportThunkChunkX64 : public Chunk {
class ImportThunkChunkX64 : public NonSectionChunk {
public:
explicit ImportThunkChunkX64(Defined *S);
size_t getSize() const override { return sizeof(ImportThunkX86); }
Expand All @@ -389,9 +490,10 @@ class ImportThunkChunkX64 : public Chunk {
Defined *ImpSymbol;
};

class ImportThunkChunkX86 : public Chunk {
class ImportThunkChunkX86 : public NonSectionChunk {
public:
explicit ImportThunkChunkX86(Defined *S) : ImpSymbol(S) {}
explicit ImportThunkChunkX86(Defined *S) : ImpSymbol(S) {
}
size_t getSize() const override { return sizeof(ImportThunkX86); }
void getBaserels(std::vector<Baserel> *Res) override;
void writeTo(uint8_t *Buf) const override;
Expand All @@ -402,9 +504,10 @@ class ImportThunkChunkX86 : public Chunk {
Defined *ImpSymbol;
};

class ImportThunkChunkARM : public Chunk {
class ImportThunkChunkARM : public NonSectionChunk {
public:
explicit ImportThunkChunkARM(Defined *S) : ImpSymbol(S) {}
explicit ImportThunkChunkARM(Defined *S) : ImpSymbol(S) {
}
size_t getSize() const override { return sizeof(ImportThunkARM); }
void getBaserels(std::vector<Baserel> *Res) override;
void writeTo(uint8_t *Buf) const override;
Expand All @@ -415,9 +518,10 @@ class ImportThunkChunkARM : public Chunk {
Defined *ImpSymbol;
};

class ImportThunkChunkARM64 : public Chunk {
class ImportThunkChunkARM64 : public NonSectionChunk {
public:
explicit ImportThunkChunkARM64(Defined *S) : ImpSymbol(S) {}
explicit ImportThunkChunkARM64(Defined *S) : ImpSymbol(S) {
}
size_t getSize() const override { return sizeof(ImportThunkARM64); }
void writeTo(uint8_t *Buf) const override;

Expand All @@ -427,7 +531,7 @@ class ImportThunkChunkARM64 : public Chunk {
Defined *ImpSymbol;
};

class RangeExtensionThunkARM : public Chunk {
class RangeExtensionThunkARM : public NonSectionChunk {
public:
explicit RangeExtensionThunkARM(Defined *T) : Target(T) {}
size_t getSize() const override;
Expand All @@ -436,7 +540,7 @@ class RangeExtensionThunkARM : public Chunk {
Defined *Target;
};

class RangeExtensionThunkARM64 : public Chunk {
class RangeExtensionThunkARM64 : public NonSectionChunk {
public:
explicit RangeExtensionThunkARM64(Defined *T) : Target(T) {}
size_t getSize() const override;
Expand All @@ -447,7 +551,7 @@ class RangeExtensionThunkARM64 : public Chunk {

// Windows-specific.
// See comments for DefinedLocalImport class.
class LocalImportChunk : public Chunk {
class LocalImportChunk : public NonSectionChunk {
public:
explicit LocalImportChunk(Defined *S) : Sym(S) {
setAlignment(Config->Wordsize);
Expand Down Expand Up @@ -487,7 +591,7 @@ struct ChunkAndOffset {
using SymbolRVASet = llvm::DenseSet<ChunkAndOffset>;

// Table which contains symbol RVAs. Used for /safeseh and /guard:cf.
class RVATableChunk : public Chunk {
class RVATableChunk : public NonSectionChunk {
public:
explicit RVATableChunk(SymbolRVASet S) : Syms(std::move(S)) {}
size_t getSize() const override { return Syms.size() * 4; }
Expand All @@ -500,7 +604,7 @@ class RVATableChunk : public Chunk {
// Windows-specific.
// This class represents a block in .reloc section.
// See the PE/COFF spec 5.6 for details.
class BaserelChunk : public Chunk {
class BaserelChunk : public NonSectionChunk {
public:
BaserelChunk(uint32_t Page, Baserel *Begin, Baserel *End);
size_t getSize() const override { return Data.size(); }
Expand All @@ -524,7 +628,7 @@ class Baserel {
// specific place in a section, without any data. This is used for the MinGW
// specific symbol __RUNTIME_PSEUDO_RELOC_LIST_END__, even though the concept
// of an empty chunk isn't MinGW specific.
class EmptyChunk : public Chunk {
class EmptyChunk : public NonSectionChunk {
public:
EmptyChunk() {}
size_t getSize() const override { return 0; }
Expand All @@ -537,7 +641,7 @@ class EmptyChunk : public Chunk {
// the reference didn't use the dllimport attribute. The MinGW runtime will
// process this table after loading, before handling control over to user
// code.
class PseudoRelocTableChunk : public Chunk {
class PseudoRelocTableChunk : public NonSectionChunk {
public:
PseudoRelocTableChunk(std::vector<RuntimePseudoReloc> &Relocs)
: Relocs(std::move(Relocs)) {
Expand Down Expand Up @@ -568,7 +672,7 @@ class RuntimePseudoReloc {
};

// MinGW specific. A Chunk that contains one pointer-sized absolute value.
class AbsolutePointerChunk : public Chunk {
class AbsolutePointerChunk : public NonSectionChunk {
public:
AbsolutePointerChunk(uint64_t Value) : Value(Value) {
setAlignment(getSize());
Expand Down
33 changes: 16 additions & 17 deletions lld/COFF/DLL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ namespace {
// Import table

// A chunk for the import descriptor table.
class HintNameChunk : public Chunk {
class HintNameChunk : public NonSectionChunk {
public:
HintNameChunk(StringRef N, uint16_t H) : Name(N), Hint(H) {}

Expand All @@ -57,7 +57,7 @@ class HintNameChunk : public Chunk {
};

// A chunk for the import descriptor table.
class LookupChunk : public Chunk {
class LookupChunk : public NonSectionChunk {
public:
explicit LookupChunk(Chunk *C) : HintName(C) {
setAlignment(Config->Wordsize);
Expand All @@ -77,7 +77,7 @@ class LookupChunk : public Chunk {
// A chunk for the import descriptor table.
// This chunk represent import-by-ordinal symbols.
// See Microsoft PE/COFF spec 7.1. Import Header for details.
class OrdinalOnlyChunk : public Chunk {
class OrdinalOnlyChunk : public NonSectionChunk {
public:
explicit OrdinalOnlyChunk(uint16_t V) : Ordinal(V) {
setAlignment(Config->Wordsize);
Expand All @@ -98,7 +98,7 @@ class OrdinalOnlyChunk : public Chunk {
};

// A chunk for the import descriptor table.
class ImportDirectoryChunk : public Chunk {
class ImportDirectoryChunk : public NonSectionChunk {
public:
explicit ImportDirectoryChunk(Chunk *N) : DLLName(N) {}
size_t getSize() const override { return sizeof(ImportDirectoryTableEntry); }
Expand All @@ -119,10 +119,9 @@ class ImportDirectoryChunk : public Chunk {

// A chunk representing null terminator in the import table.
// Contents of this chunk is always null bytes.
class NullChunk : public Chunk {
class NullChunk : public NonSectionChunk {
public:
explicit NullChunk(size_t N) : Size(N) {}
bool hasData() const override { return false; }
explicit NullChunk(size_t N) : Size(N) { HasData = false; }
size_t getSize() const override { return Size; }

void writeTo(uint8_t *Buf) const override {
Expand Down Expand Up @@ -162,7 +161,7 @@ binImports(const std::vector<DefinedImportData *> &Imports) {
// See Microsoft PE/COFF spec 4.3 for details.

// A chunk for the delay import descriptor table etnry.
class DelayDirectoryChunk : public Chunk {
class DelayDirectoryChunk : public NonSectionChunk {
public:
explicit DelayDirectoryChunk(Chunk *N) : DLLName(N) {}

Expand Down Expand Up @@ -274,7 +273,7 @@ static const uint8_t ThunkARM64[] = {
};

// A chunk for the delay import thunk.
class ThunkChunkX64 : public Chunk {
class ThunkChunkX64 : public NonSectionChunk {
public:
ThunkChunkX64(Defined *I, Chunk *D, Defined *H)
: Imp(I), Desc(D), Helper(H) {}
Expand All @@ -293,7 +292,7 @@ class ThunkChunkX64 : public Chunk {
Defined *Helper = nullptr;
};

class ThunkChunkX86 : public Chunk {
class ThunkChunkX86 : public NonSectionChunk {
public:
ThunkChunkX86(Defined *I, Chunk *D, Defined *H)
: Imp(I), Desc(D), Helper(H) {}
Expand All @@ -317,7 +316,7 @@ class ThunkChunkX86 : public Chunk {
Defined *Helper = nullptr;
};

class ThunkChunkARM : public Chunk {
class ThunkChunkARM : public NonSectionChunk {
public:
ThunkChunkARM(Defined *I, Chunk *D, Defined *H)
: Imp(I), Desc(D), Helper(H) {}
Expand All @@ -341,7 +340,7 @@ class ThunkChunkARM : public Chunk {
Defined *Helper = nullptr;
};

class ThunkChunkARM64 : public Chunk {
class ThunkChunkARM64 : public NonSectionChunk {
public:
ThunkChunkARM64(Defined *I, Chunk *D, Defined *H)
: Imp(I), Desc(D), Helper(H) {}
Expand All @@ -363,7 +362,7 @@ class ThunkChunkARM64 : public Chunk {
};

// A chunk for the import descriptor table.
class DelayAddressChunk : public Chunk {
class DelayAddressChunk : public NonSectionChunk {
public:
explicit DelayAddressChunk(Chunk *C) : Thunk(C) {
setAlignment(Config->Wordsize);
Expand Down Expand Up @@ -393,7 +392,7 @@ class DelayAddressChunk : public Chunk {
// Read Microsoft PE/COFF spec 5.3 for details.

// A chunk for the export descriptor table.
class ExportDirectoryChunk : public Chunk {
class ExportDirectoryChunk : public NonSectionChunk {
public:
ExportDirectoryChunk(int I, int J, Chunk *D, Chunk *A, Chunk *N, Chunk *O)
: MaxOrdinal(I), NameTabSize(J), DLLName(D), AddressTab(A), NameTab(N),
Expand Down Expand Up @@ -424,7 +423,7 @@ class ExportDirectoryChunk : public Chunk {
Chunk *OrdinalTab;
};

class AddressTableChunk : public Chunk {
class AddressTableChunk : public NonSectionChunk {
public:
explicit AddressTableChunk(size_t MaxOrdinal) : Size(MaxOrdinal + 1) {}
size_t getSize() const override { return Size * 4; }
Expand All @@ -450,7 +449,7 @@ class AddressTableChunk : public Chunk {
size_t Size;
};

class NamePointersChunk : public Chunk {
class NamePointersChunk : public NonSectionChunk {
public:
explicit NamePointersChunk(std::vector<Chunk *> &V) : Chunks(V) {}
size_t getSize() const override { return Chunks.size() * 4; }
Expand All @@ -466,7 +465,7 @@ class NamePointersChunk : public Chunk {
std::vector<Chunk *> Chunks;
};

class ExportOrdinalChunk : public Chunk {
class ExportOrdinalChunk : public NonSectionChunk {
public:
explicit ExportOrdinalChunk(size_t I) : Size(I) {}
size_t getSize() const override { return Size * 2; }
Expand Down
4 changes: 2 additions & 2 deletions lld/COFF/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ OutputSection *Chunk::getOutputSection() const {

namespace {

class DebugDirectoryChunk : public Chunk {
class DebugDirectoryChunk : public NonSectionChunk {
public:
DebugDirectoryChunk(const std::vector<Chunk *> &R, bool WriteRepro)
: Records(R), WriteRepro(WriteRepro) {}
Expand Down Expand Up @@ -143,7 +143,7 @@ class DebugDirectoryChunk : public Chunk {
bool WriteRepro;
};

class CVDebugRecordChunk : public Chunk {
class CVDebugRecordChunk : public NonSectionChunk {
public:
size_t getSize() const override {
return sizeof(codeview::DebugInfo) + Config->PDBAltPath.size() + 1;
Expand Down