Commit 4ea16835 authored by Dimitry Andric's avatar Dimitry Andric
Browse files

Vendor import of lld trunk r306325:

https://llvm.org/svn/llvm-project/lld/trunk@306325
parent 15f7a1a3
......@@ -37,8 +37,14 @@ SectionChunk::SectionChunk(ObjectFile *F, const coff_section *H)
Align = Header->getAlignment();
// Only COMDAT sections are subject of dead-stripping.
Live = !isCOMDAT();
// Chunks may be discarded during comdat merging.
Discarded = false;
// If linker GC is disabled, every chunk starts out alive. If linker GC is
// enabled, treat non-comdat sections as roots. Generally optimized object
// files will be built with -ffunction-sections or /Gy, so most things worth
// stripping will be in a comdat.
Live = !Config->DoGC || !isCOMDAT();
}
static void add16(uint8_t *P, int16_t V) { write16le(P, read16le(P) + V); }
......@@ -46,6 +52,15 @@ static void add32(uint8_t *P, int32_t V) { write32le(P, read32le(P) + V); }
static void add64(uint8_t *P, int64_t V) { write64le(P, read64le(P) + V); }
static void or16(uint8_t *P, uint16_t V) { write16le(P, read16le(P) | V); }
static void applySecRel(const SectionChunk *Sec, uint8_t *Off, Defined *Sym) {
// Don't apply section relative relocations to absolute symbols in codeview
// debug info sections. MSVC does not treat such relocations as fatal errors,
// and they can be found in the standard library for linker-provided symbols
// like __guard_fids_table and __safe_se_handler_table.
if (!(isa<DefinedAbsolute>(Sym) && Sec->isCodeView()))
add32(Off, Sym->getSecrel());
}
void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, Defined *Sym,
uint64_t P) const {
uint64_t S = Sym->getRVA();
......@@ -60,7 +75,7 @@ void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, Defined *Sym,
case IMAGE_REL_AMD64_REL32_4: add32(Off, S - P - 8); break;
case IMAGE_REL_AMD64_REL32_5: add32(Off, S - P - 9); break;
case IMAGE_REL_AMD64_SECTION: add16(Off, Sym->getSectionIndex()); break;
case IMAGE_REL_AMD64_SECREL: add32(Off, Sym->getSecrel()); break;
case IMAGE_REL_AMD64_SECREL: applySecRel(this, Off, Sym); break;
default:
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
}
......@@ -75,7 +90,7 @@ void SectionChunk::applyRelX86(uint8_t *Off, uint16_t Type, Defined *Sym,
case IMAGE_REL_I386_DIR32NB: add32(Off, S); break;
case IMAGE_REL_I386_REL32: add32(Off, S - P - 4); break;
case IMAGE_REL_I386_SECTION: add16(Off, Sym->getSectionIndex()); break;
case IMAGE_REL_I386_SECREL: add32(Off, Sym->getSecrel()); break;
case IMAGE_REL_I386_SECREL: applySecRel(this, Off, Sym); break;
default:
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
}
......@@ -135,7 +150,7 @@ void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, Defined *Sym,
case IMAGE_REL_ARM_BRANCH20T: applyBranch20T(Off, S - P - 4); break;
case IMAGE_REL_ARM_BRANCH24T: applyBranch24T(Off, S - P - 4); break;
case IMAGE_REL_ARM_BLX23T: applyBranch24T(Off, S - P - 4); break;
case IMAGE_REL_ARM_SECREL: add32(Off, Sym->getSecrel()); break;
case IMAGE_REL_ARM_SECREL: applySecRel(this, Off, Sym); break;
default:
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
}
......@@ -226,8 +241,12 @@ bool SectionChunk::isCOMDAT() const {
void SectionChunk::printDiscardedMessage() const {
// Removed by dead-stripping. If it's removed by ICF, ICF already
// printed out the name, so don't repeat that here.
if (Sym && this == Repl)
message("Discarded " + Sym->getName());
if (Sym && this == Repl) {
if (Discarded)
message("Discarded comdat symbol " + Sym->getName());
else if (!Live)
message("Discarded " + Sym->getName());
}
}
StringRef SectionChunk::getDebugName() {
......
......@@ -64,7 +64,6 @@ class Chunk {
uint64_t getRVA() const { return RVA; }
uint32_t getAlign() const { return Align; }
void setRVA(uint64_t V) { RVA = V; }
void setOutputSectionOff(uint64_t V) { OutputSectionOff = V; }
// Returns true if this has non-zero data. BSS chunks return
// false. If false is returned, the space occupied by this chunk
......@@ -97,17 +96,19 @@ class Chunk {
Chunk(Kind K = OtherKind) : ChunkKind(K) {}
const Kind ChunkKind;
// The alignment of this chunk. The writer uses the value.
uint32_t Align = 1;
// The RVA of this chunk in the output. The writer sets a value.
uint64_t RVA = 0;
public:
// The offset from beginning of the output section. The writer sets a value.
uint64_t OutputSectionOff = 0;
protected:
// The output section for this chunk.
OutputSection *Out = nullptr;
// The alignment of this chunk. The writer uses the value.
uint32_t Align = 1;
};
// A chunk corresponding a section of an input file.
......@@ -159,13 +160,29 @@ class SectionChunk : public Chunk {
StringRef getDebugName() override;
void setSymbol(DefinedRegular *S) { if (!Sym) Sym = S; }
// Returns true if the chunk was not dropped by GC or COMDAT deduplication.
bool isLive() { return Live && !Discarded; }
// Used by the garbage collector.
bool isLive() { return !Config->DoGC || Live; }
void markLive() {
assert(Config->DoGC && "should only mark things live from GC");
assert(!isLive() && "Cannot mark an already live section!");
Live = true;
}
// Returns true if this chunk was dropped by COMDAT deduplication.
bool isDiscarded() const { return Discarded; }
// Used by the SymbolTable when discarding unused comdat sections. This is
// redundant when GC is enabled, as all comdat sections will start out dead.
void markDiscarded() { Discarded = true; }
// 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.
bool isCodeView() const {
return SectionName == ".debug" || SectionName.startswith(".debug$");
}
// 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, Relocs.begin()),
......@@ -196,6 +213,9 @@ class SectionChunk : public Chunk {
llvm::iterator_range<const coff_relocation *> Relocs;
size_t NumRelocs;
// True if this chunk was discarded because it was a duplicate comdat section.
bool Discarded;
// Used by the garbage collector.
bool Live;
......
......@@ -161,9 +161,6 @@ struct Configuration {
bool LargeAddressAware = false;
bool HighEntropyVA = false;
bool AppContainer = false;
// This is for debugging.
bool DumpPdb = false;
};
extern Configuration *Config;
......
......@@ -905,7 +905,6 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
Config->TerminalServerAware = false;
if (Args.hasArg(OPT_nosymtab))
Config->WriteSymtab = false;
Config->DumpPdb = Args.hasArg(OPT_dumppdb);
Config->MapFile = getMapFile(Args);
......@@ -936,9 +935,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
Config->Machine = AMD64;
}
// Windows specific -- Input files can be Windows resource files (.res files).
// We invoke cvtres.exe to convert resource files to a regular COFF file
// then link the result file normally.
// Input files can be Windows resource files (.res files). We use
// WindowsResource to convert resource files to a regular COFF file,
// then link the resulting file normally.
if (!Resources.empty())
addBuffer(convertResToCOFF(Resources));
......@@ -1027,17 +1026,21 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
if (Config->ImageBase == uint64_t(-1))
Config->ImageBase = getDefaultImageBase();
Symtab.addRelative(mangle("__ImageBase"), 0);
Symtab.addSynthetic(mangle("__ImageBase"), nullptr);
if (Config->Machine == I386) {
Config->SEHTable = Symtab.addRelative("___safe_se_handler_table", 0);
Config->SEHCount = Symtab.addAbsolute("___safe_se_handler_count", 0);
Symtab.addAbsolute("___safe_se_handler_table", 0);
Symtab.addAbsolute("___safe_se_handler_count", 0);
}
// We do not support /guard:cf (control flow protection) yet.
// Define CFG symbols anyway so that we can link MSVC 2015 CRT.
Symtab.addAbsolute(mangle("__guard_fids_table"), 0);
Symtab.addAbsolute(mangle("__guard_fids_count"), 0);
Symtab.addAbsolute(mangle("__guard_fids_table"), 0);
Symtab.addAbsolute(mangle("__guard_flags"), 0x100);
Symtab.addAbsolute(mangle("__guard_iat_count"), 0);
Symtab.addAbsolute(mangle("__guard_iat_table"), 0);
Symtab.addAbsolute(mangle("__guard_longjmp_count"), 0);
Symtab.addAbsolute(mangle("__guard_longjmp_table"), 0);
// This code may add new undefined symbols to the link, which may enqueue more
// symbol resolution tasks, so we need to continue executing tasks until we
......
......@@ -178,7 +178,7 @@ void runMSVCLinker(std::string Rsp, ArrayRef<StringRef> Objects);
// Create enum with OPT_xxx values for each option in Options.td
enum {
OPT_INVALID = 0,
#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11) OPT_##ID,
#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID,
#include "Options.inc"
#undef OPTION
};
......
......@@ -20,12 +20,15 @@
#include "Symbols.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/BinaryFormat/COFF.h"
#include "llvm/Object/COFF.h"
#include "llvm/Object/WindowsResource.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/raw_ostream.h"
......@@ -41,6 +44,9 @@ namespace lld {
namespace coff {
namespace {
const uint16_t SUBLANG_ENGLISH_US = 0x0409;
const uint16_t RT_MANIFEST = 24;
class Executor {
public:
explicit Executor(StringRef S) : Prog(Saver.save(S)) {}
......@@ -257,26 +263,6 @@ void parseManifestUAC(StringRef Arg) {
}
}
// Quote each line with "". Existing double-quote is converted
// to two double-quotes.
static void quoteAndPrint(raw_ostream &Out, StringRef S) {
while (!S.empty()) {
StringRef Line;
std::tie(Line, S) = S.split("\n");
if (Line.empty())
continue;
Out << '\"';
for (int I = 0, E = Line.size(); I != E; ++I) {
if (Line[I] == '\"') {
Out << "\"\"";
} else {
Out << Line[I];
}
}
Out << "\"\n";
}
}
// An RAII temporary file class that automatically removes a temporary file.
namespace {
class TemporaryFile {
......@@ -390,38 +376,64 @@ static std::string createManifestXml() {
return readFile(File2.Path);
}
static std::unique_ptr<MemoryBuffer>
createMemoryBufferForManifestRes(size_t ManifestSize) {
size_t ResSize = alignTo(object::WIN_RES_MAGIC_SIZE +
object::WIN_RES_NULL_ENTRY_SIZE +
sizeof(object::WinResHeaderPrefix) +
sizeof(object::WinResIDs) +
sizeof(object::WinResHeaderSuffix) +
ManifestSize,
object::WIN_RES_DATA_ALIGNMENT);
return MemoryBuffer::getNewMemBuffer(ResSize);
}
static void writeResFileHeader(char *&Buf) {
memcpy(Buf, COFF::WinResMagic, sizeof(COFF::WinResMagic));
Buf += sizeof(COFF::WinResMagic);
memset(Buf, 0, object::WIN_RES_NULL_ENTRY_SIZE);
Buf += object::WIN_RES_NULL_ENTRY_SIZE;
}
static void writeResEntryHeader(char *&Buf, size_t ManifestSize) {
// Write the prefix.
auto *Prefix = reinterpret_cast<object::WinResHeaderPrefix *>(Buf);
Prefix->DataSize = ManifestSize;
Prefix->HeaderSize = sizeof(object::WinResHeaderPrefix) +
sizeof(object::WinResIDs) +
sizeof(object::WinResHeaderSuffix);
Buf += sizeof(object::WinResHeaderPrefix);
// Write the Type/Name IDs.
auto *IDs = reinterpret_cast<object::WinResIDs *>(Buf);
IDs->setType(RT_MANIFEST);
IDs->setName(Config->ManifestID);
Buf += sizeof(object::WinResIDs);
// Write the suffix.
auto *Suffix = reinterpret_cast<object::WinResHeaderSuffix *>(Buf);
Suffix->DataVersion = 0;
Suffix->MemoryFlags = object::WIN_RES_PURE_MOVEABLE;
Suffix->Language = SUBLANG_ENGLISH_US;
Suffix->Version = 0;
Suffix->Characteristics = 0;
Buf += sizeof(object::WinResHeaderSuffix);
}
// Create a resource file containing a manifest XML.
std::unique_ptr<MemoryBuffer> createManifestRes() {
// Create a temporary file for the resource script file.
TemporaryFile RCFile("manifest", "rc");
std::string Manifest = createManifestXml();
// Open the temporary file for writing.
std::error_code EC;
raw_fd_ostream Out(RCFile.Path, EC, sys::fs::F_Text);
if (EC)
fatal(EC, "failed to open " + RCFile.Path);
// Write resource script to the RC file.
Out << "#define LANG_ENGLISH 9\n"
<< "#define SUBLANG_DEFAULT 1\n"
<< "#define APP_MANIFEST " << Config->ManifestID << "\n"
<< "#define RT_MANIFEST 24\n"
<< "LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT\n"
<< "APP_MANIFEST RT_MANIFEST {\n";
quoteAndPrint(Out, createManifestXml());
Out << "}\n";
Out.close();
// Create output resource file.
TemporaryFile ResFile("output-resource", "res");
Executor E("rc.exe");
E.add("/fo");
E.add(ResFile.Path);
E.add("/nologo");
E.add(RCFile.Path);
E.run();
return ResFile.getMemoryBuffer();
std::unique_ptr<MemoryBuffer> Res =
createMemoryBufferForManifestRes(Manifest.size());
char *Buf = const_cast<char *>(Res->getBufferStart());
writeResFileHeader(Buf);
writeResEntryHeader(Buf, Manifest.size());
// Copy the manifest data into the .res file.
std::copy(Manifest.begin(), Manifest.end(), Buf);
return Res;
}
void createSideBySideManifest() {
......@@ -592,40 +604,22 @@ void checkFailIfMismatch(StringRef Arg) {
// using cvtres.exe.
std::unique_ptr<MemoryBuffer>
convertResToCOFF(const std::vector<MemoryBufferRef> &MBs) {
// Create an output file path.
TemporaryFile File("resource-file", "obj");
object::WindowsResourceParser Parser;
// Execute cvtres.exe.
Executor E("cvtres.exe");
E.add("/machine:" + machineToStr(Config->Machine));
E.add("/readonly");
E.add("/nologo");
E.add("/out:" + Twine(File.Path));
// We must create new files because the memory buffers we have may have no
// underlying file still existing on the disk.
// It happens if it was created from a TemporaryFile, which usually delete
// the file just after creating the MemoryBuffer.
std::vector<TemporaryFile> ResFiles;
ResFiles.reserve(MBs.size());
for (MemoryBufferRef MB : MBs) {
// We store the temporary file in a vector to avoid deletion
// before running cvtres
ResFiles.emplace_back("resource-file", "res");
TemporaryFile& ResFile = ResFiles.back();
// Write the content of the resource in a temporary file
std::error_code EC;
raw_fd_ostream OS(ResFile.Path, EC, sys::fs::F_None);
if (EC)
fatal(EC, "failed to open " + ResFile.Path);
OS << MB.getBuffer();
OS.close();
E.add(ResFile.Path);
std::unique_ptr<object::Binary> Bin = check(object::createBinary(MB));
object::WindowsResource *RF = dyn_cast<object::WindowsResource>(Bin.get());
if (!RF)
fatal("cannot compile non-resource file as resource");
if (auto EC = Parser.parse(RF))
fatal(EC, "failed to parse .res file");
}
E.run();
return File.getMemoryBuffer();
Expected<std::unique_ptr<MemoryBuffer>> E =
llvm::object::writeWindowsResourceCOFF(Config->Machine, Parser);
if (!E)
fatal(errorToErrorCode(E.takeError()), "failed to write .res to COFF");
return std::move(E.get());
}
// Run MSVC link.exe for given in-memory object files.
......@@ -657,11 +651,9 @@ void runMSVCLinker(std::string Rsp, ArrayRef<StringRef> Objects) {
// Create table mapping all options defined in Options.td
static const llvm::opt::OptTable::Info infoTable[] = {
#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X6, X7, X8, X9, X10) \
{ \
X1, X2, X9, X10, OPT_##ID, llvm::opt::Option::KIND##Class, X8, X7, \
OPT_##GROUP, OPT_##ALIAS, X6 \
},
#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \
{X1, X2, X10, X11, OPT_##ID, llvm::opt::Option::KIND##Class, \
X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12},
#include "Options.inc"
#undef OPTION
};
......
......@@ -132,17 +132,17 @@ void ObjectFile::initializeChunks() {
if (!Config->Debug && Name.startswith(".debug"))
continue;
// CodeView sections are stored to a different vector because they are
// not linked in the regular manner.
if (Name == ".debug" || Name.startswith(".debug$")) {
DebugChunks.push_back(make<SectionChunk>(this, Sec));
continue;
}
if (Sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
continue;
auto *C = make<SectionChunk>(this, Sec);
Chunks.push_back(C);
// CodeView sections are stored to a different vector because they are not
// linked in the regular manner.
if (C->isCodeView())
DebugChunks.push_back(C);
else
Chunks.push_back(C);
SparseChunks[I] = C;
}
}
......@@ -249,8 +249,12 @@ SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
auto *Aux = reinterpret_cast<const coff_aux_section_definition *>(AuxP);
if (Aux->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
if (auto *ParentSC = cast_or_null<SectionChunk>(
SparseChunks[Aux->getNumber(Sym.isBigObj())]))
SparseChunks[Aux->getNumber(Sym.isBigObj())])) {
ParentSC->addAssociative(SC);
// If we already discarded the parent, discard the child.
if (ParentSC->isDiscarded())
SC->markDiscarded();
}
SC->Checksum = Aux->CheckSum;
}
......
......@@ -102,7 +102,6 @@ def nosymtab : F<"nosymtab">;
def msvclto : F<"msvclto">;
// Flags for debugging
def dumppdb : Joined<["/", "-"], "dumppdb">;
def lldmap : F<"lldmap">;
def lldmap_file : Joined<["/", "-"], "lldmap:">;
......
......@@ -15,10 +15,11 @@
#include "Symbols.h"
#include "llvm/DebugInfo/CodeView/CVDebugRecord.h"
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h"
#include "llvm/DebugInfo/CodeView/DebugSubsectionVisitor.h"
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
#include "llvm/DebugInfo/CodeView/SymbolDumper.h"
#include "llvm/DebugInfo/CodeView/TypeDatabase.h"
#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h"
#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"
#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h"
#include "llvm/DebugInfo/MSF/MSFBuilder.h"
......@@ -46,8 +47,6 @@ using namespace lld;
using namespace lld::coff;
using namespace llvm;
using namespace llvm::codeview;
using namespace llvm::support;
using namespace llvm::support::endian;
using llvm::object::coff_section;
......@@ -68,22 +67,24 @@ static SectionChunk *findByName(std::vector<SectionChunk *> &Sections,
return nullptr;
}
static ArrayRef<uint8_t> getDebugSection(ObjectFile *File, StringRef SecName) {
SectionChunk *Sec = findByName(File->getDebugChunks(), SecName);
if (!Sec)
return {};
static ArrayRef<uint8_t> consumeDebugMagic(ArrayRef<uint8_t> Data,
StringRef SecName) {
// First 4 bytes are section magic.
ArrayRef<uint8_t> Data = Sec->getContents();
if (Data.size() < 4)
fatal(SecName + " too short");
if (read32le(Data.data()) != COFF::DEBUG_SECTION_MAGIC)
if (support::endian::read32le(Data.data()) != COFF::DEBUG_SECTION_MAGIC)
fatal(SecName + " has an invalid magic");
return Data.slice(4);
}
static ArrayRef<uint8_t> getDebugSection(ObjectFile *File, StringRef SecName) {
if (SectionChunk *Sec = findByName(File->getDebugChunks(), SecName))
return consumeDebugMagic(Sec->getContents(), SecName);
return {};
}
static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder,
codeview::TypeTableBuilder &TypeTable) {
TypeTableBuilder &TypeTable) {
// Start the TPI or IPI stream header.
TpiBuilder.setVersionHeader(pdb::PdbTpiV80);
......@@ -94,17 +95,148 @@ static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder,
});
}
static void mergeDebugT(ObjectFile *File,
TypeTableBuilder &IDTable,
TypeTableBuilder &TypeTable,
SmallVectorImpl<TypeIndex> &TypeIndexMap,
pdb::PDBTypeServerHandler &Handler) {
ArrayRef<uint8_t> Data = getDebugSection(File, ".debug$T");
if (Data.empty())
return;
BinaryByteStream Stream(Data, support::little);
CVTypeArray Types;
BinaryStreamReader Reader(Stream);
Handler.addSearchPath(sys::path::parent_path(File->getName()));
if (auto EC = Reader.readArray(Types, Reader.getLength()))
fatal(EC, "Reader::readArray failed");
if (auto Err = mergeTypeAndIdRecords(IDTable, TypeTable,
TypeIndexMap, &Handler, Types))
fatal(Err, "codeview::mergeTypeStreams failed");
}
static bool remapTypeIndex(TypeIndex &TI, ArrayRef<TypeIndex> TypeIndexMap) {
if (TI.isSimple())
return true;
if (TI.toArrayIndex() >= TypeIndexMap.size())
return false;
TI = TypeIndexMap[TI.toArrayIndex()];
return true;
}
static bool remapTypesInSymbolRecord(ObjectFile *File,
MutableArrayRef<uint8_t> Contents,
ArrayRef<TypeIndex> TypeIndexMap,
ArrayRef<TiReference> TypeRefs) {
for (const TiReference &Ref : TypeRefs) {
unsigned ByteSize = Ref.Count * sizeof(TypeIndex);
if (Contents.size() < Ref.Offset + ByteSize) {
log("ignoring short symbol record");
return false;
}
MutableArrayRef<TypeIndex> TIs(
reinterpret_cast<TypeIndex *>(Contents.data() + Ref.Offset), Ref.Count);
for (TypeIndex &TI : TIs)
if (!remapTypeIndex(TI, TypeIndexMap)) {
log("ignoring symbol record in " + File->getName() +
" with bad type index 0x" + utohexstr(TI.getIndex()));
return false;
}
}
return true;
}
/// MSVC translates S_PROC_ID_END to S_END.
uint16_t canonicalizeSymbolKind(SymbolKind Kind) {
if (Kind == SymbolKind::S_PROC_ID_END)
return SymbolKind::S_END;
return Kind;
}
/// Copy the symbol record. In a PDB, symbol records must be 4 byte aligned.
/// The object file may not be aligned.
static MutableArrayRef<uint8_t> copySymbolForPdb(const CVSymbol &Sym,
BumpPtrAllocator &Alloc) {
size_t Size = alignTo(Sym.length(), alignOf(CodeViewContainer::Pdb));
assert(Size >= 4 && "record too short");
assert(Size <= MaxRecordLength && "record too long");
void *Mem = Alloc.Allocate(Size, 4);
// Copy the symbol record and zero out any padding bytes.
MutableArrayRef<uint8_t> NewData(reinterpret_cast<uint8_t *>(Mem), Size);
memcpy(NewData.data(), Sym.data().data(), Sym.length());
memset(NewData.data() + Sym.length(), 0, Size - Sym.length());
// Update the record prefix length. It should point to the beginning of the
// next record. MSVC does some canonicalization of the record kind, so we do
// that as well.
auto *Prefix = reinterpret_cast<RecordPrefix *>(Mem);
Prefix->RecordKind = canonicalizeSymbolKind(Sym.kind());
Prefix->RecordLen = Size - 2;
return NewData;
}